20140531 serebryany lecture01_fantastic_cpp_bugs
TRANSCRIPT
![Page 1: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/1.jpg)
[2/2] Find scary C++ bugs before they find you
Konstantin Serebryany, GoogleMay 2014 @compsciclub.ru
[1/2] Fantastic C++ Bugs and Where to Find Them
![Page 2: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/2.jpg)
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
![Page 3: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/3.jpg)
Why bugs are scary
● Increased development cost● Increased CPU/RAM consumption● Decreased user satisfaction● May cost money or even lives
● Security!
![Page 4: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/4.jpg)
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
![Page 5: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/5.jpg)
Memory access bugs
![Page 6: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/6.jpg)
Typical Address Space LayoutStack
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
...
![Page 7: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/7.jpg)
Virtual Function Table (VPTR)class Foo { public: virtual void f1(); virtual void f2(); private: int data1; int data2;};
VPTR
data1
data2
f1
f2
![Page 8: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/8.jpg)
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]…
![Page 9: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/9.jpg)
Buffer overflow (stack)
int foo(int i) {int ar[10];
… ar[i] …
● May access the return address and call arbitrary code
![Page 10: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/10.jpg)
Buffer overflow (stack)void bad() { std::cout << "I am BAD\n";}int main() { long a, b, ar[10]; std::cin >> a >> b; ar[a] = b;}
![Page 11: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/11.jpg)
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
![Page 12: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/12.jpg)
Buffer overflow (heap)void good() {
cout << "I am good\n";
}
void bad() {
cout << "I am BAD\n";
}
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_();
}
![Page 13: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/13.jpg)
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}
![Page 14: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/14.jpg)
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;
![Page 15: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/15.jpg)
Use-after-free: privilege escalationstruct 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 Granted\n";}
![Page 16: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/16.jpg)
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 = ...
![Page 17: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/17.jpg)
stack-u-a-r: VPTR replacementstruct Foo {
virtual void good() {
cout << "good\n";
}};
struct Bar {
virtual void bad() {
cout << "BAD\n";
}};
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();}
![Page 18: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/18.jpg)
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
![Page 19: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/19.jpg)
[De]allocation bugs
● Double-free● Invalid free● “new []” vs “delete”
![Page 20: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/20.jpg)
Memory leaks, other resource leaks
● Excessive memory consumption● [D]DOS attacks
void foo() { int *x = new int [10]; if (...) return; delete [] x;}
![Page 21: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/21.jpg)
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();}
![Page 22: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/22.jpg)
Use after destructionstruct 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);
}
![Page 23: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/23.jpg)
Why SEGV?
● NULL dereference ● Buffer overflow● Use-after-free● Read from uninitialized pointer● Stack overflow
![Page 24: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/24.jpg)
Threading bugs
![Page 25: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/25.jpg)
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;
![Page 26: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/26.jpg)
Data Racesint 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)
![Page 27: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/27.jpg)
Race on a bitfieldstruct Foo { int a : 20; int b : 12;};
Foo foo;
void Thread1() { foo.a++;}void Thread2() { foo.b++;}
![Page 28: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/28.jpg)
Race During Destructionstd::set<int> s; // Global variable
void Thread() { for (int i = 0; i < 1000000; i++) s.insert(rand());}
int main() { new std::thread(Thread); }
![Page 29: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/29.jpg)
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
![Page 30: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/30.jpg)
Race on VPTRstruct A {
A() : done_(false) {}
virtual void F() { printf("A::F\n"); }
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_; };
![Page 31: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/31.jpg)
Race on VPTR (cont)class B : public A {
public:
virtual void F() { printf("B::F\n"); }
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();
}
![Page 32: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/32.jpg)
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_;
};
![Page 33: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/33.jpg)
Atomicity violation (cont)ProtectedVector v;
void Thread1() { if (!v.empty()) v.pop_back();}
void Thread2() { if (!v.empty()) v.pop_back();}
![Page 34: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/34.jpg)
Threading bugs cause memory bugs
● Racey use-after-free
● Race on reference counter○ double-free○ leak
![Page 35: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/35.jpg)
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);}
![Page 36: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/36.jpg)
Other Undefined Behavior
![Page 37: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/37.jpg)
Init Order Fiasco
// in a.ccint foo();int X = foo();
// in b.ccint Y = X;int foo() {return 42;
}
![Page 38: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/38.jpg)
ODR (one definition rule) Violation
// in a.cc/a.soint X;
// in b.cc/b.sodouble X;
![Page 39: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/39.jpg)
Lack of Sequence Pointint 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!)
![Page 40: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/40.jpg)
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; }}
![Page 41: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/41.jpg)
Some more...
● Shift by oversized or negative value● Missing return statement● Infinite loops● ...
![Page 42: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/42.jpg)
Quiz: find all bugs#include <thread> // C++11int main() { int *a = new int[4]; int *b = new int[4]; std::thread t{[&](){b++;}}; delete a; t.detach(); return *a + (*++b) + b[3];}
![Page 43: 20140531 serebryany lecture01_fantastic_cpp_bugs](https://reader033.vdocuments.net/reader033/viewer/2022051413/555e1516d8b42a99188b53ab/html5/thumbnails/43.jpg)
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