checked c
play

Checked C Michael Hicks The University of Maryland joint work with - PowerPoint PPT Presentation

Checked C Michael Hicks The University of Maryland joint work with David Tarditi (MSR), Andrew Ruef (UMD), Sam Elliott (UW) UM Motivation - Lots of C/C++ code out there. - One open source code indexer (openhub.net) found 8.5 billion lines of C


  1. Checked C Michael Hicks The University of Maryland joint work with David Tarditi (MSR), Andrew Ruef (UMD), Sam Elliott (UW) UM

  2. Motivation - Lots of C/C++ code out there. - One open source code indexer (openhub.net) found 8.5 billion lines of C code. - Huge investment in existing (often unsafe) code - Many approaches “saving C” have been proposed : - Static/dynamic analysis (ASAN, Softbound), fuzzing, OS/HW- level mitigations - Rewrite the code in a type-safe language (Java/Rust…) - New dialects of C (Cyclone, CCured, Deputy, …) 2

  3. Checked C Checked C is another take at making system software written in C more reliable and secure. Approach: - Extend C so that type-safe programs can be written in it. - A new language risks introducing unnecessary differences. - Backward compatible , support incremental conversion of code to be type-safe. - Implement in widely used C compilers (Clang/ LLVM ) - Create automated conversion tools - Hypothesis: most source code behaves in a type-safe fashion. - Evaluate and get real-world use . Iterate based on experience. https://github.com/Microsoft/checkedc https://github.com/Microsoft/checkedc-clang 3

  4. Design Like Cyclone • Making code type safe • Static/dynamic bounds checking for pointers and arrays • Initialization of data, type casts, explicit memory management • No temporal safety enforcement at present (use GC) Unlike Cyclone • Backward compatibility • All pointers binary-compatible (no “fat” pointers) • Strict extension (parsing) of C • Unchecked and checked code can co-exist (e.g., in a function) • The former can use checked types in a looser manner • Can prove a “blame” property that characterizes isolation • Can work with improving hardware designs 4

  5. Clang & LLVM Checked LLVM C's x86 / Clang Analyses x64 / Frontend ARM / Optimizations Analyses Other Assembly LLVM IR Code Generators Generator 5

  6. Pointers T* val = malloc(sizeof(T)); Singleton Pointer ptr<T> val = malloc(sizeof(T)); T* vals = calloc(n, sizeof(T)); T vals[N] = { ... }; Array Pointer array_ptr<T> vals = calloc(n, sizeof(T)); T vals checked[N] = { ... }; 6

  7. Bounds Declarations Declaration Invariant array_ptr<T> p : bounds(l, u) l ≤ p < u array_ptr<T> p : count(n) p ≤ p < p+n array_ptr<T> p : byte_count(n) p ≤ p < (char*)p + n Expressions in bounds(l, u) must be non-modifying - No Assignments or Increments/Decrements - No Calls 7

  8. NUL Terminated Pointers char* str = calloc(n, sizeof(char)); NT Array Pointer char str[N] = { ... }; nt_array_ptr<char> str : count(n-1) = calloc(n, sizeof(char)); char str checked[N+1] = { …, ‘\0’ }; l ≤ p ≤ u && ∃ c ≥ u. *c == ‘\0’ nt_array_ptr<T> p : bounds(l, u) can read *u or write ‘\0’ to it 8

  9. Bounds Expansion size_t my_strlcpy( nt_array_ptr<char> dst: count(dst_sz - 1), nt_array_ptr<char> src, size_t dst_sz) { size_t i = 0; nt_array_ptr<char> s : count(i) = src; while (s[i] != ’\0’ && i < dst_sz - 1) { //bounds on s may expand by 1 dst[i] = s[i]; ++i; } dst[i] = ’\0’; //ok to write to upper bound return i; } 9

  10. Where Dynamic Checks Occur p[n] Pointer Dereference int i = *p; p->field Assignment *p = 0; Compound Assignment *p += 1; Increment/Decrement (*p)++; Elided if the compiler can prove the access is safe 10

  11. Copy data bool echo( int16_t user_length, from user_payload size_t user_payload_len, into new buffer in char *user_payload, resp_t *resp) { resp object char *resp_data = malloc(user_length); Example inspired by resp->payload_buf = resp_data; Heartbleed error resp->payload_buf_len = user_length; // memcpy(resp->payload_buf, user_payload_buf, user_length) for (int i = 0; i < user_length; i++) { resp->payload_buf[i] = user_payload_buf[i]; } return true; } user_length is user_payload_len is typedef struct { size_t payload_len; provided by user from the parser char *payload; // ... } resp_t; 11

  12. Copy data bool echo( int16_t user_length, from user_payload size_t user_payload_len, into new buffer in char *user_payload, resp_t *resp) { resp object char *resp_data = malloc(user_length); resp->payload = resp_data; resp->payload_len = user_length; // memcpy(resp->payload_buf, user_payload_buf, user_length) malloc could fail for (int i = 0; i < user_length; i++) { resp->payload_buf[i] = user_payload_buf[i]; } return true; } user_length is user_payload_len is typedef struct { size_t payload_len; provided by user from the parser char *payload; // ... } resp_t; 12

  13. Copy data bool echo( int16_t user_length, from user_payload size_t user_payload_len, into new buffer in char *user_payload, resp_t *resp) { resp object char *resp_data = malloc(user_length); resp->payload = resp_data; resp->payload_len = user_length; // memcpy(resp->payload, user_payload, user_length) for (size_t i = 0; i < user_length; i++) { resp->payload[i] = user_payload[i]; } return true; } user_length is user_payload_len is typedef struct { size_t payload_len; user_length could be provided by user from the parser char *payload; // ... larger than user_payload_len } resp_t; 13

  14. bool echo( Step 1: int16_t user_length, Manually size_t user_payload_len, array_ptr<char> user_payload, Convert to ptr<resp_t> resp) { Checked Types array_ptr<char> resp_data = malloc(user_length); resp->payload = resp_data; resp->payload_len = user_length; // memcpy(resp->payload, user_payload, user_length) for (size_t i = 0; i < user_length; i++) { resp->payload[i] = user_payload[i]; } return true; } typedef struct { size_t payload_len; array_ptr<char> payload; // ... } resp_t; 14

  15. bool echo( int16_t user_length, size_t user_payload_len, array_ptr<char> user_payload : count(user_payload_len), ptr<resp_t> resp) { array_ptr<char> resp_data : count(user_length) = malloc(user_length); resp->payload = resp_data; resp->payload_len = user_length; Step 2: Manually // memcpy(resp->payload, user_payload, user_length) for (size_t i = 0; i < user_length; i++) { Add Bounds resp->payload[i] = user_payload[i]; Declarations } return true; } typedef struct { size_t payload_len; array_ptr<char> payload : count(payload_len); // ... } resp_t; 15

  16. bool echo( Step 3: int16_t user_length, Compiler Inserts size_t user_payload_len, array_ptr<char> user_payload : count(user_payload_len), Checks Automatically ptr<resp_t> resp) { array_ptr<char> resp_data : count(user_length) = malloc(user_length); dynamic_check(resp != NULL); resp->payload = resp_data; resp->payload_len = user_length; malloc now checked // memcpy(resp->payload, user_payload, user_length) for (size_t i = 0; i < user_length; i++) { dynamic_check(user_payload != NULL); dynamic_check(user_payload <= &user_payload[i]); dynamic_check(&user_payload[i] < user_payload + user_payload_len); dynamic_check(resp->payload != NULL); dynamic_check(resp->payload <= &resp->payload[i]); dynamic_check(&resp->payload[i] < resp->payload + resp->payload_len resp->payload[i] = user_payload[i]; } No Memory Disclosure return true; 16 }

  17. bool echo( Step 3: int16_t user_length, Compiler Inserts size_t user_payload_len, array_ptr<char> user_payload : count(user_payload_len), Checks Automatically ptr<resp_t> resp) { array_ptr<char> resp_data : count(user_length) = malloc(user_length); Code Not Bug-Free: dynamic_check(resp != NULL); Will signal run-time error if either resp->payload = resp_data; resp->payload_len = user_length; malloc now checked - malloc(user_length) fails // memcpy(resp->payload, user_payload, user_length) - user_length > user_payload_len for (size_t i = 0; i < user_length; i++) { dynamic_check(user_payload != NULL); dynamic_check(user_payload <= &user_payload[i]); dynamic_check(&user_payload[i] < user_payload + user_payload_len); But: Vulnerable Executions Prevented dynamic_check(resp->payload != NULL); dynamic_check(resp->payload <= &resp->payload[i]); dynamic_check(&resp->payload[i] < resp->payload + resp->payload_len resp->payload[i] = user_payload[i]; } No Memory Disclosure return true; 17 }

  18. Step 4: bool echo( int16_t user_length, Restrictions on bounds size_t user_payload_len, expressions may allow removal array_ptr<char> user_payload : count(user_payload_len), ptr<resp_t> resp) { array_ptr<char> resp_data : count(user_length) = malloc(user_length); dynamic_check(resp != NULL); resp->payload = resp_data; resp->payload_len = user_length; malloc still checked dynamic_check(user_payload != NULL); dynamic_check(resp->payload != NULL); // memcpy(resp->payload, user_payload, user_length) for (size_t i = 0; i < user_length; i++) { dynamic_check(i <= user_payload_len); resp->payload[i] = user_payload[i]; } return true; } No Memory Disclosure 18

  19. Partially Converted Code - We may not want (or be able) to port all at once - Can use checked types wherever we want - Allows some hard-to-prove-safe idioms - But adds risk void more(int *b, int idx, ptr<int *>out) { int oldidx = idx, c; do { c = readvalue(); b[idx++] = c; //could overflow b? } while (c != 0); *out = b+idx-oldidx; //bad if out corrupted } 19

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend