|
|
Subscribe / Log in / New account

Unsecure optimization?

Unsecure optimization?

Posted Jan 22, 2016 14:51 UTC (Fri) by nybble41 (subscriber, #55106)
In reply to: Unsecure optimization? by madscientist
Parent article: OpenSSH and the dangers of unused code

Except that if you do that, you just get a warning ("passing argument 1 of ‘memset’ discards ‘volatile’ qualifier from pointer target type") and the argument is still treated as non-volatile, because memset() takes a "void*" argument rather than "volatile void*". (I just tried it; the memset() call is still optimized away.)

What you actually have to do is implement your own volatile_memset() function which takes a "volatile void*" and is guaranteed to overwrite the target memory area. Or (somewhat less portably) you can trick the compiler into thinking that the memory is actually used:

memset(&x, 0, sizeof x);
__asm__ ("" :: "m" (x));

No code is generated for the empty __asm__ block, but the compiler must make sure the data is available at that point just the same. The nice thing about implementing it this way is that the compiler can still optimize the memset() call, for example by replacing it with inline store instructions, but the effect will be preserved. Of course, the __asm__ block is not standard C and your mileage with other compilers will vary.


to post comments

Unsecure optimization?

Posted Jan 22, 2016 15:25 UTC (Fri) by PaXTeam (guest, #24616) [Link] (5 responses)

be careful with such tricks, not all compilers behave the same way, not even different versions of gcc itself. for some more discussion see https://llvm.org/bugs/show_bug.cgi?id=15495#c5 and later comments. in short, you want both an input constraint and a memory barrier to get consistent behaviour across all compilers (as far as we could tell, that is ;).

Unsecure optimization?

Posted Jan 22, 2016 15:54 UTC (Fri) by nybble41 (subscriber, #55106) [Link] (4 responses)

I agree that caution is needed with __asm__ constraints, but I think the problem in the bug report you referenced was that the `"r" (&x)` constraint only specified that the __asm__ block needed the address of the variable, not the contents. The "memory" constraint by itself, on the other hand, indicates that arbitrary memory is accessed, but if the compiler doesn't place the variable in memory then it isn't affected. Combining the two has the intended effect, but also forces any other data stored in memory to be reloaded.

The constraint I used, `"m" (x)`, specifies that the content of `x` is read, not just the address, so no `"memory"` clause is necessary.The GCC documentation actually suggests the "m" constraint as an alternative to "memory" when you need to access memory but wish to avoid the overhead of a full memory barrier (See https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Clob..., just before section 6.44.3.3.)

Unsecure optimization?

Posted Jan 22, 2016 16:24 UTC (Fri) by PaXTeam (guest, #24616) [Link] (3 responses)

if i'm not mistaken, passing the address of an object to a statement requires that all sideeffects of previous statements on the object be visible by that time so the two approaches are equivalent as far as semantics goes however actual compilers had inconsistent behaviour here. e.g, gcc 4.5 does not even accept your suggested constraint:

error: memory input 0 is not directly addressable

Unsecure optimization?

Posted Jan 22, 2016 17:45 UTC (Fri) by nybble41 (subscriber, #55106) [Link] (2 responses)

> e.g, gcc 4.5 does not even accept your suggested constraint

What kind of expression were you trying to use? I don't have a copy of GCC 4.5 handy (it is eight years old, after all), but that form is documented in the manual for GCC 4.5.4 (https://gcc.gnu.org/onlinedocs/gcc-4.5.4/gcc/Extended-Asm...), with the same notes about being an alternative to the "memory" constraint. GCC 4.9 accepts it at least for variables of integer, array, struct, and union types. The expression does need to be an lvalue, of course.

Unsecure optimization?

Posted Jan 22, 2016 19:44 UTC (Fri) by PaXTeam (guest, #24616) [Link] (1 responses)

it was the example from that llvm bugreport. you can play with it too, the behaviour is the same across 4.5-4.7, only 4.8+ accepts it:

$ gcc-4.5.4 -O2 -x c -c - -o /dev/null <<EOF
#include <string.h>

void foo(int x) {
char buf[10];
int i;
for (i=0; i<sizeof(buf); ++i)
buf[i]=x++;
memset(buf,0,sizeof(buf));
// asm("" : : "r"(buf));
asm("" : : "m"(buf));
// asm("" : : : "memory");
}
EOF

<stdin>: In function 'foo':
<stdin>:10:18: error: memory input 0 is not directly addressable

PS: gcc 4.5 is from 2010.

Unsecure optimization?

Posted Jan 22, 2016 20:57 UTC (Fri) by nybble41 (subscriber, #55106) [Link]

> the behaviour is the same across 4.5-4.7, only 4.8+ accepts it

It looks like those versions have a bug in the implementation of the "m" constraint for array types. It works if you wrap the array in a struct:

__asm__ ("" :: "m" (*(struct { __typeof(buf) x; } *)&buf));

A bit awkward, but it does work in at least GCC 4.4, 4.9, and 5.3 as well as Clang 3.6, and the extra syntax could be hidden with a macro.

> PS: gcc 4.5 is from 2010.

So it is. And 4.5.4 is from 2012. So why does the copyright statement in the documentation for 4.5.4 (https://gcc.gnu.org/onlinedocs/gcc-4.5.4/gcc/) indicate that it was last updated in 2008?


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds