From: Bastien Roucariès <ro...@debian.org> Add some resistance against compiler optimization by doing pointer arithmetic with parameter of the function, therefore avoid dead branch optimization
Signed-off-by: Bastien Roucariès <ro...@debian.org> --- lib/explicit_bzero.c | 56 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/lib/explicit_bzero.c b/lib/explicit_bzero.c index bb52d11de..055559907 100644 --- a/lib/explicit_bzero.c +++ b/lib/explicit_bzero.c @@ -43,6 +43,12 @@ # undef explicit_bzero #endif +#if !defined _WIN32 && !HAVE_EXPLICIT_MEMSET && !HAVE_MEMSET_S && !__GNUC__ +static void * (* const volatile volatile_memset1)(void *, int, size_t) = memset; +static void * (* const volatile volatile_memset2)(void *, int, size_t) = memset; +static int volatile cmp_memset; +#endif + /* Set LEN bytes of S to 0. The compiler will not delete a call to this function, even if S is dead after the call. */ void @@ -54,9 +60,8 @@ explicit_bzero (void *s, size_t len) explicit_memset (s, 0, len); #elif HAVE_MEMSET_S (void) memset_s (s, len, '\0', len); -#else +#elif defined __GNUC__ memset (s, '\0', len); -#if defined __GNUC__ # if !defined __clang__ /* Compiler barrier. */ asm volatile ("" ::: "memory"); @@ -64,5 +69,52 @@ explicit_bzero (void *s, size_t len) /* https://bugs.llvm.org/show_bug.cgi?id=15495#c11 */ __asm__ volatile("" : : "g"(s) : "memory"); # endif +#else + /* Here better by safe than sorry + + volatile means that the compiler must read + its value from memory each time its used because + the value may have changed. + + According to C standard: + An object that has volatile-qualified type may be modified + in ways unknown to the implementationor have other unknown + side effects. Therefore any expression referring to such + an object shall be evaluated strictly according to the + rules of the abstract machine, as described in 5.1.2.3. + Furthermore, at every sequence point the value last stored + in the object shall agree with that prescribed by the + abstract machine, except as modified by the unknown factors + mentioned previously. What constitutes an access to an object + that has volatile-qualified type is implementation-defined. + + However while the standard requires the compiler to read the + value of volatile pointer from memory, it does not require it to call memset + if it can compute the same result by other means. + Therefore, a compiler would be in compliance if it inlined each call to: + + static void * (* const volatile tmp_fptr)(void *, int, size_t) = memset; + if (tmp_fptr == &memset) + memset(ptr, 0, len); + else + tmp_fptr(ptr, 0, len); + + If the memory pointed to by ptr is not read again, then the direct + call to memset, the semantics of which are known, could be eliminated. + + See detail here: https://klevchen.ece.illinois.edu/pubs/yjoll-usesec17.pdf + + Therefore challenge the compiler by comparing two volatile with len address and s + */ + cmp_memset = ((volatile char *) volatile_memset1 + len != (volatile char*) volatile_memset2 + && ( volatile char *) volatile_memset1 - len != (volatile char *)s); + if (cmp_memset) + { + (void) volatile_memset1(s, '\0', len); + } + else + { + (void) volatile_memset2(s, '\0', len); + } #endif } -- 2.25.1