At LZ our development and nightly builds are done with clang, so it is of interest to check out what stack protection checking compiler options are available.  DB2 LUW used the intel compiler, which had very nice stack clobbering code.  How does clang’s fair against the intel compiler in this respect?

Clang does support a safe-stack option.  Here’s an example of some stack corrupting code:

#include <string.h>

void corrupt( char * b ) ;

int main()
{
   char b[12] ;

   corrupt( b ) ;

   return 0 ;
}

void corrupt( char * b )
{
   memset( b - 4, 'a', 20 ) ;
}

Running this without safe stack results in a SEGV on return from from corrupt():

Screen Shot 2016-06-10 at 11.08.54 AM

with safe-stack we have a “nicer” trap:

(gdb) run
Starting program: /home/pjoot/lznotes/proto/stackcorrupt2
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
__memset_sse2 () at ../sysdeps/x86_64/memset.S:415
415     L(P4Q0): mov    %edx,-0x4(%rdi)
Missing separate debuginfos, use: debuginfo-install libgcc-4.8.3-9.el7.x86_64 libstdc++-4.8.3-9.el7.x86_64
(gdb) where
#0  __memset_sse2 () at ../sysdeps/x86_64/memset.S:415
#1  0x0000000000411835 in corrupt (
    b=0x7ffff6bd2ff4 'a' <repeats 12 times>, "\177ELF\002\001\001\003")
    at stackcorrupt2.cc:16
#2  0x00000000004117f1 in main () at stackcorrupt2.cc:9

The compiler is able to catch the corruption in the act, right in the offending memset.

Limitations

What I do notice with this compiler option, is that the implementation has opted not to catch corruptions within the valid stack frame, nor is there any attempt to catch a corruption that does not walk over the return pointer. Here’s an example:

#include <string.h>

void corrupt( char * b ) ;

#define SZ 12
#if 0
   // safe-stack catches this:
   #define PRESZ 0
   #define POSTSZ 4
#else
   // safe-stack catches this buffer overwrite
   #define PRESZ 4
   #define POSTSZ 0
#endif
int main()
{
   char b[SZ] ;

   corrupt( b ) ;

   return 0 ;
}

void corrupt( char * b )
{
   memset( b - PRESZ, 'a', SZ + PRESZ + POSTSZ ) ;
}

and another non-trapping stack corruption:

#include <stdio.h>
#include <string.h>

void corrupt2( int & a, char * b, int & c ) ;

int main()
{
   int a = 0 ;
   char b[12] ;
   int c = 0 ;

   corrupt2( a, b, c ) ;

   printf( "0x%08X 0x%08X\n", a, c ) ;

   return 0 ;
}

void corrupt2( int & a, char * b, int & c )
{
   memset( b - 4, 'a', 20 ) ;
}

The intel compiler appeared to use guard bytes between stack variables, and was able to tell you exactly which stack variable was overwritten. Clang appears to be opting for a write-forbidden guard region on the stack frame, so it able to catch the corruption in the act, but only if the corruption is “big enough”. There are benefits of both approaches. Unfortunately, there are a number of restrictions in the safe-stack documentation. I’m not sure that I’ll be able to use this at all in LZ code.