r297389 - Retry: [ubsan] Detect UB loads from bitfields

Vedant Kumar via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 9 08:06:28 PST 2017


Author: vedantk
Date: Thu Mar  9 10:06:27 2017
New Revision: 297389

URL: http://llvm.org/viewvc/llvm-project?rev=297389&view=rev
Log:
Retry: [ubsan] Detect UB loads from bitfields

It's possible to load out-of-range values from bitfields backed by a
boolean or an enum. Check for UB loads from bitfields.

This is the motivating example:

  struct S {
    BOOL b : 1; // Signed ObjC BOOL.
  };

  S s;
  s.b = 1; // This is actually stored as -1.
  if (s.b == 1) // Evaluates to false, -1 != 1.
    ...

Changes since the original commit:

- Single-bit bools are a special case (see CGF::EmitFromMemory), and we
  can't avoid dealing with them when loading from a bitfield. Don't try to
  insert a check in this case.

Differential Revision: https://reviews.llvm.org/D30423

Added:
    cfe/trunk/test/CodeGenCXX/ubsan-bitfields.cpp
Modified:
    cfe/trunk/lib/CodeGen/CGAtomic.cpp
    cfe/trunk/lib/CodeGen/CGExpr.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h
    cfe/trunk/test/CodeGenObjC/ubsan-bool.m

Modified: cfe/trunk/lib/CodeGen/CGAtomic.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGAtomic.cpp?rev=297389&r1=297388&r2=297389&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGAtomic.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGAtomic.cpp Thu Mar  9 10:06:27 2017
@@ -1181,7 +1181,7 @@ RValue AtomicInfo::convertAtomicTempToRV
   if (LVal.isBitField())
     return CGF.EmitLoadOfBitfieldLValue(
         LValue::MakeBitfield(addr, LVal.getBitFieldInfo(), LVal.getType(),
-                             LVal.getAlignmentSource()));
+                             LVal.getAlignmentSource()), loc);
   if (LVal.isVectorElt())
     return CGF.EmitLoadOfLValue(
         LValue::MakeVectorElt(addr, LVal.getVectorIdx(), LVal.getType(),

Modified: cfe/trunk/lib/CodeGen/CGExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExpr.cpp?rev=297389&r1=297388&r2=297389&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExpr.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExpr.cpp Thu Mar  9 10:06:27 2017
@@ -1327,6 +1327,13 @@ bool CodeGenFunction::EmitScalarRangeChe
   if (!NeedsBoolCheck && !NeedsEnumCheck)
     return false;
 
+  // Single-bit booleans don't need to be checked. Special-case this to avoid
+  // a bit width mismatch when handling bitfield values. This is handled by
+  // EmitFromMemory for the non-bitfield case.
+  if (IsBool &&
+      cast<llvm::IntegerType>(Value->getType())->getBitWidth() == 1)
+    return false;
+
   llvm::APInt Min, End;
   if (!getRangeForType(*this, Ty, Min, End, /*StrictEnums=*/true, IsBool))
     return true;
@@ -1549,10 +1556,11 @@ RValue CodeGenFunction::EmitLoadOfLValue
     return EmitLoadOfGlobalRegLValue(LV);
 
   assert(LV.isBitField() && "Unknown LValue type!");
-  return EmitLoadOfBitfieldLValue(LV);
+  return EmitLoadOfBitfieldLValue(LV, Loc);
 }
 
-RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV) {
+RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV,
+                                                 SourceLocation Loc) {
   const CGBitFieldInfo &Info = LV.getBitFieldInfo();
 
   // Get the output type.
@@ -1577,7 +1585,7 @@ RValue CodeGenFunction::EmitLoadOfBitfie
                               "bf.clear");
   }
   Val = Builder.CreateIntCast(Val, ResLTy, Info.IsSigned, "bf.cast");
-
+  EmitScalarRangeCheck(Val, LV.getType(), Loc);
   return RValue::get(Val);
 }
 

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=297389&r1=297388&r2=297389&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Thu Mar  9 10:06:27 2017
@@ -2943,7 +2943,7 @@ public:
   /// rvalue, returning the rvalue.
   RValue EmitLoadOfLValue(LValue V, SourceLocation Loc);
   RValue EmitLoadOfExtVectorElementLValue(LValue V);
-  RValue EmitLoadOfBitfieldLValue(LValue LV);
+  RValue EmitLoadOfBitfieldLValue(LValue LV, SourceLocation Loc);
   RValue EmitLoadOfGlobalRegLValue(LValue LV);
 
   /// EmitStoreThroughLValue - Store the specified rvalue into the specified

Added: cfe/trunk/test/CodeGenCXX/ubsan-bitfields.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/ubsan-bitfields.cpp?rev=297389&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/ubsan-bitfields.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/ubsan-bitfields.cpp Thu Mar  9 10:06:27 2017
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - %s -fsanitize=bool,enum | FileCheck %s
+
+enum E {
+  a = 1,
+  b = 2,
+  c = 3
+};
+
+struct S {
+  E e1 : 10;
+};
+
+// CHECK-LABEL: define i32 @_Z4loadP1S
+E load(S *s) {
+  // CHECK: [[LOAD:%.*]] = load i16, i16* {{.*}}
+  // CHECK: [[CLEAR:%.*]] = and i16 [[LOAD]], 1023
+  // CHECK: [[CAST:%.*]] = zext i16 [[CLEAR]] to i32
+  // CHECK: icmp ule i32 [[CAST]], 3, !nosanitize
+  // CHECK: call void @__ubsan_handle_load_invalid_value
+  return s->e1;
+}
+
+struct Bool {
+  bool b1 : 1;
+  bool b2 : 7;
+  bool b3 : 16;
+};
+
+// CHECK-LABEL: define zeroext i1 @_Z13load_cpp_boolP4Bool
+bool load_cpp_bool(Bool *b) {
+  // CHECK-NOT: call void @__ubsan_handle_load_invalid_value
+  // CHECK-NOT: !nosanitize
+  return b->b1 || b->b2 || b->b3;
+}

Modified: cfe/trunk/test/CodeGenObjC/ubsan-bool.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/ubsan-bool.m?rev=297389&r1=297388&r2=297389&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/ubsan-bool.m (original)
+++ cfe/trunk/test/CodeGenObjC/ubsan-bool.m Thu Mar  9 10:06:27 2017
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -x objective-c -emit-llvm -triple x86_64-apple-macosx10.10.0 -fsanitize=bool %s -o - | FileCheck %s -check-prefixes=SHARED,OBJC
-// RUN: %clang_cc1 -x objective-c++ -emit-llvm -triple x86_64-apple-macosx10.10.0 -fsanitize=bool %s -o - | FileCheck %s -check-prefixes=SHARED,OBJC
+// RUN: %clang_cc1 -x objective-c -emit-llvm -triple x86_64-apple-macosx10.10.0 -fsanitize=bool %s -o - -w | FileCheck %s -check-prefixes=SHARED,OBJC
+// RUN: %clang_cc1 -x objective-c++ -emit-llvm -triple x86_64-apple-macosx10.10.0 -fsanitize=bool %s -o - -w | FileCheck %s -check-prefixes=SHARED,OBJC
 // RUN: %clang_cc1 -x c -emit-llvm -triple x86_64-apple-macosx10.10.0 -fsanitize=bool %s -o - | FileCheck %s -check-prefixes=SHARED,C
 
 typedef signed char BOOL;
@@ -10,4 +10,57 @@ BOOL f1() {
   // C-NOT: call void @__ubsan_handle_load_invalid_value
   BOOL a = 2;
   return a + 1;
+  // SHARED: ret i8
 }
+
+struct S1 {
+  BOOL b1 : 1;
+};
+
+// SHARED-LABEL: f2
+BOOL f2(struct S1 *s) {
+  // OBJC: [[LOAD:%.*]] = load i8, i8* {{.*}}
+  // OBJC: [[SHL:%.*]] = shl i8 [[LOAD]], 7
+  // OBJC: [[ASHR:%.*]] = ashr i8 [[SHL]], 7
+  // OBJC: icmp ule i8 [[ASHR]], 1, !nosanitize
+  // OBJC: call void @__ubsan_handle_load_invalid_value
+
+  // C-NOT: call void @__ubsan_handle_load_invalid_value
+  return s->b1;
+  // SHARED: ret i8
+}
+
+#ifdef __OBJC__
+ at interface I1 {
+ at public
+  BOOL b1 : 1;
+}
+ at property (nonatomic) BOOL b1;
+ at end
+ at implementation I1
+ at synthesize b1;
+ at end
+
+// Check the synthesized getter.
+// OBJC-LABEL: define internal signext i8 @"\01-[I1 b1]"
+// OBJC: [[IVAR:%.*]] = load i64, i64* @"OBJC_IVAR_$_I1.b1"
+// OBJC: [[ADDR:%.*]] = getelementptr inbounds i8, i8* {{.*}}, i64 [[IVAR]]
+// OBJC: [[LOAD:%.*]] = load i8, i8* {{.*}}
+// OBJC: [[SHL:%.*]] = shl i8 [[LOAD]], 7
+// OBJC: [[ASHR:%.*]] = ashr i8 [[SHL]], 7
+// OBJC: icmp ule i8 [[ASHR]], 1, !nosanitize
+// OBJC: call void @__ubsan_handle_load_invalid_value
+
+// Also check direct accesses to the ivar.
+// OBJC-LABEL: f3
+BOOL f3(I1 *i) {
+  // OBJC: [[LOAD:%.*]] = load i8, i8* {{.*}}
+  // OBJC: [[SHL:%.*]] = shl i8 [[LOAD]], 7
+  // OBJC: [[ASHR:%.*]] = ashr i8 [[SHL]], 7
+  // OBJC: icmp ule i8 [[ASHR]], 1, !nosanitize
+  // OBJC: call void @__ubsan_handle_load_invalid_value
+
+  return i->b1;
+  // OBJC: ret i8
+}
+#endif /* __OBJC__ */




More information about the cfe-commits mailing list