Re: Compaq Alpha Bounds Checking
In this post below to the Linux security-audit mailing list, Solar was kind enough to fulfill my request for performance data on the Compaq ccc compiler for Linux/Alpha using bounds checking. Astonishingly, Solar's tests showed virtually no performance overhead for bounds checking. I found this to be both amazing and depressing for StackGuard, and went away to sulk :-) Sorry for not answering your last post for that long -- it's still in my mailbox, and was going to be answered once I have the time. Today, I got my own access to an Linux/Alpha box with ccc, and to a Tru64 box. Both support the "-check_bounds" switch. I did my own testing, and discovered that as far as I can tell, "-check_bounds" does NOTHING AT ALL. Am I missing something? Yes, I guess so -- see below. foo() { char x[50]; gets(x); } I would _not_ expect this case to be covered by the compiler's bounds checking. This is in fact the reason I didn't use a strcpy() when demonstrating the bounds checking to you in my first post about ccc. Their bounds checking only applies to explicit array subscripts: -[no]check_bounds Generates runtime code to check the values of array subscripts (and equivalent pointer arithmetic involv- ing pointers produced by converting an array name to a pointer) to verify that the resulting address lies within the range for which the C standard requires well-defined behavior. This was so obvious for me that I forgot to mention this on the list, sorry. Now I realize that when saying "bounds checking" people often mean "complete protection", or close to that (with DoS in mind). Speaking of the usage of gets() and such, even if the compiler was able to pass bounds checking information down to functions (which ccc doesn't do), it would at least require that you also recompile those functions themselves. Thus I conclude that Solar's amazing performance results that show no overhead are because the compiler is lying about implementing bounds checking. There is no overhead because there is no protection. Well, they could be more verbose in their description, yes. As for the "no protection" -- this wasn't meant as a security feature, but there's _some_ protection, it's just far from being complete. Finally, as this also goes to BugTraq this time, here's a piece of my first post on the subject that shows a case where bounds checking can work (and does indeed work) -- [ghost@alice tests]$ cat bounds.c #include stdlib.h int f(int n, int m) { char buffer[100]; int i; for (i = 0; i n; i++) buffer[i] = 'x'; return buffer[m]; } int main(int argc, char **argv) { return f(atoi(argv[1]), atoi(argv[2])); } [ghost@alice tests]$ gcc bounds.c -o bounds -O -s [ghost@alice tests]$ ./bounds 33 Segmentation fault [ghost@alice tests]$ ./bounds 99 33 [ghost@alice tests]$ ccc bounds.c -o bounds -O [ghost@alice tests]$ ./bounds 33 Segmentation fault [ghost@alice tests]$ ccc bounds.c -o bounds -O -check_bounds [ghost@alice tests]$ ./bounds 33 Trace/breakpoint trap [ghost@alice tests]$ ./bounds 99 Trace/breakpoint trap [ghost@alice tests]$ ./bounds Segmentation fault [ghost@alice tests]$ ./bounds 99 33 [ghost@alice tests]$ The first two compiles are with gcc and ccc w/o bounds checking. We get segfaults. Then the program is recompiled with bounds checking, and we're now getting those traps (just like the man page says). The last two tests are to show that the traps are only generated from bounds checking and not other errors, and that the program is still working. BTW, here's what the checks look like: mov $3, $5 cmpule $3, 99, $16 bne $16, L$10 mov -18, $16 call_pal 0xAA # gentrap L$10: [ ... some code skipped: the loop got unrolled and large ... ] addq$sp, $5, $8 ldq_u $16, ($8) I wouldn't say that the option did "nothing at all" to SSH -- it must have added quite a few checks, which made the binary 5 KB larger. Signed, Solar Designer
Compaq Alpha Bounds Checking
In this post below to the Linux security-audit mailing list, Solar was kind enough to fulfill my request for performance data on the Compaq ccc compiler for Linux/Alpha using bounds checking. Astonishingly, Solar's tests showed virtually no performance overhead for bounds checking. I found this to be both amazing and depressing for StackGuard, and went away to sulk :-) Today, I got my own access to an Linux/Alpha box with ccc, and to a Tru64 box. Both support the "-check_bounds" switch. I did my own testing, and discovered that as far as I can tell, "-check_bounds" does NOTHING AT ALL. Am I missing something? Consider this program, which I use for trivial StackGuard testing: foo() { char x[50]; gets(x); } main() { foo(); printf("Hey, kenny lives!\n"); } Compiled thusly: cc -check_bounds test.c -o test Now what are the results of testing this program? * When compiled with ccc -check_bounds on Linux/Alpha and given a large input, this program prints out "Hey ..." and then seg faults. * When compiled with cc -check_bounds on Tru64/Alpha and given a large input, the program also prints out "Hey ..." and seg faults. * When compiled with StackGuard on Linux/x86 and given a large input, the program prints a StackGuard intruder alert and exits. Ok, so maybe -check_bounds doesn't work for autos, and only works for static arrays (admittedly, StackGuard has no impact on static array overflows either). Consider this program. #include stdio.h char a[25]; char b[12]; main() { gets(a); strcpy(b,a); printf("a=%s, b=%s\n", a, b); } This time, we get: * Linux/Alpha: prints both strings correctly and exits normally. It should not have. * Tru64: prints nothing, just seg fault. Expected behavior for a non-bounds checking compiler. So, am I missing something here? At no time could I get either of these programs to die on a SIGTRAP, which is what the Compaq man pages say should happen when you over flow an array bounds with -check_bounds turned on. Thus I conclude that Solar's amazing performance results that show no overhead are because the compiler is lying about implementing bounds checking. There is no overhead because there is no protection. Caveat: this is from 1 hour of playing on the Alphas. If someone knows more about these machines and can tell me what I did wrong, feel free to speak up. Crispin - Crispin Cowan, CTO, WireX Communications, Inc.http://wirex.com Free Hardened Linux Distribution: http://immunix.org Solar Designer wrote: Sorry for not answering the questions, but I thought I'd let people know about Compaq C for Linux/Alpha having a bounds checking feature, which some of us might find useful -- Excellent! I've been wanting to compare StackGuard against a bounds checking compiler for years. Well, they are quite different: 1. Some existing programs might break when compiled with bounds checking. In particular, the following code will likely result in a trap with ccc: int x[10]; int *p; p = x - 5; // trap here while (...) { p += 5; [...] } 2. Bounds checking as implemented in ccc is somewhat limited; I got a few warnings (not many) when compiling ssh: cc: Warning: readpass.c, line 299: In this statement, pointer arithmetic was performed more than once in computing an array element. The bounds checking code output by the compiler will only verify the "buf+strlen(...)" expression. (badboundchk) p = buf + strlen(buf) - 1; --^ 3. Bounds checking, unlike StackGuard, can be useful when auditing. If someone has access to an Alpha and the Compaq Linux/Alpha compiler, could you do before/after testing on the bounds checking feature and try to determine the %overhead? The StackGuard overhead data is here: http://immunix.org/StackGuard/performance.html OK, I did some testing with SSH, replacing sshd only, not the client, as there's some problem in ssh client when compiled with ccc that I didn't bother to investigate (it's not related to the bounds checking). Here're the performance numbers (localhost to localhost scp transfer, IDEA encryption, no compression): RH 5.2, default ssh compile 1300 KB/s egcs 1.1.2, -O2, EV56 1450 KB/s ccc, -O4 -arch host 1680 KB/s (6.35 ... 6.38 secs) ccc, -O4 -arch host -check_bounds 1680 KB/s (6.35 ... 6.38 secs) The results with Blowfish are very similar (just about 30% faster in all tests). So, no noticable performance impact. The binary got about 5 KB larger (both were stripped), when compiled with bounds checking. My guess is that ccc does most of its checking at compile time. In all these tests, the ssh client remained the same (compiled right after installing this RH,
Re: Compaq Alpha Bounds Checking
Solar Designer wrote: foo() { char x[50]; gets(x); } I would _not_ expect this case to be covered by the compiler's bounds checking. This is in fact the reason I didn't use a strcpy() when demonstrating the bounds checking to you in my first post about ccc. Understood. Unfortunately, this had the effect of hiding the things the compiler doesn't do. I understand how the compiler would be unable to affect a pre-compiled library, but I assumed that they would provide standard libraries that had been compiled with bounds checking, and supply that version of the library when you use the -check_bounds option. Since a very large proportion of "array bounds" problems have to do with improper use of library functions, this is a critical issue. This was so obvious for me that I forgot to mention this on the list, sorry. Now I realize that when saying "bounds checking" people often mean "complete protection", or close to that (with DoS in mind). That's what I mean. That appears to be what Jones Kelly meant: http://www-ala.doc.ic.ac.uk/~phjk/BoundsChecking.html If ccc is used to compile all library functions, then I would (reasonably?) expect complete protection. StackGuard has a similar issue: if you link to libraries that are not StackGuard-compiled, then vulnerabilities within the library can be exploited. This is why we ship StackGuarded libraries from http://immunix.org Speaking of the usage of gets() and such, even if the compiler was able to pass bounds checking information down to functions (which ccc doesn't do), it would at least require that you also recompile those functions themselves. Ow! Bounds checking info doesn't get passed to functions? That DEFINITELY limits the security effectiveness of this form of bounds checking. It considerably limits the debugging effectiveness. Well, they could be more verbose in their description, yes. As for the "no protection" -- this wasn't meant as a security feature, but there's _some_ protection, it's just far from being complete. Agreed :-) Finally, as this also goes to BugTraq this time, here's a piece of my first post on the subject that shows a case where bounds checking can work (and does indeed work) -- I tried Solar's code, and it does indeed "work", where "work" is defined as "bounds checking on explicit array references that are local to a function." Assorted other things produced all kinds of interesting side effects with -check_bounds turned on :-) For example, this program: [pbakke@spe85 ~]$ cat test3.c foocpy(char * to, char * from, int count) { for (; count-- *from; *to++ = *from++) {} } main() { char a[25]; char x[100]; char b[12]; char y[100]; gets(a); printf("a=%s, b=%s\n", a, b); foocpy(b, a, 25); /* strcpy(b, a); */ printf("a=%s, b=%s\n", a, b); } [pbakke@spe85 ~]$ ccc -check_bounds test3.c -o test3 test3.o: In function `main': test3.o(.text+0x60): the `gets' function is dangerous and should not be used. [pbakke@spe85 ~]$ ./test3 jjj a=jjj, b= a=jjj, b=jjj [pbakke@spe85 ~]$ ./test3 a=, b= a=, b= I just massively overflowed the b[] array, entirely in my own code. ccc -check_bounds didn't see it, either because it didn't pass the bounds down to the function, or because it doesn't deal with pointer arithmetic. If this is the intended behavior, and I misunderstood what is meant by "check bounds", mea culpa :-) Crispin - Crispin Cowan, CTO, WireX Communications, Inc.http://wirex.com Free Hardened Linux Distribution: http://immunix.org