Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Programming '18: Introspection for C and its Applications to Library Robustness

Programming '18: Introspection for C and its Applications to Library Robustness

Manuel Rigger

April 12, 2018
Tweet

More Decks by Manuel Rigger

Other Decks in Research

Transcript

  1. 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.
  2. 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
  3. Problem: Errors in C • No bounds checks • No

    bounds information 3 int *arr = malloc(3 * sizeof(int)); arr[5] = … Buffer Overflows
  4. 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
  5. Problem: Errors in C 5 Type Confusion void apply(long* arr,

    size_t n, long f(long arg1)); • No type safety • No type information
  6. 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
  7. 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
  8. 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
  9. State-of-the-art Run-time Approaches 8 int *arr = malloc(3 * sizeof(int));

    arr[5] = … Detected buffer overflow  exiting program
  10. 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
  11. 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
  12. Goal 11 Expose metadata through an introspection interface so that

    programmers can enhance the robustness of their libraries
  13. Motivation 12 Goals Log error and continue Return error code

    Add assertions Programmers could “override” the default behavior of aborting the program
  14. 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
  15. 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
  16. Memory Location 20 int a; void func() { static int

    b; int c; int *d = malloc(sizeof(int) * 10); free(d); }
  17. Memory Location 20 int a; void func() { static int

    b; int c; int *d = malloc(sizeof(int) * 10); free(d); } location(&a)  STATIC
  18. Memory Location 20 int a; void func() { static int

    b; int c; int *d = malloc(sizeof(int) * 10); free(d); } location(&b)  STATIC
  19. Memory Location 20 int a; void func() { static int

    b; int c; int *d = malloc(sizeof(int) * 10); free(d); } location(&c)  AUTOMATIC
  20. Memory Location 20 int a; void func() { static int

    b; int c; int *d = malloc(sizeof(int) * 10); free(d); } location(d)  DYNAMIC
  21. Memory Location 20 int a; void func() { static int

    b; int c; int *d = malloc(sizeof(int) * 10); free(d); } location(d)  INVALID
  22. Type Information 21 void* try_cast(void *, struct Type*); Return the

    pointer or NULL similar to dynamic_cast in C++
  23. Type Information 23 int arr[10]; int *ptr = &(arr[9]); int

    val; val = try_cast(&ptr, type(val));
  24. 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"
  25. Introspection Goals 27 Goals Improve availability of the system Fix

    incomplete APIs Improve bug-finding capabilities
  26. 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); …
  27. Example: strlen() 29 size_t strlen(const char *str) { size_t len

    = 0; while (*str != '\0') { len++; str++; } return len; }
  28. 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 ... ...
  29. 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 ... ...
  30. 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 ... ...
  31. 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 ... ...
  32. 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 ... ...
  33. 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 ... ...
  34. 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 ... ...
  35. 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 ... ...
  36. 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 ... ...
  37. 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);
  38. 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; }
  39. 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
  40. 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
  41. 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
  42. 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; } }
  43. 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
  44. 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
  45. Introspection to Fix Errors in API Design 36 char* gets(char

    *str) { int size = size_right(str); return gets_s(str, size); }
  46. 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); }
  47. 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 *));
  48. 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
  49. Other examples 38 int printf(const char *format, ...); int sprintf(char

    *str, const char *format, ...); int fprintf(FILE *stream, const char *format, ...);
  50. 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
  51. Other examples 39 void *realloc(void *ptr, size_t new_size); void free(void

    *ptr); Verify that the object was dynamically allocated
  52. Implementation in Safe Sulong 41 JVM C C++ Fortran ...

    Execute on Safe Sulong tracks all necessary metadata
  53. Implementation in Safe Sulong 42 int *arr = malloc(3 *

    sizeof(int)); int *ptr = &arr[1];
  54. 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
  55. 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
  56. 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
  57. 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
  58. Ongoing work: LLVM’s AddressSanitizer 46 ... ... • LLVM’s AddressSanitizer:

    memory error detector based on shadow memory size_right()
  59. Ongoing work: LLVM’s AddressSanitizer 46 ... ... • LLVM’s AddressSanitizer:

    memory error detector based on shadow memory size_right()
  60. Ongoing work: GCC’s Intel MPX Bounds Checks Instrumentation • Intel

    MPX: bounds registers and instructions • GCC uses them to detect memory errors 47
  61. 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; }
  62. 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; }
  63. Ongoing work: Real-world bugs • Case study on bugs contained

    in the CVE database • Libc goal: Availability 49 https://cve.mitre.org/
  64. 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);
  65. 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
  66. CVE-2017-9047 • Libxml2: XML parsing library • String concatenating caused

    a buffer overflow 51 if (content->name != NULL) strcat(buf, (char *) content->name);
  67. 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);
  68. 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’; }
  69. 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’; }
  70. 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’; }
  71. 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’; }
  72. 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’; }
  73. 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
  74. 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
  75. 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
  76. Discussion • Introspection as a complement to automatic checks •

    What about partial/no support of introspection? 57
  77. Discussion • Introspection as a complement to automatic checks •

    What about partial/no support of introspection? 57 size_right(ptr); LONG_MAX
  78. 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?
  79. Discussion • Introspection as a complement to automatic checks •

    What about partial/no support of introspection? • Safer languages 58 Legacy software
  80. 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