Integer safety crash course
Read this chapter, it's from a great book you should buy: http://taossa.com/index.php/the-vault/chapter-6-c-language-issues/ Signedness
● 1111111111111111111 ● int a = -1; 1111111111111 ● Most significant bit ● Least significant bit
Two's Complement
● 1111111111111111111111111 ● int a = -1, b; 1111111 ● ● Most significant bit b = -a; printf(“%d\n”,a); ● Least significant bit ● ● ~negate → b = ~a + 1; 00000000000000000000000 printf(“%d\n”,b); 000000000
● add 1 → 00000000000000000000000 000000001
Declaring variables
● int c; ● Signed or unsigned? ● char c; ● Signed or unsigned? ● long c; ● Signed or unsigned? ● short c; ● Signed or unsigned? ● #define X 4 ● Signed or unsigned?
http://www.cdecl.org/
Declaring variables
I. int c; I. Signed or unsigned? II.char c; II.Often signed- III.long c; depends IV.short c; III. Signed V. #define X 4 IV. Signed V. Signed
http://www.cdecl.org/
Signedness in a security context
● Bounds checking ● void dumpcash(unsigned ● Bounds checking int amt) { … } ● Bounds checking if (amt < acct_bal) dumpcash(amt); ● #define SIZE 64 if (index < SIZE) buffer[index] = data;
Signedness Concept → implicit funk
● int a = ● If a = -1? read_int(sockfd); ● if (a < sizeof(buf) ) {
}
Signedness Concept 1 → implicit conversion (coercion)
● signed vs unsigned ● int a; ● Converted to ● a < sizeof(buf) unsigned arithmetic ● == false ● Unsigned takes ● 0xffffffff < sizeof(buf) precedence
Signedness Concept 2 → sign extension
● a = 10000000 ● char a = 0x80; ● b = ● unsigned int b = a; 1111111111111111111 ● printf(“%u\n”,b); 1111110000000
● This slide might remind you of a lab exercise advisory you might be stuck on
Signedness Concept 3 → integer promotion
● Well covered in ● unsigned short a, b; taossas ● a = ● Learned this from strtoul(argv[1],0,0); fellow RPI student ● b = Roy Wellington in strtoul(argv[2],0,0); 2008; Roy read the C spec ● if(a*b < 0){ printf(“Roy is the ● Default is int man\n”); conversion }
Considerations for the future
● Signedness bugs are here to stay ● 64-bit won't make them go away
Esoteric signedness fun
● ● 10000000000000000 int a = -2147483648; 000000000000000 printf(“%d %d\n”, -a, ● Most significant bit ~a+1); ● Least significant bit
Guess the next topic
● What are the ● weirdcopy(char *buf, boundary conditions char *data, unsigned for len? int len){ do { buf[len] = *data++; } while(--len); }
Thanks to A^2 for pointing out an error
Underflow
● Aside: everything but 0 is ● while(--len){ true in C do_stuff ● } ● len = 0;
● len → len = -1
● len = -2
● …
● len = 1
● len = 0
A common pattern for underflows
● This bad code is ● strlen(buf) can be 0 everywhere
● malloc 0 still allocates ● buf[strlen(buf)-1] = 0; memory ● These are ● sz = 0 exploitable buf = malloc(sz); for(i = 0; i < sz-1; i++) buf[i] = read_char(fd);
Overflow on addition
● int x = 0xdeadbeef; ● x + 0xbadc0ded = ?
http://www.youtube.com/watch?v=y4GDcvweo14
Addition under the covers
● int x = 0xdeadbeef; ● x + 0xbadc0ded = ? ● 11011110101011011011111011101111 10111010110111000000110111101101 ● 110011001100010011100110011011100
Multiplication too!
● uint sz = read_int(); ● Numeric overflow by multiplication, ● Buf = malloc(sizeof(int) * ● Can be used to sz); trigger buffer underflows and overflows depending on signedness.
Overflow thoughts
● Numeric overflows ● X86 + other archs have become more can detect overflows, difficult to exploit compilers dont because of 64-bit ● :-(
Don't forget about truncation
● Look for lots of these in 64 bit code; anywhere you have variable integer sizes
● uint64_t sz = read_bytes(sizeof(uint64_t)); ● uint32_t limit = sz; ● if(limit < MAX) { … }
2 – Format strings
● printf(buf) ● sprintf,snprintf,syslog,asprintf,v*rintf, custom debug functions, ... ● Newer gcc versions warn about unspecified format strings
Deep thoughts on fmt strings
● Easy to find ● Direct parameter access not available on windows ● Latest VC++ disables %n entirely
3 – off by ones
● char buf[256]; int i; ● for(i = sizeof(buf); i >= 0; i—) ● for(i = 0; I <= sizeof(buf); i++) ● for(i = 0; sizeof(buf) > i; i++) ● char *buf = malloc(256); buf[256] = 0;
NUL termination
● “The strncpy() function is similar, except that at most n bytes are copied. Warning: If there is no null byte among the first of src, the string placed in dest will not be null terminated.” “The functions snprintf() and vsnprintf() write at most size bytes (including the trailing null byte ('\0')) to str.”
float != double
● Float 8 bit exponent 23 bit mantissa About 7 decimal digits precision ● Double 11 bit exponent 53 bit mantissa About 16 decimal digits precision
Floating point woes
#include
Output
● a = 1265603328.000000 ● b = 1265603328.000000 ● dt = 0.000000
Spot the bug int verify(char* in, char* pass) { if(strcmp(in,pass) == 0) return STATUS_PASSWORD_OK; return STATUS_PASSWORD_ERROR; }
buf = new T[x]
● buf = malloc(x*sizeof(T)); for(i=0; i
delete x != delete[] x
● delete x call destructor on *x free(x) ● delete[] x ask memory manager for size of block for(i=0; i Double free / use after free ● Member functions are just functions with a hidden parameter “this” ● Calling on an invalid pointer will sometimes succeed ● Data corruption, especially static / global variables, typically results Reference counting ● Double free = drop reference counter twice ● We have one object and free it twice ● Refcount is now... ? Exception handling ● Exception records are usually on the stack ● Corrupt these and throw an exception Don't trust your compiler #include Here's a harder one #include printf("a = %3d, b = %3d. ",a, b); if(a == b) printf("Equal\n"); else printf("Not equal\n"); } return 0; } Citations, Good reading ● http://www.ruxcon.org.au/files/2006/unusual_bugs.pdf ● http://taossa.com/index.php/the-vault/chapter-6-c-language-issues/ ● http://cr.yp.to/2004-494.html