Share
Embed
Programming '18: Introspection for C and its Applications to Library Robustness
Manuel Rigger
April 12, 2018
Transcript
102 slides
-
Introspection for C and its Applications to Library Robustness Manuel
Rigger1, René Mayrhofer1, Roland Schatz2, Matthias Grimmer2, Hanspeter Mössenböck1 1 Johannes Kepler University Linz, Austria 2 Oracle Labs Linz, Austria <Programming> 2018, 12 April 2018, Nice, France. -
Problem: Errors in C 2 • Problem 1: C lacks
automatic checks • Errors can be exploited by attackers or cause hard-to-find-bugs • Problem 2: C objects lack type information • No defensive programming -
Problem: Errors in C • No bounds checks • No
bounds information 3 int *arr = malloc(3 * sizeof(int)); arr[5] = … Buffer Overflows -
Problem: Errors in C 4 free(stackobject); Invalid free error free(heapobject);
free(heapobject); Double-free error free(heapobject); heapobject[0] = val; Use-after-free error free(heapobject); Memory leak Memory Management Errors • No automatic memory management • No location/liveness information -
Problem: Errors in C 5 Type Confusion void apply(long* arr,
size_t n, long f(long arg1)); • No type safety • No type information -
Problem: Errors in C 5 Type Confusion void apply(long* arr,
size_t n, long f(long arg1)); int f(int arg1) • No type safety • No type information -
Problem: Errors in C 6 Format String Vulnerabilities printf("%d %d
%s" , 3, 5); Missing argument printf("%s", 3); Type Confusion • No checks for accessing variadic arguments • No information for number and types of variadic arguments -
Problem: Errors in C • Problem 1: C lacks automatic
checks • Errors can be exploited by attackers or cause hard-to-find-bugs • Problem 2: C objects lack type information • No defensive programming 7 Addressed by state-of-the-art tools -
State-of-the-art Run-time Approaches 8 int *arr = malloc(3 * sizeof(int));
arr[5] = … Detected buffer overflow exiting program -
State-of-the-art Run-time Approaches • LLVM’s sanitizers • Valgrind, Dr. Memory
• Libcrunch • SoftBound+CETS • Safe Sulong 9 -
State-of-the-art Run-time Approaches • LLVM’s sanitizers • Valgrind, Dr. Memory
• Libcrunch • SoftBound+CETS • Safe Sulong 9 These systems track object metadata to implement checks -
Problem: Errors in C • Problem 1: C lacks automatic
checks • Errors can be exploited by attackers or cause hard-to-find-bugs • Problem 2: C objects lack type information • No defensive programming 10 Unaddressed -
Goal 11 Expose metadata through an introspection interface so that
programmers can enhance the robustness of their libraries -
Motivation 12 Goals Log error and continue Return error code
Add assertions -
Motivation 12 Goals Log error and continue Return error code
Add assertions Programmers could “override” the default behavior of aborting the program -
Structure 13 Ongoing work Implementation in tools Case study on
libc Introspection interface -
Bounds 17 int *arr = malloc(sizeof (int) * 10) ;
int *ptr = &(arr[4]); printf ("%ld\n", size_left(ptr)); // prints 16 _size_left() sizeof(int) * 10 -
Bounds 18 int *arr = malloc(sizeof (int) * 10) ;
int *ptr = &(arr[4]); printf ("%ld\n", size_left(ptr)); // prints 16 printf ("%ld\n", size_right(ptr)); // prints 24 _size_right() sizeof(int) * 10 -
Memory Location 19 enum Location location(void *); enum Location {
INVALID, AUTOMATIC, DYNAMIC, STATIC }; -
Memory Location 20 int a; void func() { static int
b; int c; int *d = malloc(sizeof(int) * 10); free(d); } -
Memory Location 20 int a; void func() { static int
b; int c; int *d = malloc(sizeof(int) * 10); free(d); } location(&a) STATIC -
Memory Location 20 int a; void func() { static int
b; int c; int *d = malloc(sizeof(int) * 10); free(d); } location(&b) STATIC -
Memory Location 20 int a; void func() { static int
b; int c; int *d = malloc(sizeof(int) * 10); free(d); } location(&c) AUTOMATIC -
Memory Location 20 int a; void func() { static int
b; int c; int *d = malloc(sizeof(int) * 10); free(d); } location(d) DYNAMIC -
Memory Location 20 int a; void func() { static int
b; int c; int *d = malloc(sizeof(int) * 10); free(d); } location(d) INVALID -
Type Information 21 void* try_cast(void *, struct Type*); Return the
pointer or NULL similar to dynamic_cast in C++ -
Type Information 22 void* try_cast(void *, struct Type*); Check if
an object is "compatible" with a type -
Type Information 23 int arr[10]; int *ptr = &(arr[9]); int
val; val = try_cast(&ptr, type(val)); -
Variadic Arguments 25 int printf(const char * format, ... )
{ if (count_varargs() != 3) { abort(); } int int1 = *get_vararg(0, type(int1)); int int2 = *get_vararg(1, type(&int2)); char* str = *get_vararg(2, type(&str)); } "%d %d %s" -
Introspection Goals 27 Goals Improve availability of the system Fix
incomplete APIs Improve bug-finding capabilities -
Introspection to Increase Availability • Continue execution after mitigating an
error • E.g., make libc robust against not NUL-terminated strings 28 size_t strlen(const char *str); char *strcpy(char *dest, const char *src); … -
Example: strlen() 29 size_t strlen(const char *str) { size_t len
= 0; while (*str != '\0') { len++; str++; } return len; } -
Example: strlen() 29 size_t strlen(const char *str) { size_t len
= 0; while (*str != '\0') { len++; str++; } return len; } P r o g r a m m i n g \0 ... ... -
Example: strlen() 29 size_t strlen(const char *str) { size_t len
= 0; while (*str != '\0') { len++; str++; } return len; } P r o g r a m m i n g \0 ... ... -
Example: strlen() 29 size_t strlen(const char *str) { size_t len
= 0; while (*str != '\0') { len++; str++; } return len; } 11 P r o g r a m m i n g \0 ... ... -
Example: strlen() 30 size_t strlen(const char *str) { size_t len
= 0; while (*str != '\0') { len++; str++; } return len; } P r o g r a m m i n g ... ... -
Example: strlen() 30 size_t strlen(const char *str) { size_t len
= 0; while (*str != '\0') { len++; str++; } return len; } P r o g r a m m i n g ... ... -
Example: strlen() 30 size_t strlen(const char *str) { size_t len
= 0; while (*str != '\0') { len++; str++; } return len; } 23415 P r o g r a m m i n g ... ... -
size_t strlen(const char *str) { size_t len = 0; while
( size_right(str) > 0 && *str != '\0') { len++; str++; } return len; } Example: strlen() 31 P r o g r a m m i n g ... ... -
size_t strlen(const char *str) { size_t len = 0; while
( size_right(str) > 0 && *str != '\0') { len++; str++; } return len; } Example: strlen() 31 P r o g r a m m i n g ... ... -
size_t strlen(const char *str) { size_t len = 0; while
( size_right(str) > 0 && *str != '\0') { len++; str++; } return len; } Example: strlen() 31 11 P r o g r a m m i n g ... ... -
Introspection to Improve Bug-Finding • Check invariants • Abort when
detecting inconsistencies • E.g., allow libc’s “safe” functions to detect incorrect buffer sizes 32 size_t strnlen_s(const char *str, size_t maxlen); errno_t strcpy_s(char *dest, rsize_t maxdestsz, const char *src); -
Improve bug-finding capabilities 33 size_t strnlen_s(char *str, rsize_t maxsize) {
size_t i = 0; for (; i < maxsize && s[i] != '\0'; i++); return i; } -
Improve bug-finding capabilities 33 size_t strnlen_s(char *str, rsize_t maxsize) {
size_t i = 0; for (; i < maxsize && s[i] != '\0'; i++); return i; } P r o g r a m m i n g \0 ... ... 100 -
Improve bug-finding capabilities 33 size_t strnlen_s(char *str, rsize_t maxsize) {
size_t i = 0; for (; i < maxsize && s[i] != '\0'; i++); return i; } P r o g r a m m i n g \0 ... ... 100 11 -
Improve bug-finding capabilities 33 size_t strnlen_s(char *str, rsize_t maxsize) {
size_t i = 0; for (; i < maxsize && s[i] != '\0'; i++); return i; } P r o g r a m m i n g \0 ... ... 100 11 Correct result but did not detect incorrect buffer size -
Improve bug-finding capabilities 34 size_t strnlen_s(char *str, rsize_t maxsize) {
if ( size_right(str) < maxsize) { abort(); } else { size_t i = 0; for (; i < maxsize && s[i] != '\0'; i++); return i; } } -
Improve bug-finding capabilities 34 size_t strnlen_s(char *str, rsize_t maxsize) {
if ( size_right(str) < maxsize) { abort(); } else { size_t i = 0; for (; i < maxsize && s[i] != '\0'; i++); return i; } } P r o g r a m m i n g \0 ... ... 100 -
Improve bug-finding capabilities 34 size_t strnlen_s(char *str, rsize_t maxsize) {
if ( size_right(str) < maxsize) { abort(); } else { size_t i = 0; for (; i < maxsize && s[i] != '\0'; i++); return i; } } P r o g r a m m i n g \0 ... ... 100 Abort -
Introspection to Fix Errors in API Design 35 char *gets(char
*str); ... ... -
Introspection to Fix Errors in API Design 35 char *gets(char
*str); ... ... -
Introspection to Fix Errors in API Design 36 char* gets(char
*str) { int size = size_right(str); return gets_s(str, size); } -
Introspection to Fix Errors in API Design 36 Make gets()
robust against input that would overflow the buffer char* gets(char *str) { int size = size_right(str); return gets_s(str, size); } -
Other examples 37 void qsort(void *base, size_t nitems, size_t size,
int (*compar)(const void *, const void*)); void *bsearch(const void *key, const void *base, size_t nel, size_t width, int (*compar)(const void *, const void *)); -
Other examples 37 void qsort(void *base, size_t nitems, size_t size,
int (*compar)(const void *, const void*)); void *bsearch(const void *key, const void *base, size_t nel, size_t width, int (*compar)(const void *, const void *)); Verify the function pointer -
Other examples 38 int printf(const char *format, ...); int sprintf(char
*str, const char *format, ...); int fprintf(FILE *stream, const char *format, ...); -
Other examples 38 int printf(const char *format, ...); int sprintf(char
*str, const char *format, ...); int fprintf(FILE *stream, const char *format, ...); Verify the number and types of variadic arguments -
Other examples 39 void *realloc(void *ptr, size_t new_size); void free(void
*ptr); Verify that the object was dynamically allocated -
Implementation in Safe Sulong 41 JVM C C++ Fortran ...
Execute on Safe Sulong tracks all necessary metadata -
Implementation in Safe Sulong 42 int *arr = malloc(3 *
sizeof(int)); int *ptr = &arr[1]; -
Implementation in Safe Sulong 42 int *arr = malloc(3 *
sizeof(int)); int *ptr = &arr[1]; ptr: Address offset=4 data I32Array contents {0, 0, 0} location=AUTOMATIC location=DYNAMIC -
Implementation in Safe Sulong 42 int *arr = malloc(3 *
sizeof(int)); int *ptr = &arr[1]; ptr: Address offset=4 data I32Array contents {0, 0, 0} location=AUTOMATIC location=DYNAMIC size_right(ptr) data.contents.length * sizeof(int) – offset 3 * 4 – 4 = 8 -
Implementation in Safe Sulong 43 int *arr = malloc(3 *
sizeof(int)); int *ptr = &arr[1]; ptr: Address offset=4 data I32Array contents {0, 0, 0} location=AUTOMATIC location=DYNAMIC location(ptr) DYNAMIC -
Implementation in Safe Sulong 44 int *arr = malloc(3 *
sizeof(int)); int *ptr = &arr[1]; ptr: Address offset=4 data I32Array contents {0, 0, 0} location=AUTOMATIC location=DYNAMIC try_cast(ptr, type(*ptr)) ptr -
Ongoing Work 45 • Implementation in other tools • Case
Study on real-world bugs -
Ongoing work: LLVM’s AddressSanitizer 46 ... ... • LLVM’s AddressSanitizer:
memory error detector based on shadow memory size_right() -
Ongoing work: LLVM’s AddressSanitizer 46 ... ... • LLVM’s AddressSanitizer:
memory error detector based on shadow memory size_right() -
Ongoing work: GCC’s Intel MPX Bounds Checks Instrumentation • Intel
MPX: bounds registers and instructions • GCC uses them to detect memory errors 47 -
Ongoing work: GCC’s Intel MPX Bounds Checks Instrumentation • Intel
MPX: bounds registers and instructions • GCC uses them to detect memory errors 47 ssize_t size_right(void* p){ ssize_t upper_bounds = (ssize_t)__builtin___bnd_get_ptr_ubound(p); size_t size = (size_t) (upper_bounds + 1) - (size_t) p; return (ssize_t) size; } -
Ongoing work: SoftBound • SoftBound: bounds checker • Tracks upper
bounds for each pointer 48 ssize_t _size_right(const char* p) { const char* bound = __softboundcets_load_bound_shadow_stack(1); return bound - p; } -
Ongoing work: Real-world bugs • Case study on bugs contained
in the CVE database • Libc goal: Availability 49 https://cve.mitre.org/ -
CVE-2017-14493 • Dnsmasq: DHCP server • Incorrect size argument to
memcpy() caused buffer overflow 50 state->mac_len = opt6_len(opt) - 2; memcpy(&state->mac[0], opt6_ptr(opt, 2), state->mac_len); -
CVE-2017-14493 • Dnsmasq: DHCP server • Incorrect size argument to
memcpy() caused buffer overflow 50 state->mac_len = opt6_len(opt) - 2; memcpy(&state->mac[0], opt6_ptr(opt, 2), state->mac_len); The server stayed fully functional after mitigation -
CVE-2017-9047 • Libxml2: XML parsing library • String concatenating caused
a buffer overflow 51 if (content->name != NULL) strcat(buf, (char *) content->name); -
CVE-2017-9047 • Libxml2: XML parsing library • String concatenating caused
a buffer overflow 51 The parser printed a truncated error message if (content->name != NULL) strcat(buf, (char *) content->name); -
CVE-2017-16352 • GraphicsMagick: image processing library • Incorrect size argument
to strncpy() 52 for (p=image->directory; *p != ’\0’; p++) { q=p; while ((*q != ’\n’) && (*q != ’\0’)) q++; (void) strncpy(image_info->filename,p,q-p); image_info->filename[q-p]=’\0’; } -
CVE-2017-16352 • GraphicsMagick: image processing library • Incorrect size argument
to strncpy() 52 The error was mitigated… for (p=image->directory; *p != ’\0’; p++) { q=p; while ((*q != ’\n’) && (*q != ’\0’)) q++; (void) strncpy(image_info->filename,p,q-p); image_info->filename[q-p]=’\0’; } -
53 for (p=image->directory; *p != ’\0’; p++) { q=p; while
((*q != ’\n’) && (*q != ’\0’)) q++; (void) strncpy(image_info- >filename,p,q-p); image_info->filename[q-p]=’\0’; } -
CVE-2017-16352 • GraphicsMagick: image processing library • Incorrect size argument
to strncpy() 54 for (p=image->directory; *p != ’\0’; p++) { q=p; while ((*q != ’\n’) && (*q != ’\0’)) q++; (void) strncpy(image_info->filename,p,q-p); image_info->filename[q-p]=’\0’; } -
CVE-2017-16352 • GraphicsMagick: image processing library • Incorrect size argument
to strncpy() 54 … but the application code had a subsequent out-of-bounds access for (p=image->directory; *p != ’\0’; p++) { q=p; while ((*q != ’\n’) && (*q != ’\0’)) q++; (void) strncpy(image_info->filename,p,q-p); image_info->filename[q-p]=’\0’; } -
Discussion • Introspection as a complement to automatic checks 56
P r o g r a m m i n g ... ... -
Discussion • Introspection as a complement to automatic checks 56
size_t strlen(const char *str) P r o g r a m m i n g ... ... 11 -
Discussion • Introspection as a complement to automatic checks 56
size_t strlen(const char *str) P r o g r a m m i n g ... ... size_t my_strlen(const char *str) 11 abort -
Discussion • Introspection as a complement to automatic checks 56
size_t strlen(const char *str) P r o g r a m m i n g ... ... size_t my_strlen(const char *str) 11 abort Automatic checks still abort if the error is not mitigated -
Discussion • Introspection as a complement to automatic checks •
What about partial/no support of introspection? 57 -
Discussion • Introspection as a complement to automatic checks •
What about partial/no support of introspection? 57 size_right(ptr); LONG_MAX -
Discussion • Introspection as a complement to automatic checks •
What about partial/no support of introspection? 57 size_right(ptr); LONG_MAX Sensible default values and conservative checks? -
Discussion • Introspection as a complement to automatic checks •
What about partial/no support of introspection? • Safer languages 58 Legacy software -
Discussion • Introspection as a complement to automatic checks •
What about partial/no support of introspection? • Safer languages • Programming effort 59 Introspection checks only useful for frequently used libraries