Skip to content

SoftBoundCETS shadow stack metadata propagation is wrong when llvm optimizations remove arguments #8

@jayPLim

Description

@jayPLim

I found a bug with softboundcets when llvm optimization removes function arguments. Softboundcets is retrieving function argument base and bound by using the position of the function argument as a reference, but when llvm optimization removes function arguments, softboundcets base and bound retrieving function does not change the index of the function argument.

Here is an example: In unoptimized code:

define internal void @foo(i32* %pwidth, i32* %ptype) #1 {
entry:
  %0 = call i8* @__softboundcets_load_base_shadow_stack(i32 2)
  %1 = call i8* @__softboundcets_load_bound_shadow_stack(i32 2)
  %2 = call i64 @__softboundcets_load_key_shadow_stack(i32 2)
  %3 = call i8* @__softboundcets_load_lock_shadow_stack(i32 2)
  %4 = call i8* @__softboundcets_load_base_shadow_stack(i32 1)
  %5 = call i8* @__softboundcets_load_bound_shadow_stack(i32 1)
  %6 = call i64 @__softboundcets_load_key_shadow_stack(i32 1)
  %7 = call i8* @__softboundcets_load_lock_shadow_stack(i32 1)
  %8 = call i8* @__softboundcets_get_global_lock()

...

  %bitcast8 = bitcast i32* %ptype to i8*
  %cmp.i = icmp ult i8* %bitcast8, %0, !tv.orig !1
  %add.ptr.i1 = getelementptr inbounds i8, i8* %bitcast8, i64 ptrtoint (i32* getelementptr (i32, i32* null, i32 1) to i64), !tv.orig !1
  %cmp1.i2 = icmp ugt i8* %add.ptr.i1, %1, !tv.orig !1
  %or.cond.i = or i1 %cmp.i, %cmp1.i2
  br i1 %or.cond.i, label %if.then.i3, label %__softboundcets_spatial_store_dereference_check.exit, !tv.orig !1

And in optimized code (ran with O1)

define internal fastcc void @foo(i32* %ptype) unnamed_addr #1 {
entry:
  %0 = tail call fastcc i8* @__softboundcets_load_base_shadow_stack(i32 2)
  %1 = tail call fastcc i8* @__softboundcets_load_bound_shadow_stack(i32 2)
  %2 = tail call fastcc i64 @__softboundcets_load_key_shadow_stack(i32 2)
  %3 = tail call fastcc i8* @__softboundcets_load_lock_shadow_stack(i32 2)
  %4 = tail call fastcc i8* @__softboundcets_load_base_shadow_stack(i32 1)
  %5 = tail call fastcc i8* @__softboundcets_load_bound_shadow_stack(i32 1)
  %6 = tail call fastcc i64 @__softboundcets_load_key_shadow_stack(i32 1)
  %7 = tail call fastcc i8* @__softboundcets_load_lock_shadow_stack(i32 1)

...

  %bitcast8 = bitcast i32* %ptype to i8*
  %cmp.i = icmp ult i8* %bitcast8, %0, !tv.orig !1
  %add.ptr.i11 = getelementptr inbounds i32, i32* %ptype, i64 1
  %add.ptr.i1 = bitcast i32* %add.ptr.i11 to i8*
  %cmp1.i2 = icmp ugt i8* %add.ptr.i1, %1, !tv.orig !1
  %or.cond.i = or i1 %cmp.i, %cmp1.i2
  br i1 %or.cond.i, label %if.then.i3, label %__softboundcets_spatial_store_dereference_check.exit, !tv.orig !1

In both cases softboundcets checks the base and bound of ptype by using the 2nd function argument's base and bound. However, in optimized code, there is no second argument. ptype is the first argument.

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions