1 / 27

Memory Safety Without Runtime Checks or Garbage Collection

Memory Safety Without Runtime Checks or Garbage Collection. By Dinakar Dhurjati Joint work with Sumant Kowshik, Vikram Adve and Chris Lattner University of Illinois at Urbana-Champaign. Motivation. Secure Online Upgrades in Embedded Systems.

meadel
Download Presentation

Memory Safety Without Runtime Checks or Garbage Collection

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Memory Safety Without Runtime Checks or Garbage Collection By Dinakar Dhurjati Joint work with Sumant Kowshik, Vikram Adve and Chris Lattner University of Illinois at Urbana-Champaign

  2. Motivation Secure Online Upgrades in Embedded Systems Upgrade := new software modules in to host application • Same address space • Need to protect the host from Buggy/Untrusted modules • Need to ensure each module is memory safe Memory safe := Never access a memory location outside its data area Never execute instructions outside its code area Need Language and Compiler support for memory safety

  3. Existing Solutions Type checker disallows Runtime null pointer checks Runtime array bound checks Not expressible No free + Garbage Collection [ + scoped regions + runtime checks] Runtime checksorgarbage collection unattractive for Embedded Code

  4. Our Approach Goal : 100 % Static Checking • Minimal semantic restrictions to enable static checking • No new syntax or annotations • Aggressive compiler techniques (old and new)

  5. Our Previous Work[CASES2002] Static safety for real time control programs Type checker disallows Initialize to reserved address range Restrict index to be affine in terms of size Language rule + Compiler checks ???

  6. Contributions of this Work • 100% static technique for ensuring heap safety for “type safe” C programs • No Runtime Checks • No Garbage Collection : allow explicit deallocation! • No Programmer annotations • Evaluate our approach on 17 embedded benchmarks • Array Safety • Heap safety • Stack safety

  7. Talk Outline • Introduction • Our Solution • Results • Related Work • Conclusion

  8. Methodology • Do not prevent uses of dangling pointers to freed memory • Ensure that they cannot cause memory safety violation • Builds on a compiler transformation called “Automatic Pool Allocation”

  9. Dangling pointer problem p r r q q->next q q q struct S *p, *q; …. p = q …. free(p) … q->next->val = … //dangling pointer usage r = malloc(sizeof(struct T))

  10. Making Dangling Pointers Safe s q s ->next q->next q struct S *p, *q; …. p = q …. free(p) … q->next->val = … //dangling pointer usage s = malloc(sizeof(struct S)) Principle : If freed memory is reallocated to any object of the same type with same alignment, then dereferencing pointers to freed memory is safe.

  11. Exploiting the principle • First simple solution • N different heaps based on type, N = #types • Never move memory from one heap to other BUT : Increased memory consumption • A more sophisticated solution : Using previously developed compiler transformation called Automatic Pool Allocation

  12. Automatic Pool Allocation • Identifies logical data structures not reachable from outside a function • Creates a separate pool (region) for nodes of that data structure. • At the function exit, entire pool is deallocated • Advantages : • Fine grained pools • Small life times • Type homogenous pools • Explicit deallocation

  13. Pool Allocation Example h(struct S *p) { … for (j = 0; j < 100000; j++) { tmp = malloc(sizeof(struct s)) insert_tmp_to_list(tmp,p); …. q = least_useful_member(p) free(q); } … } p f() { … p =g(); … p->next->val = ….. } g() { … p = create_list_10_nodes(p); h(p); free_everything_but_head(p); … return p; }

  14. Pool Allocation Example h(struct S *p, PoolPointer *PP) { … for (j = 0; j < 100000; j++) { tmp = poolalloc(PP); insert_tmp_to_list(tmp,p); …. q = least_useful_member(p); poolfree(q, PP); } … } PP p f() { PP = poolinit(struct S); … p =g(PP); … p->next->val = ….. pooldestroy(PP) } g(PoolPointer *PP) { … p = create_list_10_nodes(PP); h(p, PP); free_everything_but_head(p, PP); … return p; } Not Memory Safe

  15. Using Pool Allocation for Safety • Pools are type homogenous • Restriction : Do not release memory from a pool until pooldestroy => The principle is satisfied : “Accessible freed memory is reallocated only to objects of the same type”. Memory Safety guaranteed Problem • Could lead to increased memory consumption • Need to identify when it happens

  16. Identifying increased memory usage h(struct S *p, PoolPointer *PP) { … //no free after allocation for (j = 0; j < 10; j++) { …. q = least_useful_member(p) poolfree(q,PP); } … } h(struct S *p, PoolPointer *PP) { … for (j = 0; j < 100000; j++) { tmp = poolalloc(PP); tmp2 = poolalloc(PP2); insert_tmp_to_list(tmp,p); …. q = least_useful_member(p) poolfree(q,PP); } … } h(struct S *p, PoolPointer *PP) { … for (j = 0; j < 100000; j++) { tmp = poolalloc(PP); insert_tmp_to_list(tmp,p); …. q = least_useful_member(p) poolfree(q,PP); } … } f() { PP = poolinit(struct S); … g(p, PP); … p->next->val = ….. pooldestoy(PP); } g(Struct S *p, PoolPointer *PP) { … create_list_10_nodes(p, PP); h(p, PP); free_everything_but_head(p, PP); … } Case 1 : No Reuse Case 2 : Self Reuse Case 3 : Cross Reuse Case 2 : Self Reuse

  17. Algorithm On all control flow paths interprocedurally For every poolfree(…, P) on the path, if before the subsequent pooldestroy(P) there is No poolalloc : P is Case 1 poolalloc only from the same pool : P is Case 2 poolalloc from a different pool : P is Case 3

  18. Implementation C++ LLVM Object code LLVM Linker C GCC Type Safety Array Safety Uninit. Variables Stack Safety Categorizing pools Safe Code with no checks Pool Allocation • Source Language Independence • Link – time Analysis => whole program analysis

  19. Evaluation • 17 applications in MediaBench and MIBench suite of bench marks • Studied how easy it is to port them • Results for 6 of them Total for 17 pgms

  20. Results : Heap Safety All 17 programs are proven heap safe! • 15 Programs had only Case 1 or Case 2 pools Memory safety without increase in memory consumption • 2 programs with Case 3 pools • Rasta : 5 Case-3 pools out of 14 • Epic : 1 Case-3 pool out of 14 • Further Analysis can convert some Case 3 pools to Case 2

  21. Results - II How do our previous techniques work ? • Stack Safety • 16 codes passed the compiler • 1 code needs restructuring to pass • Array Safety • Only 8 codes passed • Indirection vectors caused 5 codes to fail • Detected 4 bugs in benchmarks Array Bounds Checking remaining bottleneck for 100% static checking

  22. Related Work : Static Heap Safety Checking Linear and alias type systems : Vault • Severely restrict aliasing in programs • Require lot of annotations Region based Schemes : TofteTalpin[TOPLAS98], Aiken[PLDI98], Cyclone, RT-Java, Boyapati[PLDI03] • No deallocation within a region • Manual region annotations in most cases

  23. Conclusions • Result : Guarantee heap safety statically for “type safe” C. => New State of the Art : 100 % Static Checking for all C codes that • Are “type safe” • Use only affine array references

  24. URLs SAFECode StaticAnalysisForsafeExecutionof Code http://safecode.cs.uiuc.edu/ LLVM http://llvm.cs.uiuc.edu

  25. Pool Allocation Example h(struct S *p) { … for (j = 0; j < 100000; j++) { insert_tmp_to_list(tmp); …. q = least_useful_member(p) free(q); } … } poolfree(PP,q) f() { … g(p); … p->next->val = ….. } PP = poolinit(Struct S); tmp = malloc(sizeof(Struct S)) tmp = poolalloc(PP) pooldestroy(PP); g(Struct S *p) { … create_list_10_nodes(p); h(p); free_everything_but_head(p); … }

  26. Results : Heap Safety • All 17 are guaranteed heap safe • 15 programs had only Case 1 or Case 2 poolsMemory safety without increase in memory consumption • 2 programs with Case 3 pools • rasta -- 5 Case 3 out of 14 pools • epic -- 1 Case 3 out of 13 pools • Further analysis can convert some case 3 to case 2 pools

  27. Conclusions and Future Work • 100 % static checking for heap safety • Future Work • Improve the array bounds checker • Evaluate the actual increase, if any, in the few case 3 pools. • Detect illegal system calls/illegal sequences of system calls

More Related