Skip to content

Commit 3faed9f

Browse files
author
Mandeep Singh Grang
committed
Correctly determine bounds for predefined literals (#673)
Cherry-picked from commit 6252abb Correctly determine bounds for predefined literals. Section 6.4.2.2 of the C11 Standard defines predefined identifiers: the special identifier __func__ evaluates to a string that is the name of the current function. In clang, predefined identifiers are represented using the PredefinedExpr class, which wraps a string literal. The PredefinedExpr class evaluates to an lvalue that is the address of the string literal. To represent bounds information, follow the same pattern that we use for string literals:. Bind the result of the PredefinedExpr class to a temporary and use the temporary in the bounds of the expression. We considered having the class wrap a temporary expression, but that causes existing tests of predefined literals to break and requires more changes to the compiler logic for predefined expressions.
1 parent 7cde307 commit 3faed9f

File tree

4 files changed

+145
-28
lines changed

4 files changed

+145
-28
lines changed

clang/lib/Sema/Sema.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -518,11 +518,12 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
518518
E = Materialized.get();
519519
}
520520

521-
// For Checked C, create a temporary for string literal or compound literals
522-
// for use during bounds checking.
521+
// For Checked C, create a temporary for string literal, compound literals or
522+
// predefined literals for use during bounds checking.
523523
if (Kind == CK_ArrayToPointerDecay && getLangOpts().CheckedC) {
524524
Expr *S = E->IgnoreParens();
525-
if (isa<StringLiteral>(S) || isa<CompoundLiteralExpr>(S))
525+
if (isa<StringLiteral>(S) || isa<CompoundLiteralExpr>(S) ||
526+
isa<PredefinedExpr>(S))
526527
E = new (Context) CHKCBindTemporaryExpr(E);
527528
}
528529

clang/lib/Sema/SemaBounds.cpp

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,36 @@ namespace {
759759
return ExpandToRange(Base, BE);
760760
}
761761

762+
// Infer bounds for string literals.
763+
BoundsExpr *InferBoundsForStringLiteral(Expr *E, StringLiteral *SL,
764+
CHKCBindTemporaryExpr *Binding) {
765+
// Use the number of characters in the string (excluding the null
766+
// terminator) to calcaulte size. Don't use the array type of the
767+
// literal. In unchecked scopes, the array type is unchecked and its
768+
// size includes the null terminator. It converts to an ArrayPtr that
769+
// could be used to overwrite the null terminator. We need to prevent
770+
// this because literal strings may be shared and writeable, depending on
771+
// the C implementation.
772+
auto *Size = CreateIntegerLiteral(llvm::APInt(64, SL->getLength()));
773+
auto *CBE =
774+
new (Context) CountBoundsExpr(BoundsExpr::Kind::ElementCount,
775+
Size, SourceLocation(),
776+
SourceLocation());
777+
778+
auto PtrType = Context.getDecayedType(E->getType());
779+
780+
// For a string literal expression, we always bind the result of the
781+
// expression to a temporary. We then use this temporary in the bounds
782+
// expression for the string literal expression. Otherwise, a runtime
783+
// bounds check based on accessing the predefined expression could be
784+
// incorrect: the base value could be different for the lower and upper
785+
// bounds.
786+
auto *ArrLValue = CreateTemporaryUse(Binding);
787+
auto *Base = CreateImplicitCast(PtrType,
788+
CastKind::CK_ArrayToPointerDecay,
789+
ArrLValue);
790+
return ExpandToRange(Base, CBE);
791+
}
762792

763793
// Infer bounds for an lvalue. The bounds determine whether
764794
// it is valid to access memory using the lvalue. The bounds
@@ -879,6 +909,7 @@ namespace {
879909
case Expr::CHKCBindTemporaryExprClass: {
880910
CHKCBindTemporaryExpr *Binding = cast<CHKCBindTemporaryExpr>(E);
881911
Expr *SE = Binding->getSubExpr()->IgnoreParens();
912+
882913
if (isa<CompoundLiteralExpr>(SE)) {
883914
BoundsExpr *BE = CreateBoundsForArrayType(E->getType());
884915
QualType PtrType = Context.getDecayedType(E->getType());
@@ -887,27 +918,15 @@ namespace {
887918
CastKind::CK_ArrayToPointerDecay,
888919
ArrLValue);
889920
return ExpandToRange(Base, BE);
890-
} else if (StringLiteral *SL = dyn_cast<StringLiteral>(SE)) {
891-
// Use the number of characters in the string (excluding the
892-
// null terminator) to calcaulte size. Don't use the
893-
// array type of the literal. In unchecked scopes, the array type is
894-
// unchecked and its size includes the null terminator. It converts
895-
// to an ArrayPtr that could be used to overwrite the null terminator.
896-
// We need to prevent this because literal strings may be shared and
897-
// writeable, depending on the C implementation.
898-
IntegerLiteral *Size = CreateIntegerLiteral(llvm::APInt(64, SL->getLength()));
899-
CountBoundsExpr *CBE =
900-
new (Context) CountBoundsExpr(BoundsExpr::Kind::ElementCount,
901-
Size, SourceLocation(),
902-
SourceLocation());
903-
QualType PtrType = Context.getDecayedType(E->getType());
904-
Expr *ArrLValue = CreateTemporaryUse(Binding);
905-
Expr *Base = CreateImplicitCast(PtrType,
906-
CastKind::CK_ArrayToPointerDecay,
907-
ArrLValue);
908-
return ExpandToRange(Base, CBE);
909-
} else
910-
return CreateBoundsAlwaysUnknown();
921+
}
922+
923+
if (auto *SL = dyn_cast<StringLiteral>(SE))
924+
return InferBoundsForStringLiteral(E, SL, Binding);
925+
926+
if (auto *PE = dyn_cast<PredefinedExpr>(SE)) {
927+
auto *SL = PE->getFunctionName();
928+
return InferBoundsForStringLiteral(E, SL, Binding);
929+
}
911930
}
912931
default:
913932
return CreateBoundsAlwaysUnknown();
@@ -3647,4 +3666,4 @@ void Sema::WarnDynamicCheckAlwaysFails(const Expr *Condition) {
36473666
<< Condition->getSourceRange();
36483667
}
36493668
}
3650-
}
3669+
}

clang/lib/Sema/SemaExpr.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3264,25 +3264,29 @@ ExprResult Sema::BuildPredefinedExpr(SourceLocation Loc,
32643264
unsigned Length = Str.length();
32653265

32663266
llvm::APInt LengthI(32, Length + 1);
3267+
// Get an array type for the string, according to C99 6.4.5.
3268+
CheckedArrayKind ArrayKind = IsCheckedScope() ?
3269+
CheckedArrayKind::NtChecked : CheckedArrayKind::Unchecked;
3270+
32673271
if (IK == PredefinedExpr::LFunction || IK == PredefinedExpr::LFuncSig) {
32683272
ResTy =
32693273
Context.adjustStringLiteralBaseType(Context.WideCharTy.withConst());
32703274
SmallString<32> RawChars;
32713275
ConvertUTF8ToWideString(Context.getTypeSizeInChars(ResTy).getQuantity(),
32723276
Str, RawChars);
3277+
32733278
ResTy = Context.getConstantArrayType(ResTy, LengthI, ArrayType::Normal,
3274-
/*IndexTypeQuals*/ 0);
3279+
/*IndexTypeQuals*/ 0, ArrayKind);
32753280
SL = StringLiteral::Create(Context, RawChars, StringLiteral::Wide,
32763281
/*Pascal*/ false, ResTy, Loc);
32773282
} else {
32783283
ResTy = Context.adjustStringLiteralBaseType(Context.CharTy.withConst());
32793284
ResTy = Context.getConstantArrayType(ResTy, LengthI, ArrayType::Normal,
3280-
/*IndexTypeQuals*/ 0);
3285+
/*IndexTypeQuals*/ 0, ArrayKind);
32813286
SL = StringLiteral::Create(Context, Str, StringLiteral::Ascii,
32823287
/*Pascal*/ false, ResTy, Loc);
32833288
}
32843289
}
3285-
32863290
return PredefinedExpr::Create(Context, Loc, ResTy, IK, SL);
32873291
}
32883292

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Tests of inferred bounds for predefined literals like __func__, etc.
2+
//
3+
// RUN: %clang_cc1 -fdump-inferred-bounds %s | FileCheck %s
4+
// expected-no-diagnostics
5+
6+
void f1() {
7+
// CHECK: ImplicitCastExpr {{0x[0-9a-f]+}} '_Ptr<const char>' <BitCast>
8+
// CHECK-NEXT: |-Inferred SubExpr Bounds
9+
// CHECK-NEXT: | `-RangeBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE'
10+
// CHECK-NEXT: | |-ImplicitCastExpr {{0x[0-9a-f]+}} 'const char *':'const char *' <ArrayToPointerDecay>
11+
// CHECK-NEXT: | | `-BoundsValueExpr {{0x[0-9a-f]+}} 'const char [3]' lvalue _BoundTemporary {{0x[0-9a-f]+}}
12+
// CHECK-NEXT: | `-BinaryOperator {{0x[0-9a-f]+}} 'const char *':'const char *' '+'
13+
// CHECK-NEXT: | |-ImplicitCastExpr {{0x[0-9a-f]+}} 'const char *':'const char *' <ArrayToPointerDecay>
14+
// CHECK-NEXT: | | `-BoundsValueExpr {{0x[0-9a-f]+}} 'const char [3]' lvalue _BoundTemporary {{0x[0-9a-f]+}}
15+
// CHECK-NEXT: | `-IntegerLiteral {{0x[0-9a-f]+}} 'int' 2
16+
// CHECK-NEXT: `-ImplicitCastExpr {{0x[0-9a-f]+}} 'const char *' <ArrayToPointerDecay>
17+
// CHECK-NEXT: `-CHKCBindTemporaryExpr {{0x[0-9a-f]+}} 'const char [3]' lvalue
18+
// CHECK-NEXT: `-PredefinedExpr {{0x[0-9a-f]+}} 'const char [3]' lvalue __func__
19+
// CHECK-NEXT: `-StringLiteral {{0x[0-9a-f]+}} 'const char [3]' lvalue "f1"
20+
_Ptr<const char> s1 = __func__;
21+
}
22+
23+
void f2(_Ptr<const char> s2);
24+
void f3() {
25+
// CHECK: ImplicitCastExpr {{0x[0-9a-f]+}} '_Ptr<const char>' <BitCast>
26+
// CHECK-NEXT: |-Inferred SubExpr Bounds
27+
// CHECK-NEXT: | `-RangeBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE'
28+
// CHECK-NEXT: | |-ImplicitCastExpr {{0x[0-9a-f]+}} 'const char *':'const char *' <ArrayToPointerDecay>
29+
// CHECK-NEXT: | | `-BoundsValueExpr {{0x[0-9a-f]+}} 'const char [3]' lvalue _BoundTemporary {{0x[0-9a-f]+}}
30+
// CHECK-NEXT: | `-BinaryOperator {{0x[0-9a-f]+}} 'const char *':'const char *' '+'
31+
// CHECK-NEXT: | |-ImplicitCastExpr {{0x[0-9a-f]+}} 'const char *':'const char *' <ArrayToPointerDecay>
32+
// CHECK-NEXT: | | `-BoundsValueExpr {{0x[0-9a-f]+}} 'const char [3]' lvalue _BoundTemporary {{0x[0-9a-f]+}}
33+
// CHECK-NEXT: | `-IntegerLiteral {{0x[0-9a-f]+}} 'int' 2
34+
// CHECK-NEXT: `-ImplicitCastExpr {{0x[0-9a-f]+}} 'const char *' <ArrayToPointerDecay>
35+
// CHECK-NEXT: `-CHKCBindTemporaryExpr {{0x[0-9a-f]+}} 'const char [3]' lvalue
36+
// CHECK-NEXT: `-PredefinedExpr {{0x[0-9a-f]+}} 'const char [3]' lvalue __func__
37+
// CHECK-NEXT: `-StringLiteral {{0x[0-9a-f]+}} 'const char [3]' lvalue "f3"
38+
f2(__func__);
39+
}
40+
41+
void f4() {
42+
// CHECK: ImplicitCastExpr {{0x[0-9a-f]+}} '_Ptr<const char>' <BitCast>
43+
// CHECK-NEXT: |-Inferred SubExpr Bounds
44+
// CHECK-NEXT: | `-RangeBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE'
45+
// CHECK-NEXT: | |-ImplicitCastExpr {{0x[0-9a-f]+}} 'const char *':'const char *' <ArrayToPointerDecay>
46+
// CHECK-NEXT: | | `-BoundsValueExpr {{0x[0-9a-f]+}} 'const char [3]' lvalue _BoundTemporary {{0x[0-9a-f]+}}
47+
// CHECK-NEXT: | `-BinaryOperator {{0x[0-9a-f]+}} 'const char *':'const char *' '+'
48+
// CHECK-NEXT: | |-ImplicitCastExpr {{0x[0-9a-f]+}} 'const char *':'const char *' <ArrayToPointerDecay>
49+
// CHECK-NEXT: | | `-BoundsValueExpr {{0x[0-9a-f]+}} 'const char [3]' lvalue _BoundTemporary {{0x[0-9a-f]+}}
50+
// CHECK-NEXT: | `-IntegerLiteral {{0x[0-9a-f]+}} 'int' 2
51+
// CHECK-NEXT: `-ImplicitCastExpr {{0x[0-9a-f]+}} 'const char *' <ArrayToPointerDecay>
52+
// CHECK-NEXT: `-CHKCBindTemporaryExpr {{0x[0-9a-f]+}} 'const char [3]' lvalue
53+
// CHECK-NEXT: `-PredefinedExpr {{0x[0-9a-f]+}} 'const char [3]' lvalue __FUNCTION__
54+
// CHECK-NEXT: `-StringLiteral {{0x[0-9a-f]+}} 'const char [3]' lvalue "f4"
55+
_Ptr<const char> s4 = __FUNCTION__;
56+
}
57+
58+
void f5() {
59+
// CHECK: |-RangeBoundsExpr {{0x[0-9a-f]+}} <col:{{[0-9]+}}, col:{{[0-9]+}}> 'NULL TYPE'
60+
// CHECK-NEXT: | |-ImplicitCastExpr {{0x[0-9a-f]+}} <col:{{[0-9]+}}> '_Array_ptr<const char>' <LValueToRValue>
61+
// CHECK-NEXT: | | `-DeclRefExpr {{0x[0-9a-f]+}} <col:{{[0-9]+}}> '_Array_ptr<const char>' lvalue Var {{0x[0-9a-f]+}} 's5' '_Array_ptr<const char>'
62+
// CHECK-NEXT: | `-BinaryOperator {{0x[0-9a-f]+}} <col:{{[0-9]+}}, col:{{[0-9]+}}> '_Array_ptr<const char>' '+'
63+
// CHECK-NEXT: | |-ImplicitCastExpr {{0x[0-9a-f]+}} <col:{{[0-9]+}}> '_Array_ptr<const char>' <LValueToRValue>
64+
// CHECK-NEXT: | | `-DeclRefExpr {{0x[0-9a-f]+}} <col:{{[0-9]+}}> '_Array_ptr<const char>' lvalue Var {{0x[0-9a-f]+}} 's5' '_Array_ptr<const char>'
65+
// CHECK-NEXT: | `-IntegerLiteral {{0x[0-9a-f]+}} <col:{{[0-9]+}}> 'int' 2
66+
// CHECK-NEXT: `-ImplicitCastExpr {{0x[0-9a-f]+}} <col:{{[0-9]+}}> '_Array_ptr<const char>' <BitCast>
67+
// CHECK-NEXT: `-ImplicitCastExpr {{0x[0-9a-f]+}} <col:{{[0-9]+}}> 'const char *' <ArrayToPointerDecay>
68+
// CHECK-NEXT: `-CHKCBindTemporaryExpr {{0x[0-9a-f]+}} <col:{{[0-9]+}}> 'const char [3]' lvalue
69+
// CHECK-NEXT: `-PredefinedExpr {{0x[0-9a-f]+}} <col:{{[0-9]+}}> 'const char [3]' lvalue __func__
70+
// CHECK-NEXT: `-StringLiteral {{0x[0-9a-f]+}} <col:{{[0-9]+}}> 'const char [3]' lvalue "f5"
71+
_Array_ptr<const char> s5 : bounds(s5, s5+ 2) = __func__;
72+
}
73+
74+
char f6() {
75+
// CHECK: ImplicitCastExpr {{0x[0-9a-f]+}} 'char' <LValueToRValue>
76+
// CHECK-NEXT: `-ArraySubscriptExpr {{0x[0-9a-f]+}} 'const char' lvalue
77+
// CHECK-NEXT: |-Bounds Null-terminated read
78+
// CHECK-NEXT: | `-RangeBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE'
79+
// CHECK-NEXT: | |-ImplicitCastExpr {{0x[0-9a-f]+}} '_Nt_array_ptr<const char>':'_Nt_array_ptr<const char>' <ArrayToPointerDecay>
80+
// CHECK-NEXT: | | `-BoundsValueExpr {{0x[0-9a-f]+}} 'const char _Nt_checked[3]' lvalue _BoundTemporary {{0x[0-9a-f]+}}
81+
// CHECK-NEXT: | `-BinaryOperator {{0x[0-9a-f]+}} '_Nt_array_ptr<const char>':'_Nt_array_ptr<const char>' '+'
82+
// CHECK-NEXT: | |-ImplicitCastExpr {{0x[0-9a-f]+}} '_Nt_array_ptr<const char>':'_Nt_array_ptr<const char>' <ArrayToPointerDecay>
83+
// CHECK-NEXT: | | `-BoundsValueExpr {{0x[0-9a-f]+}} 'const char _Nt_checked[3]' lvalue _BoundTemporary {{0x[0-9a-f]+}}
84+
// CHECK-NEXT: | `-IntegerLiteral {{0x[0-9a-f]+}} 'int' 2
85+
// CHECK-NEXT: |-ImplicitCastExpr {{0x[0-9a-f]+}} '_Nt_array_ptr<const char>' <ArrayToPointerDecay>
86+
// CHECK-NEXT: | `-CHKCBindTemporaryExpr {{0x[0-9a-f]+}} 'const char _Nt_checked[3]' lvalue
87+
// CHECK-NEXT: | `-PredefinedExpr {{0x[0-9a-f]+}} 'const char _Nt_checked[3]' lvalue __func__
88+
// CHECK-NEXT: | `-StringLiteral {{0x[0-9a-f]+}} 'const char _Nt_checked[3]' lvalue "f6"
89+
// CHECK-NEXT: `-IntegerLiteral {{0x[0-9a-f]+}} 'int' 100
90+
#pragma CHECKED_SCOPE ON
91+
return __func__[100];
92+
#pragma CHECKED_SCOPE OFF
93+
}

0 commit comments

Comments
 (0)