diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 1da85f3ce135f..d88ea3a100485 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -705,19 +705,19 @@ class ScalarExprEmitter // Unary Operators. Value *VisitUnaryPostDec(const UnaryOperator *E) { - LValue LV = EmitLValue(E->getSubExpr()); + LValue LV = EmitCheckedLValue(E->getSubExpr(), CodeGenFunction::TCK_Store); return EmitScalarPrePostIncDec(E, LV, false, false); } Value *VisitUnaryPostInc(const UnaryOperator *E) { - LValue LV = EmitLValue(E->getSubExpr()); + LValue LV = EmitCheckedLValue(E->getSubExpr(), CodeGenFunction::TCK_Store); return EmitScalarPrePostIncDec(E, LV, true, false); } Value *VisitUnaryPreDec(const UnaryOperator *E) { - LValue LV = EmitLValue(E->getSubExpr()); + LValue LV = EmitCheckedLValue(E->getSubExpr(), CodeGenFunction::TCK_Store); return EmitScalarPrePostIncDec(E, LV, false, true); } Value *VisitUnaryPreInc(const UnaryOperator *E) { - LValue LV = EmitLValue(E->getSubExpr()); + LValue LV = EmitCheckedLValue(E->getSubExpr(), CodeGenFunction::TCK_Store); return EmitScalarPrePostIncDec(E, LV, true, true); } diff --git a/clang/test/BoundsSafety/CodeGen/array-subscript-incdec-bounds-check.c b/clang/test/BoundsSafety/CodeGen/array-subscript-incdec-bounds-check.c new file mode 100644 index 0000000000000..39a4fa380fce6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/array-subscript-incdec-bounds-check.c @@ -0,0 +1,282 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" "!prof ![0-9]+" --version 6 + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +// Pre/post inc/dec on an array element must go through the checked-lvalue +// path so that -fbounds-safety inserts a bounds check on the write. A prior +// bug left inc/dec unchecked, so a hardening feature silently skipped a +// common write form. + +int a[5]; + +// CHECK-O0-LABEL: define dso_local void @store( +// CHECK-O0-SAME: i32 noundef [[I:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-O0-NEXT: [[ENTRY:.*:]] +// CHECK-O0-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr @a, i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[ARRAYIDX]], i64 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT]]: +// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP1]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT2]]: +// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP3]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT4]]: +// CHECK-O0-NEXT: store i32 1, ptr [[ARRAYIDX]], align 4 +// CHECK-O0-NEXT: ret void +// +// CHECK-O2-LABEL: define dso_local void @store( +// CHECK-O2-SAME: i32 noundef [[I:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-O2-NEXT: [[ENTRY:.*:]] +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[I]] to i64 +// CHECK-O2-NEXT: [[ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr @a, i64 [[IDXPROM]] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND3:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[OR_COND3]], label %[[CONT2:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O2: [[TRAP]]: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: [[CONT2]]: +// CHECK-O2-NEXT: store i32 1, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-O2-NEXT: ret void +// +void store(int i) { a[i] = 1; } + +// CHECK-O0-LABEL: define dso_local void @postinc( +// CHECK-O0-SAME: i32 noundef [[I:%.*]]) #[[ATTR0]] { +// CHECK-O0-NEXT: [[ENTRY:.*:]] +// CHECK-O0-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr @a, i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[ARRAYIDX]], i64 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT]]: +// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP1]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT2]]: +// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP3]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT4]]: +// CHECK-O0-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-O0-NEXT: [[INC:%.*]] = add nsw i32 [[TMP5]], 1 +// CHECK-O0-NEXT: store i32 [[INC]], ptr [[ARRAYIDX]], align 4 +// CHECK-O0-NEXT: ret void +// +// CHECK-O2-LABEL: define dso_local void @postinc( +// CHECK-O2-SAME: i32 noundef [[I:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-O2-NEXT: [[ENTRY:.*:]] +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[I]] to i64 +// CHECK-O2-NEXT: [[ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr @a, i64 [[IDXPROM]] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND3:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[OR_COND3]], label %[[CONT2:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O2: [[TRAP]]: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: [[CONT2]]: +// CHECK-O2-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-O2-NEXT: [[INC:%.*]] = add nsw i32 [[TMP4]], 1 +// CHECK-O2-NEXT: store i32 [[INC]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-O2-NEXT: ret void +// +void postinc(int i) { a[i]++; } + +// CHECK-O0-LABEL: define dso_local void @preinc( +// CHECK-O0-SAME: i32 noundef [[I:%.*]]) #[[ATTR0]] { +// CHECK-O0-NEXT: [[ENTRY:.*:]] +// CHECK-O0-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr @a, i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[ARRAYIDX]], i64 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT]]: +// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP1]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT2]]: +// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP3]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT4]]: +// CHECK-O0-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-O0-NEXT: [[INC:%.*]] = add nsw i32 [[TMP5]], 1 +// CHECK-O0-NEXT: store i32 [[INC]], ptr [[ARRAYIDX]], align 4 +// CHECK-O0-NEXT: ret void +// +// CHECK-O2-LABEL: define dso_local void @preinc( +// CHECK-O2-SAME: i32 noundef [[I:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-O2-NEXT: [[ENTRY:.*:]] +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[I]] to i64 +// CHECK-O2-NEXT: [[ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr @a, i64 [[IDXPROM]] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND3:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[OR_COND3]], label %[[CONT2:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O2: [[TRAP]]: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: [[CONT2]]: +// CHECK-O2-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-O2-NEXT: [[INC:%.*]] = add nsw i32 [[TMP4]], 1 +// CHECK-O2-NEXT: store i32 [[INC]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-O2-NEXT: ret void +// +void preinc(int i) { ++a[i]; } + +// CHECK-O0-LABEL: define dso_local void @postdec( +// CHECK-O0-SAME: i32 noundef [[I:%.*]]) #[[ATTR0]] { +// CHECK-O0-NEXT: [[ENTRY:.*:]] +// CHECK-O0-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr @a, i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[ARRAYIDX]], i64 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT]]: +// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP1]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT2]]: +// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP3]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT4]]: +// CHECK-O0-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-O0-NEXT: [[DEC:%.*]] = add nsw i32 [[TMP5]], -1 +// CHECK-O0-NEXT: store i32 [[DEC]], ptr [[ARRAYIDX]], align 4 +// CHECK-O0-NEXT: ret void +// +// CHECK-O2-LABEL: define dso_local void @postdec( +// CHECK-O2-SAME: i32 noundef [[I:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-O2-NEXT: [[ENTRY:.*:]] +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[I]] to i64 +// CHECK-O2-NEXT: [[ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr @a, i64 [[IDXPROM]] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND3:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[OR_COND3]], label %[[CONT2:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O2: [[TRAP]]: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: [[CONT2]]: +// CHECK-O2-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-O2-NEXT: [[DEC:%.*]] = add nsw i32 [[TMP4]], -1 +// CHECK-O2-NEXT: store i32 [[DEC]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-O2-NEXT: ret void +// +void postdec(int i) { a[i]--; } + +// CHECK-O0-LABEL: define dso_local void @predec( +// CHECK-O0-SAME: i32 noundef [[I:%.*]]) #[[ATTR0]] { +// CHECK-O0-NEXT: [[ENTRY:.*:]] +// CHECK-O0-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr @a, i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[ARRAYIDX]], i64 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT]]: +// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP1]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT2]]: +// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O0: [[TRAP3]]: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: [[CONT4]]: +// CHECK-O0-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-O0-NEXT: [[DEC:%.*]] = add nsw i32 [[TMP5]], -1 +// CHECK-O0-NEXT: store i32 [[DEC]], ptr [[ARRAYIDX]], align 4 +// CHECK-O0-NEXT: ret void +// +// CHECK-O2-LABEL: define dso_local void @predec( +// CHECK-O2-SAME: i32 noundef [[I:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-O2-NEXT: [[ENTRY:.*:]] +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[I]] to i64 +// CHECK-O2-NEXT: [[ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr @a, i64 [[IDXPROM]] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND3:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[OR_COND3]], label %[[CONT2:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-O2: [[TRAP]]: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: [[CONT2]]: +// CHECK-O2-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-O2-NEXT: [[DEC:%.*]] = add nsw i32 [[TMP4]], -1 +// CHECK-O2-NEXT: store i32 [[DEC]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-O2-NEXT: ret void +// +void predec(int i) { --a[i]; } diff --git a/clang/test/CodeGen/bounds-checking.c b/clang/test/CodeGen/bounds-checking.c index 5e6b317a99969..fe28143f74902 100644 --- a/clang/test/CodeGen/bounds-checking.c +++ b/clang/test/CodeGen/bounds-checking.c @@ -1,6 +1,6 @@ // N.B. The clang driver defaults to -fsanitize-merge but clang_cc1 effectively // defaults to -fno-sanitize-merge. -// RUN: %clang_cc1 -fsanitize=array-bounds -O -fsanitize-trap=array-bounds -emit-llvm -triple x86_64-apple-darwin10 -DNO_DYNAMIC %s -o - | FileCheck %s +// RUN: %clang_cc1 -fsanitize=array-bounds -O -fsanitize-trap=array-bounds -emit-llvm -triple x86_64-apple-darwin10 -DNO_DYNAMIC %s -o - | FileCheck %s --check-prefixes=CHECK,INCDEC // RUN: %clang_cc1 -fsanitize=array-bounds -O -emit-llvm -triple x86_64-apple-darwin10 %s -o - | not FileCheck %s // // RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s @@ -104,5 +104,41 @@ struct S { struct S *f9(int i) { return &s[i]; } + +// Pre/post inc/dec on an array element must use the same bounds check as a +// store: index < size, not index <= size. A prior bug allowed the write at +// index == size (one past the end) to slip through. +int Arr[5]; +// INCDEC-LABEL: @f10_store +void f10_store(int i) { + // INCDEC: icmp ult i32 %i, 5 + // INCDEC: call {{.*}} @llvm.{{(ubsan)?trap}} + Arr[i] = 1; +} +// INCDEC-LABEL: @f10_postinc +void f10_postinc(int i) { + // INCDEC: icmp ult i32 %i, 5 + // INCDEC: call {{.*}} @llvm.{{(ubsan)?trap}} + Arr[i]++; +} +// INCDEC-LABEL: @f10_preinc +void f10_preinc(int i) { + // INCDEC: icmp ult i32 %i, 5 + // INCDEC: call {{.*}} @llvm.{{(ubsan)?trap}} + ++Arr[i]; +} +// INCDEC-LABEL: @f10_postdec +void f10_postdec(int i) { + // INCDEC: icmp ult i32 %i, 5 + // INCDEC: call {{.*}} @llvm.{{(ubsan)?trap}} + Arr[i]--; +} +// INCDEC-LABEL: @f10_predec +void f10_predec(int i) { + // INCDEC: icmp ult i32 %i, 5 + // INCDEC: call {{.*}} @llvm.{{(ubsan)?trap}} + --Arr[i]; +} + // NOOPTLOCAL: attributes #[[ATTR1]] = { nomerge noreturn nounwind } // NOOPTARRAY: attributes #[[ATTR2]] = { nomerge noreturn nounwind }