Efficient CFI Enforcement for C++ Dynamic Dispatch Dimitar Bounov , - - PowerPoint PPT Presentation
Efficient CFI Enforcement for C++ Dynamic Dispatch Dimitar Bounov , - - PowerPoint PPT Presentation
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
Why A:ack Dynamic Dispatch?
- Valuable targets (e.g. browsers)
11M LOC C/C++ 7M LOC C/C++ ?M LOC C/C++ ~30M LOC 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 { virtual void foo(); } class B : public A { virtual void foo(); virtual void bar(); } class C : public A { virtual void baz(); } class D : public B { virtual void foo(); virtual void boo(); }
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo()
C++ Memory Layout
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo()
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A … Afoo Bfoo
C++ Memory Layout
B … Bbar C … Afoo Cbaz D … Dfoo Bbar Dboo
Virtual Table Object Instance
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A … Afoo Bfoo
Dynamic Dispatch
B … Bbar C … Afoo Cbaz D … Dfoo Bbar Dboo
A* a = (A*) … a->foo() vptr = (*a) fn_ptr = (*(vptr + 0)) (*fn_ptr)();
Method Index
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A … Afoo Bfoo
ExploiAng Dynamic Dispatch
B … Bbar C … Afoo Cbaz D … Dfoo Bbar Dboo
fn_ptr = (*(vptr + 0)) (*fn_ptr)();
exec
vptr = (*a)
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A … Afoo Bfoo
ProtecAng Dynamic Dispatch
B … Bbar C … Afoo Cbaz D … Dfoo Bbar Dboo
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); vptr = (*a)
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A … Afoo Bfoo
ProtecAng Dynamic Dispatch
B … Bbar C … Afoo Cbaz D … Dfoo Bbar Dboo
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (vptr ∈ { A, B, C, D }) vptr = (*a)
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A … Afoo Bfoo
ProtecAng Dynamic Dispatch
B … Bbar C … Afoo Cbaz D … Dfoo Bbar Dboo
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (vptr ∈ { A, B, C, D }) vptr = (*a)
Inline Constant Read-Only
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A … Afoo Bfoo
ProtecAng Dynamic Dispatch
B … Bbar C … Afoo Cbaz D … Dfoo Bbar Dboo
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (vptr ∈ { A, B, C, D }) vptr = (*a)
How to implement safety check efficiently?
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A … Afoo Bfoo
ProtecAng Dynamic Dispatch
B … Bbar C … Afoo Cbaz D … Dfoo Bbar Dboo
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (vptr ∈ { 0x0, 0x8, 0x18, 0x28 }) vptr = (*a)
Non-regular values Hard to test
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A … Afoo Bfoo
ProtecAng Dynamic Dispatch
B … Bbar C … Afoo Cbaz D … Dfoo Bbar Dboo
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); vptr = (*a) assert (vptr ∈ { 0x0, 0x8, 0x18, 0x28 })
Non-regular values Hard to test
Key idea 1: Order and Pad VTables
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Ordered Memory Layout
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo 24 B 16 B 8 B
- 1. Traverse in pre-order: A, B, D, C
- 2. For each class layout vtable and pad
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Ordered Memory Layout
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo 24 B 16 B 8 B
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (vptr ∈ { A,B,C,D }) vptr = (*a)
- 1. Traverse in pre-order: A, B, D, C
- 2. For each class layout vtable and pad
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Ordered Memory Layout
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo 24 B 16 B 8 B
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (vptr ∈ { A,B,C,D }) vptr = (*a)
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Ordered Memory Layout
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo 24 B 16 B 8 B
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (vptr ∈ { 0x0, 0x20, 0x40, 0x60 }) vptr = (*a)
Regular Address Points
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Ordered Memory Layout
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo 24 B 16 B 8 B
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (vptr ∈ { 0x0, 0x20, 0x40, 0x60 }) vptr = (*a)
Efficient Check
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Ordered Memory Layout
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo 24 B 16 B 8 B
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (0x0 ≤ vptr ≤ 0x60 ∧ vptr % 0x20 = 0) vptr = (*a)
Efficient Check
A …
Ordered Memory Layout
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo 24 B 16 B 8 B
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (vptr ∈ { B, D }) vptr = (*b) A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo()
A …
Ordered Memory Layout
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo 24 B 16 B 8 B
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (vptr ∈ { 0x20, 0x40 }) vptr = (*b) A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo()
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Ordered Memory Layout
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo 24 B 16 B 8 B
fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (0x20 ≤ vptr ≤ 0x40 ∧ vptr % 0x20 = 0) vptr = (*b)
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Ordered Memory Layout
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo 24 B 16 B 8 B
Wasteful Extra Padding
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Ordered Memory Layout
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo 24 B
Wasteful Extra Padding
16 B 16 B
Key idea 2: Interleave VTables
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Interleaved Memory Layout
B … C … D …
- 1. Traverse in pre-order: A, B, D, C
- 2. Layout each method
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Interleaved Memory Layout
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo
- 1. Traverse in pre-order: A, B, D, C
- 2. Layout each method
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Interleaved Dynamic Dispatch
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo
- 1. Traverse in pre-order: A, B, D, C
- 2. Layout each method
vptr = (*a) fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (vptr ∈ { A,B,C,D })
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Interleaved Dynamic Dispatch
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo
vptr = (*a) fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (vptr ∈ { A,B,C,D })
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Interleaved Dynamic Dispatch
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo
vptr = (*a) fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (vptr ∈ { 0x0, 0x8, 0x10, 0x18 })
Address Points Consecu^ve Addrs.
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Interleaved Dynamic Dispatch
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo
vptr = (*a) fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (0x0 ≤ vptr ≤ 0x18 ∧ vptr % 0x8 = 0)
Same Check Different range & alignment
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Interleaved Dynamic Dispatch
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo
vptr = (*b) fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (0x0 ≤ vptr ≤ 0x18 ∧ vptr % 0x8 = 0)
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Interleaved Dynamic Dispatch
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo
vptr = (*b) fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (0x8 ≤ vptr ≤ 0x10 ∧ vptr % 0x8 = 0)
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Interleaved Dynamic Dispatch
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo
vptr = (*b) fn_ptr = (*(vptr + 0)) (*fn_ptr)(); assert (0x8 ≤ vptr ≤ 0x10 ∧ vptr % 0x8 = 0)
A B C D
Afoo() Afoo() Cbaz() Bfoo() Bbar() Dfoo() Bbar() Dboo() A …
Interleaved Dynamic Dispatch
B … C … D … Afoo Bfoo Bbar Afoo Cbaz Dfoo Bbar Dboo
vptr = (*b) fn_ptr = (*(vptr + 0x18)) (*fn_ptr)(); assert (0x8 ≤ vptr ≤ 0x10 ∧ vptr % 0x8 = 0) Same index in all vtables
Where are the Vtables?
Afoo Bfoo Dfoo Afoo A B C D Bbar Dboo Cbaz Bbar
1 1 2 2 3 3
Bar method index same VTable Dynamic Dispatch s^ll works!
ImplementaAon
- Implemented technique in LLVM
- Handled all cases of C++ inheritance
- Algorithms with proofs of correctness
EvaluaAon
- Evaluated on SPEC 2006/Chrome
- Compared to state-of-the-art industry CFI
- One (benign) CFI viola^on in SPEC 2006
Results: RunAme Overhead
1% IVT vs. 2% LLVM-VCFI Interleaved VTables Ordered VTables Industry State-of-The-Art
Results: Size Overhead
IVT 1.7% Size Overhead
Future Work
- Dynamic linking/loading
- Handle C++ member pointers
- Hardware assisted range checks
Summary
- New approach based on VTable layouts
- Efficient range checks for dynamic
dispatch
- hyps://github.com/UCSD-PL/ivt
THANK YOU!
Backup
Dynamic Linking
C B A Afoo()
Afoo() Bfoo() Bbar()
D Dfoo()
Dbar()
lib.so
D … A … B … C … Afoo Bfoo Bbar Afoo 8 B 8 B Dfoo Dbar
main lib.so main D
- >foo()
Only 0.012% of dynamic Firefox virtual calls!
A
Dynamic Fault Handlers
D … A … B … C … Afoo Bfoo Bbar Afoo 8 B 8 B Dfoo Dbar
vptr = (*a) if (0x00 ≤ vptr ≤ 0x20 ∧ vptr % 0x10 = 0) goto S; fn_ptr = (*(vptr + 0)) (fn_ptr*)(); S : assert(false);
Dynamic Fault Handlers
D … A … B … C … Afoo Bfoo Bbar Afoo 8 B 8 B Dfoo Dbar
vptr = (*a) if (0x00 ≤ vptr ≤ 0x20 ∧ vptr % 0x10 = 0) goto S; fn_ptr = (*(vptr + 0)) (fn_ptr*)(); S : L = link_unit (vptr) // e.g. dladddr() if vptr_safe(L, vptr, class A) goto S assert(false);
MulAple Inheritance
A Afoo()
Abar()
C
Cfoo() Cbar() Cbaz()
A-in-C … B-in-C … Cfoo CBaz Cbar
B Bbaz() A subobject B subobject
Object Instance Vtable (Group)
MulAple Inheritance
A Afoo()
Abar()
C
Cfoo() Cbar() Cbaz()
A-in-C … B-in-C … Cfoo CBaz Cbar
B Bbaz()
MulAple Inheritance
A Afoo()
Abar()
A-in-C … B-in-C … Cfoo CBaz Cbar
B Bbaz() C
Cfoo() Cbar() Cbaz()
MulAple Inheritance
A Afoo()
Abar()
A-in-C … B-in-C … Cfoo CBaz Cbar
B Bbaz() A-in-C
Cfoo() Cbar()
B-in-C Cbaz()