Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 717fbcb

Browse files
committed
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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@297389 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 64a83a0 commit 717fbcb

File tree

5 files changed

+102
-7
lines changed

5 files changed

+102
-7
lines changed

Diff for: lib/CodeGen/CGAtomic.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,7 @@ RValue AtomicInfo::convertAtomicTempToRValue(Address addr,
11811181
if (LVal.isBitField())
11821182
return CGF.EmitLoadOfBitfieldLValue(
11831183
LValue::MakeBitfield(addr, LVal.getBitFieldInfo(), LVal.getType(),
1184-
LVal.getAlignmentSource()));
1184+
LVal.getAlignmentSource()), loc);
11851185
if (LVal.isVectorElt())
11861186
return CGF.EmitLoadOfLValue(
11871187
LValue::MakeVectorElt(addr, LVal.getVectorIdx(), LVal.getType(),

Diff for: lib/CodeGen/CGExpr.cpp

+11-3
Original file line numberDiff line numberDiff line change
@@ -1327,6 +1327,13 @@ bool CodeGenFunction::EmitScalarRangeCheck(llvm::Value *Value, QualType Ty,
13271327
if (!NeedsBoolCheck && !NeedsEnumCheck)
13281328
return false;
13291329

1330+
// Single-bit booleans don't need to be checked. Special-case this to avoid
1331+
// a bit width mismatch when handling bitfield values. This is handled by
1332+
// EmitFromMemory for the non-bitfield case.
1333+
if (IsBool &&
1334+
cast<llvm::IntegerType>(Value->getType())->getBitWidth() == 1)
1335+
return false;
1336+
13301337
llvm::APInt Min, End;
13311338
if (!getRangeForType(*this, Ty, Min, End, /*StrictEnums=*/true, IsBool))
13321339
return true;
@@ -1549,10 +1556,11 @@ RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, SourceLocation Loc) {
15491556
return EmitLoadOfGlobalRegLValue(LV);
15501557

15511558
assert(LV.isBitField() && "Unknown LValue type!");
1552-
return EmitLoadOfBitfieldLValue(LV);
1559+
return EmitLoadOfBitfieldLValue(LV, Loc);
15531560
}
15541561

1555-
RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV) {
1562+
RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV,
1563+
SourceLocation Loc) {
15561564
const CGBitFieldInfo &Info = LV.getBitFieldInfo();
15571565

15581566
// Get the output type.
@@ -1577,7 +1585,7 @@ RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV) {
15771585
"bf.clear");
15781586
}
15791587
Val = Builder.CreateIntCast(Val, ResLTy, Info.IsSigned, "bf.cast");
1580-
1588+
EmitScalarRangeCheck(Val, LV.getType(), Loc);
15811589
return RValue::get(Val);
15821590
}
15831591

Diff for: lib/CodeGen/CodeGenFunction.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -2943,7 +2943,7 @@ class CodeGenFunction : public CodeGenTypeCache {
29432943
/// rvalue, returning the rvalue.
29442944
RValue EmitLoadOfLValue(LValue V, SourceLocation Loc);
29452945
RValue EmitLoadOfExtVectorElementLValue(LValue V);
2946-
RValue EmitLoadOfBitfieldLValue(LValue LV);
2946+
RValue EmitLoadOfBitfieldLValue(LValue LV, SourceLocation Loc);
29472947
RValue EmitLoadOfGlobalRegLValue(LValue LV);
29482948

29492949
/// EmitStoreThroughLValue - Store the specified rvalue into the specified

Diff for: test/CodeGenCXX/ubsan-bitfields.cpp

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - %s -fsanitize=bool,enum | FileCheck %s
2+
3+
enum E {
4+
a = 1,
5+
b = 2,
6+
c = 3
7+
};
8+
9+
struct S {
10+
E e1 : 10;
11+
};
12+
13+
// CHECK-LABEL: define i32 @_Z4loadP1S
14+
E load(S *s) {
15+
// CHECK: [[LOAD:%.*]] = load i16, i16* {{.*}}
16+
// CHECK: [[CLEAR:%.*]] = and i16 [[LOAD]], 1023
17+
// CHECK: [[CAST:%.*]] = zext i16 [[CLEAR]] to i32
18+
// CHECK: icmp ule i32 [[CAST]], 3, !nosanitize
19+
// CHECK: call void @__ubsan_handle_load_invalid_value
20+
return s->e1;
21+
}
22+
23+
struct Bool {
24+
bool b1 : 1;
25+
bool b2 : 7;
26+
bool b3 : 16;
27+
};
28+
29+
// CHECK-LABEL: define zeroext i1 @_Z13load_cpp_boolP4Bool
30+
bool load_cpp_bool(Bool *b) {
31+
// CHECK-NOT: call void @__ubsan_handle_load_invalid_value
32+
// CHECK-NOT: !nosanitize
33+
return b->b1 || b->b2 || b->b3;
34+
}

Diff for: test/CodeGenObjC/ubsan-bool.m

+55-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// 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
2-
// 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
1+
// 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
2+
// 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
33
// RUN: %clang_cc1 -x c -emit-llvm -triple x86_64-apple-macosx10.10.0 -fsanitize=bool %s -o - | FileCheck %s -check-prefixes=SHARED,C
44

55
typedef signed char BOOL;
@@ -10,4 +10,57 @@ BOOL f1() {
1010
// C-NOT: call void @__ubsan_handle_load_invalid_value
1111
BOOL a = 2;
1212
return a + 1;
13+
// SHARED: ret i8
1314
}
15+
16+
struct S1 {
17+
BOOL b1 : 1;
18+
};
19+
20+
// SHARED-LABEL: f2
21+
BOOL f2(struct S1 *s) {
22+
// OBJC: [[LOAD:%.*]] = load i8, i8* {{.*}}
23+
// OBJC: [[SHL:%.*]] = shl i8 [[LOAD]], 7
24+
// OBJC: [[ASHR:%.*]] = ashr i8 [[SHL]], 7
25+
// OBJC: icmp ule i8 [[ASHR]], 1, !nosanitize
26+
// OBJC: call void @__ubsan_handle_load_invalid_value
27+
28+
// C-NOT: call void @__ubsan_handle_load_invalid_value
29+
return s->b1;
30+
// SHARED: ret i8
31+
}
32+
33+
#ifdef __OBJC__
34+
@interface I1 {
35+
@public
36+
BOOL b1 : 1;
37+
}
38+
@property (nonatomic) BOOL b1;
39+
@end
40+
@implementation I1
41+
@synthesize b1;
42+
@end
43+
44+
// Check the synthesized getter.
45+
// OBJC-LABEL: define internal signext i8 @"\01-[I1 b1]"
46+
// OBJC: [[IVAR:%.*]] = load i64, i64* @"OBJC_IVAR_$_I1.b1"
47+
// OBJC: [[ADDR:%.*]] = getelementptr inbounds i8, i8* {{.*}}, i64 [[IVAR]]
48+
// OBJC: [[LOAD:%.*]] = load i8, i8* {{.*}}
49+
// OBJC: [[SHL:%.*]] = shl i8 [[LOAD]], 7
50+
// OBJC: [[ASHR:%.*]] = ashr i8 [[SHL]], 7
51+
// OBJC: icmp ule i8 [[ASHR]], 1, !nosanitize
52+
// OBJC: call void @__ubsan_handle_load_invalid_value
53+
54+
// Also check direct accesses to the ivar.
55+
// OBJC-LABEL: f3
56+
BOOL f3(I1 *i) {
57+
// OBJC: [[LOAD:%.*]] = load i8, i8* {{.*}}
58+
// OBJC: [[SHL:%.*]] = shl i8 [[LOAD]], 7
59+
// OBJC: [[ASHR:%.*]] = ashr i8 [[SHL]], 7
60+
// OBJC: icmp ule i8 [[ASHR]], 1, !nosanitize
61+
// OBJC: call void @__ubsan_handle_load_invalid_value
62+
63+
return i->b1;
64+
// OBJC: ret i8
65+
}
66+
#endif /* __OBJC__ */

0 commit comments

Comments
 (0)