20140531 serebryany lecture01_fantastic_cpp_bugs
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

20140531 serebryany lecture01_fantastic_cpp_bugs

on

  • 413 views

 

Statistics

Views

Total Views
413
Views on SlideShare
167
Embed Views
246

Actions

Likes
0
Downloads
1
Comments
0

3 Embeds 246

http://compsciclub.ru 173
http://logic.pdmi.ras.ru 71
http://www.compsciclub.ru 2

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

20140531 serebryany lecture01_fantastic_cpp_bugs Presentation Transcript

  • 1. [2/2] Find scary C++ bugs before they find you Konstantin Serebryany, Google May 2014 @compsciclub.ru [1/2] Fantastic C++ Bugs and Where to Find Them
  • 2. Agenda ● How bad the bugs are? ● Most common C++ bugs ○ Memory access bugs ○ Threading bugs ○ Other undefined behavior bugs ● Quiz ● Lecture 2/2: tools that find bugs
  • 3. Why bugs are scary ● Increased development cost ● Increased CPU/RAM consumption ● Decreased user satisfaction ● May cost money or even lives ● Security!
  • 4. Undefined Behavior (UB) ● UB != undefined result ● UB: the program may misbehave depending on compiler, hardware, system load, current date, outside temperature, etc ● UB: the program may turn hostile to the host system or launch nuclear missiles
  • 5. Memory access bugs
  • 6. Typical Address Space Layout Stack Heap Globals Constants (RO) Code NULL Page Malloc Header User Chunk Malloc Header User Chunk ... Return Address Local Variables Return Address Local Variables ... Global Variable Global Variable ...
  • 7. Virtual Function Table (VPTR) class Foo { public: virtual void f1(); virtual void f2(); private: int data1; int data2; }; VPTR data1 data2 f1 f2
  • 8. Buffer overflow (global, heap, stack) ● Access invalid memory ○ SEGV (Good!!) ● Access some other object in memory ○ Read garbage or corrupt data ■ Subvert further execution ○ Leak private data or memory layout ○ Overwrite function pointers or VPTR int ar[10]; … ar[i]…
  • 9. Buffer overflow (stack) int foo(int i) { int ar[10]; … ar[i] … ● May access the return address and call arbitrary code
  • 10. Buffer overflow (stack) void bad() { std::cout << "I am BADn"; } int main() { long a, b, ar[10]; std::cin >> a >> b; ar[a] = b; }
  • 11. Buffer overflow (heap) int foo(int i) { int *ar = new int [10]; … ar[i] ... ● Access malloc header ○ Crash later in new/delete ○ Deallocate wrong amount of memory
  • 12. Buffer overflow (heap) void good() { cout << "I am goodn"; } void bad() { cout << "I am BADn"; } typedef void (*F)(void); struct Object { Object(F f) : f_(f) {} F f_; }; int main() { long a, b; long *x = new long[1]; Object *o = new Object(good); cin >> a >> b; x[a] = b; o->f_(); }
  • 13. Erroneous type cast struct Base { int data; }; struct Derived : Base { int more_data; }; Base b; int main() { Derived *d = (Derived*)&b; d->more_data = 0; // OOPS }
  • 14. Use-after-free (heap) ● Even worse than heap-buffer-overflow because touches arbitrary part of heap int *x = new int[10]; delete [] x; x[5] = 0;
  • 15. Use-after-free: privilege escalation struct Thing { bool has_access; }; int main() { Thing *security_check = new Thing; security_check->has_access = false; delete security_check; int *x = new int(42); if (security_check->has_access) // OOPS cout << "Access Grantedn"; }
  • 16. Use-after-return (stack) ● Relatively rare, but combines the worst of heap-use-after-free and stack-buffer-overflow int *x; void foo() { int local; x = &local; } *x = ...
  • 17. stack-u-a-r: VPTR replacement struct Foo { virtual void good() { cout << "goodn"; }}; struct Bar { virtual void bad() { cout << "BADn"; }}; long **p; void leak() { long *local[10]; p = &local[0]; }; void use(Foo *foo) { *p[5] -= 0x20; foo->good(); } void oops() { Foo foo; use(&foo); } int main() {leak(); oops();}
  • 18. Use-after-scope (stack) int *p; if (...) { int a; p = &a; } if (...) { int b[100]; *p = … // oops } ● Behavior depends on compiler version, flags, function size, etc
  • 19. [De]allocation bugs ● Double-free ● Invalid free ● “new []” vs “delete”
  • 20. Memory leaks, other resource leaks ● Excessive memory consumption ● [D]DOS attacks void foo() { int *x = new int [10]; if (...) return; delete [] x; }
  • 21. Use of uninitialized memory ● Reading garbage from heap or stack ● Results change from run-to run ● Values could be controlled by attacker void foo() { int x[10]; if (x[5]) Something(); }
  • 22. Use after destruction struct Foo { void set(string *s) { s_ = s; } ~Foo () { cout << *s_ << endl; } string *s_; }; struct Bar { Foo foo; string s; }; int main() { Bar b; b.s = "hello world"; b.foo.set(&b.s); }
  • 23. Why SEGV? ● NULL dereference ● Buffer overflow ● Use-after-free ● Read from uninitialized pointer ● Stack overflow
  • 24. Threading bugs
  • 25. Mutex Deadlock void Thread1() { mu1.lock() mu2.lock() mu2.unlock() mu1.unlock() } void Thread2() { mu2.lock() mu1.lock() mu2.unlock() mu1.unlock() } std::mutex mu1, mu2;
  • 26. Data Races int var; void Thread1() { var--; } void Thread2() { var++; } ● Two accesses to the same memory location ● At least one is a store ● No happens-before relation (no explicit synchronization)
  • 27. Race on a bitfield struct Foo { int a : 20; int b : 12; }; Foo foo; void Thread1() { foo.a++; } void Thread2() { foo.b++; }
  • 28. Race During Destruction std::set<int> s; // Global variable void Thread() { for (int i = 0; i < 1000000; i++) s.insert(rand()); } int main() { new std::thread(Thread); }
  • 29. struct A { virtual ... }; struct B : public A { virtual ... }; B b; ● ‘A’ is constructed ○ VPTR = A::VPTR ● ‘B’ is constructed ○ VPTR = B::VPTR ● ‘B’ is destroyed ○ VPTR = B::VPTR ● ‘A’ is destroyed ○ VPTR = A::VPTR VPTR: construction order
  • 30. Race on VPTR struct A { A() : done_(false) {} virtual void F() { printf("A::Fn"); } void Done() { std::unique_lock<std::mutex> lk(m_); done_ = true; cv_.notify_one(); } virtual ~A() { // Wait for Done() std::unique_lock<std::mutex> lk(m_); cv_.wait(lk, [this] {return done_;}); } private: std::mutex m_; std::condition_variable cv_; bool done_; };
  • 31. Race on VPTR (cont) class B : public A { public: virtual void F() { printf("B::Fn"); } virtual ~B() {} }; int main() { A *a = new B; std::thread t1([a] {a->F(); a->Done();}); std::thread t2([a] {delete a;}); t1.join(); t2.join(); }
  • 32. Atomicity violationstruct ProtectedVector { bool empty() { // Protected by m_ std::lock_guard<std::mutex> g(m_); return v_.empty(); } void pop_back() { // Protected by m_ std::lock_guard<std::mutex> g(m_); v_.pop_back(); } ... private: std::mutex m_; std::vector<int> v_; };
  • 33. Atomicity violation (cont) ProtectedVector v; void Thread1() { if (!v.empty()) v.pop_back(); } void Thread2() { if (!v.empty()) v.pop_back(); }
  • 34. Threading bugs cause memory bugs ● Racey use-after-free ● Race on reference counter ○ double-free ○ leak
  • 35. Async-signal safety ● No real threading ● But similar to races ● malloc is not safe in signal handlers! struct Node { Node *next, *prev; }; Node *head; void PushFront() { Node *n = new Node; n->next = head; n->prev = NULL; if(head) head->prev = n; head = n; } void SignalHandler(...) { assert(!head || !head->prev); }
  • 36. Other Undefined Behavior
  • 37. Init Order Fiasco // in a.cc int foo(); int X = foo(); // in b.cc int Y = X; int foo() { return 42; }
  • 38. ODR (one definition rule) Violation // in a.cc/a.so int X; // in b.cc/b.so double X;
  • 39. Lack of Sequence Point int i = 0; i = ++i + i++; // What is i? std::map<int> m; m[10] = m.size(); // What is m[10]? ● Clang and GCC will give different answers (GOOD!)
  • 40. Integer Overflow Remember: UB != undefined result void f (int *array) { int val = 0x03020100; for(int i = 0; i < 64; i++) { array[i] = val; // Overflow when i==63 val += 0x04040404; } }
  • 41. Some more... ● Shift by oversized or negative value ● Missing return statement ● Infinite loops ● ...
  • 42. Quiz: find all bugs #include <thread> // C++11 int main() { int *a = new int[4]; int *b = new int[4]; std::thread t{[&](){b++;}}; delete a; t.detach(); return *a + (*++b) + b[3]; }
  • 43. Links ● https://en.wikipedia.org/wiki/Buffer_overflow ● https://en.wikipedia.org/wiki/Dangling_pointer ● http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html ● https://code.google.com/p/thread-sanitizer/wiki/AboutRaces ● (*) http://people.freebsd.org/~lstewart/articles/cpumemory.pdf ● (*) https://www.usenix.org/legacy/event/hotpar11/tech/final_files/Boehm.pdf