making c less dangerous
play

Making C Less Dangerous Linux Security Summit August 27, 2018 - PowerPoint PPT Presentation

Making C Less Dangerous Linux Security Summit August 27, 2018 Vancouver, Canada Kees (Case) Cook keescook@chromium.org @kees_cook https://outflux.net/slides/2018/lss/danger.pdf Agenda Background Kernel Self Protection Project


  1. Making C Less Dangerous Linux Security Summit August 27, 2018 Vancouver, Canada Kees (“Case”) Cook keescook@chromium.org @kees_cook https://outflux.net/slides/2018/lss/danger.pdf

  2. Agenda ● Background – Kernel Self Protection Project – C as a fancy assembler ● Towards less dangerous C – Variable Length Arrays are bad and slow – Explicit switch case fall-through – Always-initialized automatic variables – Arithmetic overflow detection – Hope for bounds checking – Control Flow Integrity: forward edges – Control Flow Integrity: backward edges – Where are we now? – How you can help @Rob_Russell

  3. Kernel Self Protection Project https://kernsec.org/wiki/index.php/Kernel_Self_Protection_Project ● KSPP focuses on the kernel protecting the kernel from attack (e.g. ● refcount overflow) rather than the kernel protecting userspace from attack (e.g. execve brute force detection) but any area of related development is welcome Currently ~12 organizations and ~10 individuals working on about ● ~20 technologies Slow and steady ●

  4. C as a fancy assembler: almost machine code ● The kernel wants to be as fast and small as possible ● At the core, kernel wants to do very architecture-specific things for memory management, interrupt handling, scheduling, ... ● No C API for setting up page tables, switching to 64-bit mode …

  5. C as a fancy assembler: undefined behavior ● The C langauge comes with some operational baggage, and weak “standard” libraries – What are the contents of “uninitialized” variables? … whatever was in memory from before now! ● – v pointers have no type yet we can call typed functions through them? o i d … assembly doesn’t care: everything can be an address to call! ● – Why does m have no “max destination length” argument? e m c p y ( ) … just do what I say; memory areas are all the same! ● ● “With undefined behavior, anything is possible!” – https://raphlinus.github.io/programming/rust/2018/08/17/undefined-behavior.html

  6. Variable Length Arrays are bad ● Exhaust stack, linear overflow: write to things following it ● Jump over guard pages and write to things following it ● But easy to find with compiler flag: - W v l a stack 1 … stack 1 … size = 8192; size = 8192; … ... ... ... … char buf[size]; u8 array[size]; ... guard page … … stack 2 stack 2 strcpy(buf, src, size); array[big] = foo; … … … … ... ...

  7. Variable Length Arrays are slow ● While this seems conceptually sound: more instructions to change stack size, it seems like it would be hard to notice. ● But… 13% speed up measured during lib/bch.c VLA removal: https://git.kernel.org/linus/02361bc77888 (Ivan Djelic)

  8. Variable Length Arrays: stop it fixed-size array variable length array

  9. Switch case fall-through: did I mean it? ● CWE-484 “Omitted Break Statement in Switch” ● Semantic weakness in C (“switch” is just assembly test/jump...) ● Commit logs with “missing break statement”: 67 Did they mean to leave out “ b ” ?? r e a k ;

  10. Switch case fall-through: new “statement” ● Use - to add a new switch “statement” W i m p l i c i t - f a l l t h r o u g h – Actually a comment, but is parsed by compilers now, following the lead of static checkers ● Mark all non-breaks with a “fall through” comment, for example https://git.kernel.org/linus/4597b62f7a60 (Gustavo A. R. Silva)

  11. Always-initialized local variables: just do it ● CWE-200 “Information Exposure”, CWE-457 “Use of Uninitialized Variable” ● gcc - not upstream f i n i t - l o c a l - v a r s ● Clang - not upstream f s a n i t i z e = i n i t - l o c a l ● C O N F I G _ G C C _ P L U G I N _ . . . (for structs S T R U C T L E A K – with _ pointers) _ u s e r (when S T R U C T L E A K _ B Y R E F – passed into funcs) Soon, plugin to mimic – too - f i n i t - l o c a l - v a r s

  12. Always-initialized local variables: switch gotcha w a r n i n g : s t a t e m e n t w i l l n e v e r b e e x e c u t e d [ - W s w i t c h - u n r e a c h a b l e ]

  13. Arithmetic overflow detection: gcc? ● gcc’s - ( C ) f s a n i t i z e = s i g n e d - i n t e g e r - o v e r f l o w O N F I G _ U B S A N – Only signed. Fast: in the noise. Big: warnings grow kernel image by 6% (aborts grow it by 0.1%) ● But we can use explicit single-operation helpers. To quote Rasmus Villemoes:

  14. Arithmetic overflow detection: Clang :) ● Clang can do signed and unsigned instrumentation: - f s a n i t i z e = s i g n e d - i n t e g e r - o v e r f l o w - f s a n i t i z e = u n s i g n e d - i n t e g e r - o v e r f l o w

  15. Bounds checking: explicit checking is slow :( ● Explicit checks for linear overflows of SLAB objects, stack, etc checking: <~1% performance hit – c o p y _ { t o , f r o m } _ u s e r ( ) -family checking: ~2% performance hit – s t r c p y ( ) -family checking: ~1% performance hit – m e m c p y ( ) ● Can we get better APIs? doesn’t always NUL terminate, NUL pads entire destination – s t r n c p y ( ) – s reads source beyond max destination size t r l c p y ( ) … okay, I guess? – s t r s c p y ( ) – How about m that takes (and updates?) destination remaining size? e m c p y ( )

  16. Bounds checking: memory tagging :) ● Hardware memory tagging/coloring is so much faster! – SPARC Application Data Integrity (ADI) – ARM? – Intel? 128 byte alloc (tag 5): char *buf; ... ok: pointer tag matches data buf = kmalloc(128, …); ... /* 0x...5...beef0000 */ h c 128 byte alloc (tag 3): t a m s m i g a t buf[40] = 0xaa; r ... e t n o i p L : I A F data buf[130] = 0xbb; ...

  17. Control Flow Integrity: indirect calls ● With memory W^X, gaining execution control needs to change function pointers saved in heap or stack, where all type information was lost! heap: saved_actions[] … int action_launch(int idx) my_action { int my_action(struct thing *info) ... { e g int (*action)(struct thing *); stuff; d e d r a int rc; w and; r o f ... things; action = saved_actions[idx]; … backward edge rc = action(info); return 0; … } stack: } … return address ...

  18. CFI, forward edges: just call pointers :( Ignore function prototype … Normally just a call to a memory address:

  19. CFI, forward edges: enforce prototype :) Ignore function prototype … Clang - will check at runtime: f s a n i t i z e = c f i

  20. CFI, backward edges: two stacks ● Clang’s Safe Stack – Clang: - f s a n i t i z e = s a f e - s t a c k unsafe stack: ... regular stack: buffers by-referenced vars ... etc all local variables ... register spills return address safe stack: ... ... local variables safe variables return address register spills ... return address ...

  21. CFI, backward edges: shadow call stack ● Clang’s Shadow Call Stack – Clang: - f s a n i t i z e = s h a d o w - c a l l - s t a c k – Results in two stack registers: s and unspilled x p 1 8 sp stack: ... regular stack: all local variables register spills ... etc all local variables ... register spills return address x18 stack: ... ... local variables return address return address return address ... ...

  22. CFI, backward edges: hardware support ● Intel CET: hardware-based read-only shadow call stack – Implicit use of otherwise read-only shadow stack during c and r a l l e t instructions ● ARM v8.3a Pointer Authentication (“signed return address”) – New instructions: p and a a c i a s p u t i a s p – Clang and gcc: - m s i g n - r e t u r n - a d d r e s s

  23. Where is the Linux kernel now? ● Variable Length Arrays – Nearly eradicated from kernel (only handful in crypto remain) ● Explicit switch case fall-through – Steady progress on full markings (745 of 2311 remain) ● Always-initialized automatic variables – Various partial options, needs more compiler work ● Arithmetic overflow detection – Memory allocations now doing explicit overflow detection – Needs better kernel support for Clang and gcc ● Bounds checking – Crying about performance hits – Waiting (im)patiently for hardware support ● Control Flow Integrity: forward edges – Need Clang LTO support in kernel, but works on Android ● Control Flow Integrity: backward edges – Shadow Call Stack works on Android – Waiting (im)patiently for hardware support https://www.flickr.com/photos/wonderlane/5270711224

  24. Challenges in Kernel Security Development Cultural : Conservatism, Responsibility, Sacrifice, Patience Technical : Complexity, Innovation, Collaboration Resource : Dedicated Developers, Reviewers, Testers, Backporters

  25. Thoughts? Kees (“Case”) Cook keescook@chromium.org keescook@google.com kees@outflux.net https://outflux.net/slides/2018/lss/danger.pdf http://www.openwall.com/lists/kernel-hardening/ http://kernsec.org/wiki/index.php/Kernel_Self_Protection_Project

Recommend


More recommend