39
What happened to my code? Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012.

Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

Embed Size (px)

Citation preview

Page 1: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

What happened to my code?

Computer Security 2014 – Ymir Vigfusson

Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012.

Page 2: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

2

Linux AGP: [CVE-2011-1745]

Page 3: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

3

Linux AGP: [CVE-2011-1745]

What if pg_start + page_count wraps around?

Page 4: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

4

Linux kernel: flow steering

Page 5: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

5

Linux kernel: flow steering

sizeof(struct rps_dev_flow) is 8, so the check for count of at most 1<<30 is insufficient to prevent overflow

Page 6: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

6

Linux kernel

Page 7: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

7

Linux kernel

On 64-bit systems, ULONG_MAX is 64-bits, whereas opt is 32-bits. The check is thus insufficient to avoid overflow.

Page 8: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

8

Linux: ceph file system

Page 9: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

9

Linux: ceph file system

Do your math correctly. The correct check for “a + b*x” is “x > (uintmax – a) / b”

Page 10: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

10

Linux: OLPC display controller

Page 11: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

11

Linux: OLPC display controller

Value of status is in the range of 0-255. This check will never be called.

Page 12: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

12

Linux: AX.25

Page 13: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

13

Linux: AX.25

Remember that a comparing signed and unsigned numbers will be viewed as an unsigned comparison

Page 14: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

14

But fixing isn‘t easy ...

Page 15: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

15

Linux kernel: lib/mpi/mpi-pow.c int mpi_powm( MPI res, MPI base, MPI exp, MPI mod) { mpi_size_t esize, msize, bsize, rsize; int esign, msign, bsign, rsign; mpi_size_t size; mpi_size_t tsize=0; /* to avoid compiler warning */ ... 

esize = exp->nlimbs; msize = mod->nlimbs; size = 2 * msize; esign = exp->sign; msign = mod->sign;  rp = res->d; ep = exp->d;  if( !msize )

msize = 1 / msize; /* provoke a signal */  if( !esize ) {

/* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0 * depending on if MOD equals 1. */rp[0] = 1;res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1;res->sign = 0;goto leave;

}

Page 16: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

16

Linux kernel: lib/mpi/mpi-pow.c int mpi_powm( MPI res, MPI base, MPI exp, MPI mod) { mpi_size_t esize, msize, bsize, rsize; int esign, msign, bsign, rsign; mpi_size_t size; mpi_size_t tsize=0; /* to avoid compiler warning */ ... 

esize = exp->nlimbs; msize = mod->nlimbs; size = 2 * msize; esign = exp->sign; msign = mod->sign;  rp = res->d; ep = exp->d;  if( !msize )

msize = 1 / msize; /* provoke a signal */  if( !esize ) {

/* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0 * depending on if MOD equals 1. */rp[0] = 1;res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1;res->sign = 0;goto leave;

}

Division by zero is undefined. No exception generated on MIPS/PowerPC. Optimized out by Clang!

Page 17: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

17

Linux kernel: lib/ext4/super.cstatic int ext4_fill_flex_info(struct super_block *sb) { struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_group_desc *gdp = NULL, ext4_group_t flex_group_count, ext4_group_t flex_group; unsigned int groups_per_flex = 0; size_t size; int i; sbi->s_log_groups_per_flex = sbi->s_es>s_log_groups_per_flex; if (sbi->s_log_groups_per_flex < 1 || sbi->s_log_groups_per_flex > 31) { sbi->s_log_groups_per_flex = 0; return 1; } groups_per_flex = 1 << sbi->s_log_groups_per_flex; if (groups_per_flex == 0) return 1; /* We allocate both existing and potentially added groups */ flex_group_count = ((sbi->s_groups_count + groups_per_flex - 1) + ((le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) + 1) << EXT4_DESC_PER_BLOCK_BITS(sb))) / groups_per_flex; size = flex_group_count * sizeof(struct flex_groups);

Page 18: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

18

Linux kernel: lib/ext4/super.c

Left shift of n-bit integer by n is undefined. Compiler thinks result is positive and removes the check.

static int ext4_fill_flex_info(struct super_block *sb) { struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_group_desc *gdp = NULL, ext4_group_t flex_group_count, ext4_group_t flex_group; unsigned int groups_per_flex = 0; size_t size; int i; sbi->s_log_groups_per_flex = sbi->s_es>s_log_groups_per_flex; if (sbi->s_log_groups_per_flex < 1 || sbi->s_log_groups_per_flex > 31) { sbi->s_log_groups_per_flex = 0; return 1; } groups_per_flex = 1 << sbi->s_log_groups_per_flex; if (groups_per_flex == 0) return 1; /* We allocate both existing and potentially added groups */ flex_group_count = ((sbi->s_groups_count + groups_per_flex - 1) + ((le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) + 1) << EXT4_DESC_PER_BLOCK_BITS(sb))) / groups_per_flex; size = flex_group_count * sizeof(struct flex_groups);

Page 19: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

19

Linux kernel: fs/open.c

Page 20: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

20

Linux kernel: fs/open.c

Some architectures do not silently wrap around on signed overflow. In C signed integer overflow is undefined (so “x+100 < x” is optimized away!).Here both offset and len are non-negative so GCC decides to remove the check! (Must use –fno-strict-overflow)

Page 21: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

21

Linux kernel: lib/vsprintf.c

Page 22: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

22

Linux kernel: lib/vsprintf.c

The C standard does not define pointer arithmetic to wrap around, and thus compilers do algebra on the pointers. They cancel buf from “buf + size < buf”, reducing the check to “size < 0”. (Must use –fno-strict-overflow)

Page 23: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

23

Linux kernel: fs/open.c

Page 24: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

24

Linux kernel: fs/open.c

Dereferencing a NULL pointer is undefined behavior in C, and so compilers assume that all dereferenced pointers are non-null.The check for NULL here is optimized away because of the dereferencing of tun->sk above. (Must use -fno-delete-null-pointer-checks)

Page 25: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

25

Linux kernel: include/net/iw_handler.h

Page 26: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

26

Linux kernel: include/net/iw_handler.h

Page 27: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

27

Linux kernel: include/net/iw_handler.h

“Type-punning” (viewing an object by different types) actually is pretty strict in C. Here, the compiler thinks (char *)iwe and struct iw_event *iwe are different objects and reorders the two instructions above. Thus a stale copy of iwe->len gets copied. (Must use -fno-strict-aliasing)

Page 28: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

28

FreeBSD libc: lib/libc/stdlib/rand.c

Page 29: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

29

FreeBSD libc: lib/libc/stdlib/rand.c

Misuse of old variable contents on the stack. What if “junk” is assigned to a register? (This is what GCC does). Moreover, the undefined use of junk will make Clang eliminate the entire srandom() computation, making random numbers predictable.

Page 30: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

30

PostgreSQL: src/backend/utils/adt/int8.c

Page 31: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

31

PostgreSQL: src/backend/utils/adt/int8.c

Compiler is unaware that ereport() will never return. Thus compiler assumes the division will always execute. On some platforms (Alpha, S/390, SPARC, …) GCC will move the division before the check for arg2==0 …

Page 32: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

32

Summary

Divison by zero• Not always an exception• Undefined, so be aware of instruction reordering

Oversized shift• Shifting n-bit integer by n bits is undefined in C

Signed integer overflow• Undefined by C. Checks might get optimized out

Out-of-bounds pointers• Pointer arithmetic does not wrap around

Uninitialized reads• Don’t misuse the stack

Page 33: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

33

How do we improve the

situation?

Page 34: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

34

Possible remedies

• Flagging all unexpected behavior is undecidable

• Lots of options: -ftrapv (signed overflow), -fcatch-undefined-behavior (Clang, but only a subset)

Compiler improveme

nts

• Clang‘s static analyzer, KLEE, Kint, ...• Would not catch offset+len < 0 …

Bug finding tools

• Outlaw undefined behavior in the C standard?

• May heavily restrict compilers from exploiting hardware performance. Trade-off…

Improved standard

Page 35: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

35

Sudo in 2012 – Where’s the bug?

void sudo_debug(int level, const char *fmt, ...) { va_list ap; char *fmt2;

if (level > debug_level) return;

/* Backet fmt with prog name and a newline to make it a single write */ easprintf(&fmt2, "%s: %s\n", getprogname(), fmt); va_start(ap, fmt); vfprintf(stderr, fmt2, ap); va_end(ap); efree(fmt2);}

Page 36: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

36

Asterisk phones (2012) – Where‘s the bug?

char exten[AST_MAX_EXTENSION]; static int handle_message(struct skinny_req *req, struct skinnysession *s) { case KEYPAD_BUTTON_MESSAGE: struct skinny_device *d = s->device; struct skinny_subchannel *sub; int lineInstance; int callReference; lineInstance = letohl(req->data.keypad.lineInstance); callReference = letohl(req->data.keypad.callReference); if (lineInstance) { sub = find_subchannel_by_instance_reference(d, lineInstance, callReference); } else { sub = d->activeline->activesub; } if (sub && ((sub->owner && sub->owner->_state < AST_STATE_UP) || sub->onhold)) { char dgt; int digit = letohl(req->data.keypad.button); if (digit == 14) { dgt = '*'; } else if (digit == 15) { dgt = '#'; } else if (digit >= 0 && digit <= 9) { dgt = '0' + digit; } else { dgt = '0' + digit; ast_log(LOG_WARNING, "Unsupported digit %d\n", digit); } d->exten[strlen(d->exten)] = dgt; d->exten[strlen(d->exten)+1] = '\0'; } else res = handle_keypad_button_message(req, s); } break;

Page 37: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

37

Sendmail – Where‘s the bug?

void sighndlr(int dummy) { syslog(LOG_NOTICE,user_dependent_data); // *** Initial cleanup code, calling the following somewhere: free(global_ptr2); free(global_ptr1); // *** 1 *** >> Additional clean-up code - unlink tmp files, etc << exit(0);}

/************************************************** * This is a signal handler declaration somewhere * * at the beginning of main code. * **************************************************/

signal(SIGHUP,sighndlr); signal(SIGTERM,sighndlr);

// *** Other initialization routines, and global pointer // *** assignment somewhere in the code (we assume that // *** nnn is partially user-dependent, yyy does not have to be):

global_ptr1=malloc(nnn); global_ptr2=malloc(yyy);

// *** 2 *** >> further processing, allocated memory << // *** 2 *** >> is filled with any data, etc... <<

Page 38: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

38

Sudo again – Where‘s the bug?/* Log a message to syslog, pre-pending the username and splitting the message into parts if it is longer than MAXSYSLOGLEN. */static void do_syslog( int pri, char * msg ) { int count; char * p; char * tmp; char save;

for ( p=msg, count=0; count < strlen(msg)/MAXSYSLOGLEN + 1; count++ ) { if ( strlen(p) > MAXSYSLOGLEN ) { for ( tmp = p + MAXSYSLOGLEN; tmp > p && *tmp != ' '; tmp-- ) ; if ( tmp <= p ) tmp = p + MAXSYSLOGLEN;

/* NULL terminate line, but save the char to restore later */ save = *tmp; *tmp = '\0';

if ( count == 0 ) SYSLOG( pri, "%8.8s : %s", user_name, p ); else SYSLOG( pri,"%8.8s : (command continued) %s",user_name,p ); /* restore saved character */ *tmp = save; /* Eliminate leading whitespace */ for ( p = tmp; *p != ' '; p++ ) ; } else { if ( count == 0 ) SYSLOG( pri, "%8.8s : %s", user_name, p ); else SYSLOG( pri,"%8.8s : (command continued) %s",user_name,p ); } }}

Page 39: Computer Security 2014 – Ymir Vigfusson Based on “Undefined Behavior: What Happened to My Code?” by Wang et al. APsys 2012 and Wang et al. OSDI ‘2012

39

OpenSSH – Where‘s the bug?

/* * Pointer to an array containing all allocated channels. The array is * dynamically extended as needed. */static Channel **channels = NULL;

/* * Size of the channel array. All slots of the array must always be * initialized (at least the type field); unused slots set to NULL */static u_int channels_alloc = 0;

Channel *channel_by_id(int id){

Channel *c;

if (id < 0 || (u_int)id > channels_alloc) {logit("channel_by_id: %d: bad id", id);return NULL;

}c = channels[id];if (c == NULL) {

logit("channel_by_id: %d: bad id: channel free", id);return NULL;

}return c;

}