Efficient CFI Enforcement for C++ Dynamic Dispatch Dimitar Bounov , Rami Kici, Sorin Lerner UCSD
Why A:ack Dynamic Dispatch? • Valuable targets (e.g. browsers) 11M LOC ~30M LOC 7M LOC ?M LOC C/C++ C/C++ C/C++ C/C++
Why A:ack Dynamic Dispatch? • Valuable targets (e.g. browsers) • Prevalence of Dynamic Dispatch • 91.8 % of Indirect Calls in Chrome [ Tice ’14 ]
Why A:ack Dynamic Dispatch? • Valuable targets (e.g. browsers) • Prevalence of Dynamic Dispatch • Exploited in the wild
Prior Work and ContribuAon • Prior defenses vfGuard [Prakash’15], VTInt [Zhao’15], SafeDispatch [Jang’14], VTV [Tice’14] ... • Our contribu^on: novel VTable layouts • Lower overhead & no profiling
Example class A { A Afoo() virtual void foo(); } class B : public A { Bfoo() Afoo() B C virtual void foo(); Bbar() Cbaz() virtual void bar(); } class C : public A { D Dfoo() virtual void baz(); Bbar() } Dboo() class D : public B { virtual void foo(); virtual void boo(); }
C++ Memory Layout A Afoo() Bfoo() Afoo() B C Bbar() Cbaz() D Dfoo() Bbar() Dboo()
C++ Memory Layout Object Instance Virtual Table C A A Afoo() Afoo … … Bfoo Bbar Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Cbaz … … Dfoo D Dfoo() Bbar Bbar() Dboo() Dboo
Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Bbar Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Cbaz … … Dfoo D Dfoo() Bbar Bbar() Dboo() Dboo vptr = (*a) A* a = (A*) … fn_ptr = (*(vptr + 0)) a->foo() (*fn_ptr)(); Method Index
ExploiAng Dynamic Dispatch exec C A A Afoo() Afoo … … Bfoo Bbar Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Cbaz … … Dfoo D Dfoo() Bbar Bbar() Dboo() Dboo vptr = (*a) fn_ptr = (*(vptr + 0)) (*fn_ptr)();
ProtecAng Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Bbar Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Cbaz … … Dfoo D Dfoo() Bbar Bbar() Dboo() Dboo vptr = (*a) fn_ptr = (*(vptr + 0)) (*fn_ptr)();
ProtecAng Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Bbar Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Cbaz … … Dfoo D Dfoo() Bbar Bbar() Dboo() Dboo vptr = (*a) assert (vptr ∈ { A, B, C, D }) fn_ptr = (*(vptr + 0)) (*fn_ptr)();
ProtecAng Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Bbar Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Cbaz … … Dfoo D Dfoo() Bbar Bbar() Dboo() Dboo Read-Only vptr = (*a) assert (vptr ∈ { A, B, C, D }) fn_ptr = (*(vptr + 0)) (*fn_ptr)(); Inline Constant
ProtecAng Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Bbar Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Cbaz … … Dfoo D Dfoo() Bbar Bbar() Dboo() Dboo vptr = (*a) assert (vptr ∈ { A, B, C, D }) How to implement safety fn_ptr = (*(vptr + 0)) check efficiently? (*fn_ptr)();
ProtecAng Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Bbar Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Cbaz … … Dfoo D Dfoo() Bbar Bbar() Dboo() Dboo vptr = (*a) Non-regular values assert (vptr ∈ { 0x0, 0x8, 0x18, 0x28 }) fn_ptr = (*(vptr + 0)) Hard to test (*fn_ptr)();
ProtecAng Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Key idea 1: Bbar Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Cbaz Order and Pad VTables … … Dfoo D Dfoo() Bbar Bbar() Dboo() Dboo vptr = (*a) Non-regular values assert (vptr ∈ { 0x0, 0x8, 0x18, 0x28 }) fn_ptr = (*(vptr + 0)) Hard to test (*fn_ptr)();
Ordered Memory Layout Afoo C A A 24 B Afoo() … … Bfoo Bbar Bfoo() Afoo() B C 16 B Bbar() Cbaz() B D Dfoo … … Bbar D Dfoo() Dboo Bbar() 8 B Dboo() Afoo Cbaz 1. Traverse in pre-order: A, B, D, C 2. For each class layout vtable and pad
Ordered Memory Layout Afoo C A A 24 B Afoo() … … Bfoo Bbar Bfoo() Afoo() B C 16 B Bbar() Cbaz() B D Dfoo … … Bbar D Dfoo() Dboo Bbar() 8 B Dboo() Afoo vptr = (*a) Cbaz 1. Traverse in pre-order: A, B, D, C assert (vptr ∈ { A,B,C,D }) fn_ptr = (*(vptr + 0)) 2. For each class layout vtable and pad (*fn_ptr)();
Ordered Memory Layout Afoo C A A 24 B Afoo() … … Bfoo Bbar Bfoo() Afoo() B C 16 B Bbar() Cbaz() B D Dfoo … … Bbar D Dfoo() Dboo Bbar() 8 B Dboo() Afoo vptr = (*a) Cbaz assert (vptr ∈ { A,B,C,D }) fn_ptr = (*(vptr + 0)) (*fn_ptr)();
Ordered Memory Layout Afoo C A A 24 B Afoo() … … Bfoo Bbar Bfoo() Afoo() B C 16 B Bbar() Cbaz() B D Dfoo … … Bbar D Dfoo() Dboo Bbar() 8 B Dboo() Afoo vptr = (*a) Cbaz assert (vptr ∈ { 0x0, 0x20, 0x40, 0x60 }) Regular fn_ptr = (*(vptr + 0)) (*fn_ptr)(); Address Points
Ordered Memory Layout Afoo C A A 24 B Afoo() … … Bfoo Bbar Bfoo() Afoo() B C 16 B Bbar() Cbaz() B D Dfoo … … Bbar D Dfoo() Dboo Bbar() 8 B Dboo() Afoo vptr = (*a) Cbaz assert (vptr ∈ { 0x0, 0x20, 0x40, 0x60 }) fn_ptr = (*(vptr + 0)) (*fn_ptr)(); Efficient Check
Ordered Memory Layout Afoo C A A 24 B Afoo() … … Bfoo Bbar Bfoo() Afoo() B C 16 B Bbar() Cbaz() B D Dfoo … … Bbar D Dfoo() Dboo Bbar() 8 B Dboo() Afoo vptr = (*a) Cbaz assert (0x0 ≤ vptr ≤ 0x60 ∧ vptr % 0x20 = 0) fn_ptr = (*(vptr + 0)) (*fn_ptr)(); Efficient Check
Ordered Memory Layout Afoo C A A 24 B Afoo() … … Bfoo Bbar Bfoo() Afoo() B C 16 B Bbar() Cbaz() B D Dfoo … … Bbar D Dfoo() Dboo Bbar() 8 B Dboo() Afoo vptr = (*b) Cbaz assert (vptr ∈ { B, D }) fn_ptr = (*(vptr + 0)) (*fn_ptr)();
Ordered Memory Layout Afoo C A A 24 B Afoo() … … Bfoo Bbar Bfoo() Afoo() B C 16 B Bbar() Cbaz() B D Dfoo … … Bbar D Dfoo() Dboo Bbar() 8 B Dboo() Afoo vptr = (*b) Cbaz assert (vptr ∈ { 0x20, 0x40 }) fn_ptr = (*(vptr + 0)) (*fn_ptr)();
Ordered Memory Layout Afoo C A A 24 B Afoo() … … Bfoo Bbar Bfoo() Afoo() B C 16 B Bbar() Cbaz() B D Dfoo … … Bbar D Dfoo() Dboo Bbar() 8 B Dboo() Afoo vptr = (*b) Cbaz assert (0x20 ≤ vptr ≤ 0x40 ∧ vptr % 0x20 = 0) fn_ptr = (*(vptr + 0)) (*fn_ptr)();
Ordered Memory Layout Afoo C A A 24 B Afoo() … … Bfoo Bbar Bfoo() Afoo() B C 16 B Bbar() Cbaz() B D Dfoo … … Bbar D Dfoo() Dboo Bbar() 8 B Dboo() Afoo Cbaz Wasteful Extra Padding
Ordered Memory Layout Afoo C A A 24 B Afoo() … … Bfoo Bbar Key idea 2: Bfoo() Afoo() B C 16 B Bbar() Cbaz() B D Dfoo Interleave VTables … … Bbar D Dfoo() Dboo Bbar() 16 B Dboo() Afoo Cbaz Wasteful Extra Padding
Interleaved Memory Layout C A A Afoo() … … Bfoo() Afoo() B C Bbar() Cbaz() B D … … D Dfoo() Bbar() Dboo() 1. Traverse in pre-order: A, B, D, C 2. Layout each method
Interleaved Memory Layout C A A Afoo() Afoo … … Bfoo Dfoo Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Bbar … … Bbar D Dfoo() Dboo Bbar() Dboo() Cbaz 1. Traverse in pre-order: A, B, D, C 2. Layout each method
Interleaved Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Dfoo Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Bbar … … Bbar D Dfoo() Dboo Bbar() Dboo() Cbaz vptr = (*a) 1. Traverse in pre-order: A, B, D, C assert (vptr ∈ { A,B,C,D }) fn_ptr = (*(vptr + 0)) 2. Layout each method (*fn_ptr)();
Interleaved Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Dfoo Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Bbar … … Bbar D Dfoo() Dboo Bbar() Dboo() Cbaz vptr = (*a) assert (vptr ∈ { A,B,C,D }) fn_ptr = (*(vptr + 0)) (*fn_ptr)();
Interleaved Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Dfoo Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Bbar … … Bbar D Dfoo() Dboo Bbar() Dboo() Cbaz vptr = (*a) assert (vptr ∈ { 0x0, 0x8, 0x10, 0x18 }) Address Points fn_ptr = (*(vptr + 0)) (*fn_ptr)(); Consecu^ve Addrs.
Interleaved Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Dfoo Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Bbar … … Bbar D Dfoo() Dboo Bbar() Dboo() Cbaz vptr = (*a) assert (0x0 ≤ vptr ≤ 0x18 ∧ vptr % 0x8 = 0) fn_ptr = (*(vptr + 0)) Same Check (*fn_ptr)(); Different range & alignment
Interleaved Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Dfoo Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Bbar … … Bbar D Dfoo() Dboo Bbar() Dboo() Cbaz vptr = (*b) assert (0x0 ≤ vptr ≤ 0x18 ∧ vptr % 0x8 = 0) fn_ptr = (*(vptr + 0)) (*fn_ptr)();
Interleaved Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Dfoo Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Bbar … … Bbar D Dfoo() Dboo Bbar() Dboo() Cbaz vptr = (*b) assert (0x8 ≤ vptr ≤ 0x10 ∧ vptr % 0x8 = 0) fn_ptr = (*(vptr + 0)) (*fn_ptr)();
Interleaved Dynamic Dispatch C A A Afoo() Afoo … … Bfoo Dfoo Bfoo() Afoo() B C Bbar() Cbaz() Afoo B D Bbar … … Bbar D Dfoo() Dboo Bbar() Dboo() Cbaz vptr = (*b) assert (0x8 ≤ vptr ≤ 0x10 ∧ vptr % 0x8 = 0) fn_ptr = (*(vptr + 0)) (*fn_ptr)();
Recommend
More recommend