C++ Logo


Advanced search

Re: Memory Safety and Page Protected Memory

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Sun, 3 Mar 2024 22:04:02 -0500
On Fri, Mar 1, 2024 at 6:39 PM James Mitchell <james_at_[hidden]> wrote:

> I rarely jump into these discussions, but that list of cons for
> secure_memset seems like a bunch of extreme edge cases. [...]
> > It will get swapped to disk
> This seems like a strawman argument, it _could_ get swapped out to disk,
> but typically your getting the secret using it then clearing it, for it to
> be swapped out to disk the OS needs to have determined that between that
> period of time that it felt it was crucial to swap it to disk which is
> going to be rare, and only in a multi-threaded environment would something
> inside that process see it.
> > memset will zero out the L1 *cache* but fail to zero out the L2/L3
> cache or main memory
> This seems like another strawman argument, most read primitives are just
> that, a read primitive, I would love to see what technique you'll use for a
> common read primitive to gain access to that memory with a common read
> primitive? I assume this would be hoping to use different threads and race
> for it hoping that cache coherence doesn't get in the way. At the same time
> why don't we account for someone sniffing the network, reading from disk,
> etc.
> > 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
> This is the only semi-reasonable one in your list, however I'm not certain
> that it really stands up much either. [i.e. the optimizer probably doesn't
> make secret copies of strings, and most secrets are (non-SSO) strings...]
> The API you use might make copies (e.g. copy to a buffer to send over the
> network) but that's within the control of the person writing the code (and
> whatever libraries they use).
> This is where instead of guaranteeing that it will be removed which is
> impossible [...]

Well, I mean, I'm not really trying to die on the hill of "secure_clear was
a silly idea," *especially* as that hill has already been taken (WG14
adopted the feature). But all of your rebuttals could be viewed as pretty
weak too. Imagine going to any security firm with a list of potential
exploits, and imagine they responded with "Well, that _could_ happen, but
typically it would be rare," and "Well, sure that could happen, but at that
point why don't we account for someone sniffing the network too," and "I'm
not certain that the optimizer would do that [but I'm not certain it
wouldn't, either]," and "Yes, after we scrub the secrets we might call into
a third-party API that leaks them right away, but that's *their* coding
problem not ours," and so on. The challenge may have been weak, but
the rebuttal isn't terribly strong either!
As long as the entire exercise remains hypothetical, I don't think we can
say whether either side is "correct."

If we discover, 10 years from now, that some libc vendor is shipping a
`memset_explicit` implementation that is recognized as a dead write by GCC
and optimized away, then we'll *certainly know* that `memset_explicit` was
worse than useless (because it lulled people into a false sense of
security). But assuming that *never* happens, then everyone will just have
their respective vague feelings.

I don't think we should let perfect get in the way of implementing an
> obvious solution which has been done by industry for quite a while.

It's totally reasonable to say "industry does this, let's standardize it" [
*cough* trivial relocatability *cough*]. But the flip side is — as WG21
recently discussed in Kona re `benchmark::DoNotOptimize` — for some things
industry has long experience, and the great benefit of a single
implementation. Moving something like `benchmark::DoNotOptimize` or
`SecureZeroMemory` from Google Benchmark resp. Win32 into the STL, means
replacing one old battle-scarred implementation with three brand-new
implementations. It's not clear that that's better.
(But, on the pro side, here the library vendor is not so much *replacing*
SecureZeroMemory, explicit_bzero, etc., as providing *a thin façade over*
one of them. And on the con side: the vendor *can't* actually do that,
because all the prior art is shaped like `bzero` whereas the thing WG14
standardized is shaped like `memset`! I actually don't know what vendors
are going to do about that. Maybe do a secure clear, optionally followed by
an "insecure memset" in the case that the `c` argument can't be statically
proven to be '\0'? I think that should work fine.)

> Do you have any CVEs or realistic PoCs to show that cases where things
> like secure_memset have been used have still led to secrets leaking? Or is
> it just all hypothetical?

As far as I know, all of the arguments pro and con have been hypothetical.

This entire subthread is basically recapped at
so maybe I should have just pointed there in the first place. ;D But I
think my original point was that any proposal like Robin's of the form "We
should standardize a library function that makes things secure by magic" is
likely to encounter a lot of pushback (citing various physical reasons that
feel more or less plausible to different people) and he should study the
archives re: P1315 for a sense of how it might go.


Received on 2024-03-04 03:04:16