20140531 serebryany lecture01_fantastic_cpp_bugs

43
[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

Transcript of 20140531 serebryany lecture01_fantastic_cpp_bugs

Page 1: 20140531 serebryany lecture01_fantastic_cpp_bugs

[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

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

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

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

Memory access bugs

Page 6: 20140531 serebryany lecture01_fantastic_cpp_bugs

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

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

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

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

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

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

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

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

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

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

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

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

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

[De]allocation bugs

● Double-free● Invalid free● “new []” vs “delete”

Page 20: 20140531 serebryany lecture01_fantastic_cpp_bugs

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

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

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

Why SEGV?

● NULL dereference ● Buffer overflow● Use-after-free● Read from uninitialized pointer● Stack overflow

Page 24: 20140531 serebryany lecture01_fantastic_cpp_bugs

Threading bugs

Page 25: 20140531 serebryany lecture01_fantastic_cpp_bugs

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

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

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

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

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

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

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

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

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

Threading bugs cause memory bugs

● Racey use-after-free

● Race on reference counter○ double-free○ leak

Page 35: 20140531 serebryany lecture01_fantastic_cpp_bugs

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

Other Undefined Behavior

Page 37: 20140531 serebryany lecture01_fantastic_cpp_bugs

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

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

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

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

Some more...

● Shift by oversized or negative value● Missing return statement● Infinite loops● ...

Page 42: 20140531 serebryany lecture01_fantastic_cpp_bugs

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

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