Skip to content

src: safely clear certain buffers - #21637

Closed
vszakats wants to merge 3 commits into
curl:masterfrom
vszakats:use-memzero
Closed

src: safely clear certain buffers#21637
vszakats wants to merge 3 commits into
curl:masterfrom
vszakats:use-memzero

Conversation

@vszakats

@vszakats vszakats commented May 15, 2026

Copy link
Copy Markdown
Member

That may hold credentials or other sensitive data, or where we want to
ensure the zeroing is not optimized out by the compiler.

Credits-to: Daniel Gustafsson
Ref: #13589 (original attempt)
Ref: #21588

Follow-up to #21645
Follow-up to 066478f #21598


Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reduces residual sensitive data in libcurl by using secure zero-before-free helpers for selected password and credential buffers.

Changes:

  • Replaces plain frees with zeroing frees for URL, setopt, IMAP, MQTT, and SSPI password-related buffers.
  • Adds stored credential buffer sizing so refcounted credentials can be wiped before final free.
  • Tracks SSPI password length for wiping converted and identity password buffers.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
lib/urlapi.c Zeroes URL password storage when clearing or replacing URL parts.
lib/setopt.c Zeroes previous parsed password storage in the userpwd helper.
lib/mqtt.c Zeroes the temporary MQTT CONNECT packet before freeing.
lib/imap.c Zeroes the atomized IMAP LOGIN password after sending.
lib/curl_sspi.c Zeroes temporary and identity SSPI password buffers.
lib/creds.h Adds credential buffer size metadata.
lib/creds.c Stores credential buffer size and zeroes credentials on final unlink.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/mqtt.c Outdated
end:
if(packet)
curlx_free(packet);
curlx_freezero(packet, packetlen);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine for now.

Comment thread lib/imap.c Outdated

curlx_free(user);
curlx_free(passwd);
curlx_freezeroz(passwd);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's also fine for now.

Comment thread lib/setopt.c Outdated
Comment thread lib/creds.h Outdated

@dfandrich dfandrich left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like how curlx_freezero() requires tracking the allocated buffer size and keeping it around. It's too easy for this to become wrong, both when it's written but especially over time. And when it's wrong, the results can be worse than not having this feature at all. An alternative is to use malloc_usable_size() or _msize() depending on the platform to obtain it when it's needed. Since this is "just" a security-in-depth feature, platforms without this feature will still work, just without these buffers getting cleared.

@vszakats

Copy link
Copy Markdown
Member Author

I don't like how curlx_freezero() requires tracking the allocated buffer size and keeping it around. It's too easy for this to become wrong, both when it's written but especially over time. And when it's wrong, the results can be worse than not having this feature at all. An alternative is to use malloc_usable_size() or _msize() depending on the platform to obtain it when it's needed. Since this is "just" a security-in-depth feature, platforms without this feature will still work, just without these buffers getting cleared.

I wonder if it would be worth the complexity? It's another two
non-portable functions to detect and roll, and deal with them
missing (AFAICS e.g. on Windows).

This to automate the 3-4 cases in this patch, vs. the 15 that
is nul-terminated. Figuring out the former is a relatively light
effort IMO. The size is available in most cases for binary buffers,
and not a bad thing to add where it happens to be missing.

@vszakats
vszakats force-pushed the use-memzero branch 2 times, most recently from 1049c76 to b48a1d7 Compare May 16, 2026 00:24
@dfandrich

dfandrich commented May 16, 2026 via email

Copy link
Copy Markdown
Contributor

@vszakats

Copy link
Copy Markdown
Member Author

deal with them missing (AFAICS e.g. on Windows).
_msize() should be available on Windows.

I still sense a deep rabbit hole here. How do we know
the memory was allocated by the allocators _msize()
expects?

@dfandrich

dfandrich commented May 16, 2026 via email

Copy link
Copy Markdown
Contributor

@jay

jay commented May 16, 2026

Copy link
Copy Markdown
Member

I do a double take when I see the names like curlx_freezero, curlx_freezeroz. rather than combine the zeroing I think it would be easier to read like
curlx_zero_string(password);
curlx_free(password)

curlx_zero_mem(whatever, len);
curlx_free(whatever);

@bagder

bagder commented May 16, 2026

Copy link
Copy Markdown
Member

I'm with @jay: I prefer to have them as two separate calls - first clear, then free. I think that will also help us from overusing these calls.

@vszakats

vszakats commented May 16, 2026

Copy link
Copy Markdown
Member Author

Main reason for adding the wrappers was to avoid the extra NULL check everywhere.
Surely doable everywhere, but it was adding noise I did not like.

edit: Though also pbly solvable by making curlx_memzero() do the check automatically.
edit2: That's not really good, because it makes an inline zeroing a call. Unless done as a macro.. I'll play with this a little.

@vszakats
vszakats force-pushed the use-memzero branch 3 times, most recently from 82637b2 to 6e13625 Compare May 16, 2026 10:32
@vszakats

Copy link
Copy Markdown
Member Author

Reshuffled to drop the wrappers and offer curlx_memzero() (for raw buffers)
and curlx_strzero() (for null-terminated strings), both with built-in NULL-checks.

Comment thread lib/curl_setup.h Outdated
@vszakats
vszakats marked this pull request as draft May 16, 2026 22:29
@vszakats
vszakats force-pushed the use-memzero branch 2 times, most recently from 0f48882 to 5ba93cf Compare June 12, 2026 23:50
@bagder

bagder commented Jul 27, 2026

Copy link
Copy Markdown
Member

Questionable use with added complexity. Seem to not move forward. Close?

vszakats added a commit that referenced this pull request Jul 29, 2026
- delete zero-and-free wrapper macros. (not yet used)
  To keep it simple.
- do NULL-check in `curlx_memzero()`.
  To avoid noise at call sites.
- add `curlx_strzero()` for null-terminated strings, also with
  NULL-check.

Ref: #21637
Follow-upt o 066478f #21598

Closes #21645
vszakats added 2 commits July 29, 2026 13:55
Credits-to: Daniel Gustafsson
Ref: curl#13589 (original attempt)

Follow-up to 066478f curl#21598

safe clear more password buffers

tool_cfgable.c more

drop using dropped wrappers

creds.h copy comment suggested by LLM
```
/home/runner/work/curl/curl/lib/curl_setup.h:1650:7: error: check of ‘share’ for NULL after already dereferencing it [-Werror=analyzer-deref-before-check]
 1650 |     if(ptr)                         \
      |       ^
/home/runner/work/curl/curl/lib/curl_share.c:64:3: note: in expansion of macro ‘curlx_memzero’
   64 |   curlx_memzero(share, sizeof(*share));
      |   ^~~~~~~~~~~~~
  ‘share_destroy’: events 1-3
    |
    |   37 |   if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) {
    |      |      ~~~~~^~~~~~~~~~~
    |      |           |
    |      |           (1) pointer ‘share’ is dereferenced here
    |......
    |   52 |   if(share->ssl_scache) {
    |      |     ~
    |      |     |
    |      |     (2) following ‘false’ branch...
    |......
    |   58 |   Curl_psl_destroy(&share->psl);
    |      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |      |   |
    |      |   (3) ...to here
    |
  ‘share_destroy’: event 4
    |
    |/home/runner/work/curl/curl/lib/curl_setup.h:1650:7:
    | 1650 |     if(ptr)                         \
    |      |       ^
    |      |       |
    |      |       (4) pointer ‘share’ is checked for NULL here but it was already dereferenced at (1)
/home/runner/work/curl/curl/lib/curl_share.c:64:3: note: in expansion of macro ‘curlx_memzero’
    |   64 |   curlx_memzero(share, sizeof(*share));
    |      |   ^~~~~~~~~~~~~
    |
```
https://github.com/curl/curl/actions/runs/25960248795/job/76314218955?pr=21637
@vszakats
vszakats marked this pull request as ready for review July 29, 2026 12:27
@vszakats
vszakats requested a review from Copilot July 29, 2026 12:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Ready to approve

The changes are localized to cleanup/error paths, are NULL-safe via curlx_memzero/strzero, and the updated allocation-size tracking ensures full credential buffers are wiped without altering functional control flow.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details
  • Files reviewed: 11/11 changed files
  • Comments generated: 0 new
  • Review effort level: Low

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@vszakats vszakats closed this in 112a8b5 Jul 29, 2026
@vszakats
vszakats deleted the use-memzero branch July 29, 2026 12:39
@vszakats
vszakats requested a review from Copilot July 29, 2026 14:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

There is at least one error-cleanup path that still frees an existing stored password without securely clearing it first (lib/urlapi.c), which conflicts with the PR’s hardening goal.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Comments suppressed due to low confidence (1)

lib/urlapi.c:350

  • In parse_hostname_login()'s error cleanup path, u->password is freed via curlx_safefree() without being securely cleared first. If u->password already contained a password from a previous successful parse, this failure path will free it without zeroing, leaving the old credential in heap memory contrary to the goal of this PR.
  curlx_strzero(passwdp);
  curlx_free(passwdp);
  curlx_free(optionsp);
  curlx_safefree(u->user);
  curlx_safefree(u->password);
  • Files reviewed: 11/11 changed files
  • Comments generated: 0 new
  • Review effort level: Low

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

vszakats added a commit that referenced this pull request Jul 29, 2026
Reported by Copilot
Bug: #21637 (review)
Follow-up to 112a8b5 #21637
Follow-up to 7c34365 #21879

Closes #22432
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

5 participants