[cfe-dev] Security fail (memset being optimized away)

Steve O'Brien via cfe-dev cfe-dev at lists.llvm.org
Fri Jan 4 08:49:54 PST 2019


My usual go-to expression for "do not optimize this thing away" is
something like:

    asm volatile ("" : : "r"(thing));

which I've never used with memory, but "m" seems to work as well.

I'm not sure how portable this is.  `asm` usually means it's not
portable, but notice that the assembly "code" is just a blank string,
with simply a mention of the thing you want to keep / keep the results
of.

It does seem to work ok with:

* clang, GCC, old/new versions
* x86-64, arm (32bit), arm64
* C11 and C++17
* local buffers of known size and scope.  (References to "outside"
memory work with memset with or without this.)

Code:

    int test1() {
        char buf[128];
        memset(buf, 0, sizeof(buf));
        __asm volatile ("" : : "m"(buf));
        return 0;
    }

If this still doesn't cut it, try poking around in Folly's
implementation of "doNotOptimizeAway", which was created for just this
type of thing.  (It's buried in the benchmarking code, and not a
public library function, but you can at least see how it's done
there).

https://github.com/facebook/folly/blob/master/folly/Benchmark.h



On Fri, Jan 4, 2019 at 10:54 AM Jonny Grant via cfe-dev
<cfe-dev at lists.llvm.org> wrote:
>
>
>
> On 04/01/2019 15:41, myLC at gmx.de wrote:
> > On 1/4/19 3:47 PM, Jonny Grant wrote:
> >  > ...
> >> Maybe add an abort()  ?
> >>
> >> eg
> >>
> >> inline void check_memset(void *s, int c, size_t n);
> >> {
> >>      const char * buf = (char*)buf;
> >>      memset(2, 0, n);
> >>
> >>      if(0 != *buf)
> >>      {
> >>          abort();
> >>      }
> >> }
> >
> >
> > I'm afraid, that won't cut it either. On the "Compiler Explorer"
> > website ( https://godbolt.org/ ) you can see that many compilers
> > implement "their own" inlined version of memset - especially
> > when you turn on "max optimizations" (-O3). The compiler might
> > simply decide to only clear the first byte as the rest is not
> > being access anyhow...
>
> Did you verify that in practice with my code? (optimising out memset)
>
>
> I've not not time to run tests on godbolt.org
>
> >> Or use a for loop to verify all bytes are now 0.
> >
> > The compiler knows that the buffer has to be all zeros as it
> > knows, it just cleared it before. This is basically a more
> > complicated version of:
> > {
> >      int a = 0;
> >      if( a != 0 )
> >          abort();
> > }
> > This can never call abort and will therefore be removed
> > completely.
>
> Compilers are not static analysers, they don't know when ram addresses
> were touched as far as I am aware. Do you have a source for this
> information?
>
>
> If you don't want to use libc memset() if you feel it might be lost,
> write your own. Or put those functions in a library that is not
> optimised at all.
>
>
> Whatever you do. verify how it looks after compilation to be sure. Share
> your results, I'm interested to hear.
> Jonny
>
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


More information about the cfe-dev mailing list