C++ Logo


Advanced search

Re: Memory Safety and Page Protected Memory

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Fri, 1 Mar 2024 15:32:43 -0500
On Fri, Mar 1, 2024 at 3:10 PM Robin Rowe via SG14 <sg14_at_[hidden]>

> > As far as C++ standard is concerned, this proposal would only make
> sense if you mean protect access to memory assigned within the same
> application from another section that has gone rogue.
> Yes.
> [...]
> The page_protected_password is in its own bubble. A programmer can't
> land in the page_protected_password buffer by overrunning some nearby
> pointer like username. Nor can the programmer overrun the
> page_protected_password buffer without a segfault or signal triggered.
> You are right if thinking that get_pass(page_protected_password), or any
> function passed that pointer, may do whatever it likes with
> page_protected_password. Being memory safe gives protection from buffer
> overruns, doesn't mean error-proof.

Your idea, especially in the focus on "security," sounds an awful lot
like P1315
which was rejected by C++ but according to the GitHub tracker
<https://github.com/cplusplus/papers/issues/67> was actually adopted by
WG14 for C23.
The problem with "secure_clear" — which was supposed to behave like
memset(buf, '\0', sizeof(buf)) except "do what I mean" in the case that
`buf` was dead after the write — is that it doesn't actually help any
production use-case as far as we could tell.

Con: "If you're using this thing to zero out a password, or something,
you're definitely doing it wrong, because you failed to pin the buffer's
page in RAM. It will get swapped to disk, your magic memset won't zero out
the *swapfile*, and you're just as pwned as if you had used ordinary memset
in the first place. Or, your magic memset will zero out the L1 *cache* but
fail to zero out the L2/L3 cache or main memory. Or, your magic memset will
zero out the buffer you pointed it at, but it won't zero out the other
copies of that same data that the optimizer caused to be made (trivially,
thus undetectably) all over the stack, under the As-If Rule. Or, the
attacker will just read that memory *before* the memset, instead of after."

Pro: "Belt-and-suspenders! Swiss cheese security model! We certainly need
more work on things like an API for pinning memory pages, but secure_memset
can't possibly *hurt!*"

Con: "Look, there are only two cases to consider here. Case 1 is you're
writing an important program that *will be attacked* in these horrible
ways, such that you can't even leave secrets lying around in your own
process's address space, and then secure_memset won't help because it
doesn't stop all these other channels; your only sensible approach is to
work very carefully with extremely low-level code. Case 2 is you're *not*
writing that kind of program, and then secure_memset won't help because you
haven't got that problem to begin with. Either way, it doesn't help you, so
there's no point in standardizing it."

But I guess WG14 wasn't swayed by the Con arguments, because `memset_explicit`
is in C23 for real <https://en.cppreference.com/w/c/string/byte/memset>.
The entire formal wording is as follows:

The memset_explicit function copies the value of c (converted to an
> unsigned char) into each of the first n characters of the object pointed to
> by s. The purpose of this function is to make sensitive information stored
> in the object inaccessible. [Footnote: The intention is that the memory
> store is always performed (i.e. never elided), regardless of optimizations.
> This is in contrast to calls to the memset function. —end footnote]

Your proposed feature is shaped more like "protection from reads from
within the same process," which I don't even understand what that would
mean, really; but you should definitely at least read and
study-the-history-of the `secure_clear` proposal. The discussions
(including on the SG14 list) will hit a lot of the same points as they
would for your proposal.


Received on 2024-03-01 20:33:02