@@ -6050,37 +6050,80 @@ static void CheckFormatString(
6050
6050
llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
6051
6051
bool IgnoreStringsWithoutSpecifiers);
6052
6052
6053
- enum StringLiteralConstantEvaluationResult {
6054
- SLCER_NotEvaluated,
6055
- SLCER_NotNullTerminated,
6056
- SLCER_Evaluated,
6053
+ class FormatStringFinder {
6054
+ enum StringLiteralConstantEvaluationResult {
6055
+ SLCER_NotEvaluated,
6056
+ SLCER_NotNullTerminated,
6057
+ SLCER_Evaluated,
6058
+ };
6059
+
6060
+ Sema &S;
6061
+ const StringLiteral *ReferenceFormatString;
6062
+ ArrayRef<const Expr *> Args;
6063
+ llvm::SmallBitVector &CheckedVarArgs;
6064
+ UncoveredArgHandler &UncoveredArg;
6065
+ Sema::FormatArgumentPassingKind APK;
6066
+ FormatStringType Type;
6067
+ VariadicCallType CallType;
6068
+ unsigned format_idx;
6069
+ unsigned firstDataArg;
6070
+ bool InFunctionCall;
6071
+ bool IgnoreStringsWithoutSpecifiers;
6072
+
6073
+ FormatStringFinder(Sema &S, const StringLiteral *ReferenceFormatString,
6074
+ ArrayRef<const Expr *> Args,
6075
+ llvm::SmallBitVector &CheckedVarArgs,
6076
+ UncoveredArgHandler &UncoveredArg,
6077
+ Sema::FormatArgumentPassingKind APK,
6078
+ FormatStringType Type,
6079
+ VariadicCallType CallType, unsigned format_idx,
6080
+ unsigned firstDataArg, bool InFunctionCall)
6081
+ : S(S), ReferenceFormatString(ReferenceFormatString), Args(Args),
6082
+ CheckedVarArgs(CheckedVarArgs), UncoveredArg(UncoveredArg), APK(APK),
6083
+ Type(Type), CallType(CallType), format_idx(format_idx),
6084
+ firstDataArg(firstDataArg), InFunctionCall(InFunctionCall),
6085
+ IgnoreStringsWithoutSpecifiers(false) {}
6086
+
6087
+ /// Attempt to fold \c E into a constant string that \c checkFormatStringExpr
6088
+ /// can use. If \c E folds to a string literal, that string literal will be
6089
+ /// used for diagnostics. If \c E has a constant string value but it does not
6090
+ /// fold to a literal (for instance, ("%"s + "i"s).c_str() constant-folds to
6091
+ /// "%i"), a <scratch space> pseudo-source file will be allocated, containing
6092
+ /// a string literal representation of the constant string, and format
6093
+ /// diagnostics will point to it.
6094
+ static StringLiteralConstantEvaluationResult
6095
+ EvaluateStringAndCreateLiteral(Sema &S, const Expr *E,
6096
+ bool IsConstantEvaluation,
6097
+ const StringLiteral *&SL, uint64_t &Offset);
6098
+
6099
+ StringLiteralCheckType
6100
+ checkEvaluateLiteral(const Expr *E, llvm::APSInt Offset,
6101
+ bool IsConstantInitialization = false);
6102
+ StringLiteralCheckType check(const Expr *E, llvm::APSInt Offset);
6103
+
6104
+ public:
6105
+ // Determine if an expression is a string literal or constant string.
6106
+ // If this function returns false on the arguments to a function expecting a
6107
+ // format string, we will usually need to emit a warning.
6108
+ // True string literals are then checked by CheckFormatString.
6109
+ static StringLiteralCheckType
6110
+ check(Sema &S, const StringLiteral *ReferenceFormatString,
6111
+ ArrayRef<const Expr *> Args, unsigned FormatIdx, unsigned FirstDataArg,
6112
+ Sema::FormatArgumentPassingKind APK, FormatStringType FST,
6113
+ VariadicCallType CallType, llvm::SmallBitVector &CheckedVarArgs,
6114
+ UncoveredArgHandler &UncoveredArg) {
6115
+ FormatStringFinder Finder(S, ReferenceFormatString, Args, CheckedVarArgs,
6116
+ UncoveredArg, APK, FST, CallType, FormatIdx,
6117
+ FirstDataArg, true);
6118
+ const Expr *E = Args[FormatIdx];
6119
+ return S.isConstantEvaluatedContext()
6120
+ ? Finder.checkEvaluateLiteral(E, llvm::APSInt(64, false) = 0)
6121
+ : Finder.check(E, llvm::APSInt(64, false) = 0);
6122
+ }
6057
6123
};
6058
6124
6059
- /// Attempt to fold \c E into a constant string that \c checkFormatStringExpr
6060
- /// can use. If \c E folds to a string literal, that string literal will be used
6061
- /// for diagnostics. If \c E has a constant string value but it does not fold to
6062
- /// a literal (for instance, ("%"s + "i"s).c_str() constant-folds to "%i"), a
6063
- /// <scratch space> pseudo-source file will be allocated, containing a string
6064
- /// literal representation of the constant string, and format diagnostics will
6065
- /// point to it.
6066
- static StringLiteralConstantEvaluationResult
6067
- EvaluateStringAndCreateLiteral(Sema &S, const Expr *E, const StringLiteral *&SL,
6068
- uint64_t &Offset);
6069
-
6070
- // Determine if an expression is a string literal or constant string.
6071
- // If this function returns false on the arguments to a function expecting a
6072
- // format string, we will usually need to emit a warning.
6073
- // True string literals are then checked by CheckFormatString.
6074
- static StringLiteralCheckType checkFormatStringExpr(
6075
- Sema &S, const StringLiteral *ReferenceFormatString, const Expr *E,
6076
- ArrayRef<const Expr *> Args, Sema::FormatArgumentPassingKind APK,
6077
- unsigned format_idx, unsigned firstDataArg, FormatStringType Type,
6078
- VariadicCallType CallType, bool InFunctionCall,
6079
- llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
6080
- llvm::APSInt Offset, bool IgnoreStringsWithoutSpecifiers = false) {
6081
- if (S.isConstantEvaluatedContext())
6082
- return SLCT_NotALiteral;
6083
- tryAgain:
6125
+ StringLiteralCheckType FormatStringFinder::check(const Expr *E,
6126
+ llvm::APSInt Offset) {
6084
6127
assert(Offset.isSigned() && "invalid offset");
6085
6128
6086
6129
if (E->isTypeDependent() || E->isValueDependent())
@@ -6096,9 +6139,14 @@ static StringLiteralCheckType checkFormatStringExpr(
6096
6139
return SLCT_UncheckedLiteral;
6097
6140
6098
6141
switch (E->getStmtClass()) {
6099
- case Stmt::InitListExprClass:
6100
- // try to constant-evaluate the string
6101
- break;
6142
+ case Stmt::InitListExprClass: {
6143
+ const InitListExpr *InitList = cast<InitListExpr>(E);
6144
+ // Look through initializers like const char c[] = { "foo" }
6145
+ if (InitList->isStringLiteralInit())
6146
+ return check(InitList->getInit(0), Offset);
6147
+
6148
+ return checkEvaluateLiteral(E, Offset);
6149
+ }
6102
6150
6103
6151
case Stmt::BinaryConditionalOperatorClass:
6104
6152
case Stmt::ConditionalOperatorClass: {
@@ -6113,8 +6161,7 @@ static StringLiteralCheckType checkFormatStringExpr(
6113
6161
bool CheckLeft = true, CheckRight = true;
6114
6162
6115
6163
bool Cond;
6116
- if (C->getCond()->EvaluateAsBooleanCondition(
6117
- Cond, S.getASTContext(), S.isConstantEvaluatedContext())) {
6164
+ if (C->getCond()->EvaluateAsBooleanCondition(Cond, S.getASTContext())) {
6118
6165
if (Cond)
6119
6166
CheckRight = false;
6120
6167
else
@@ -6129,31 +6176,22 @@ static StringLiteralCheckType checkFormatStringExpr(
6129
6176
if (!CheckLeft)
6130
6177
Left = SLCT_UncheckedLiteral;
6131
6178
else {
6132
- Left = checkFormatStringExpr(
6133
- S, ReferenceFormatString, C->getTrueExpr(), Args, APK, format_idx,
6134
- firstDataArg, Type, CallType, InFunctionCall, CheckedVarArgs,
6135
- UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers);
6179
+ Left = check(C->getTrueExpr(), Offset);
6136
6180
if (Left == SLCT_NotALiteral || !CheckRight) {
6137
6181
return Left;
6138
6182
}
6139
6183
}
6140
6184
6141
- StringLiteralCheckType Right = checkFormatStringExpr(
6142
- S, ReferenceFormatString, C->getFalseExpr(), Args, APK, format_idx,
6143
- firstDataArg, Type, CallType, InFunctionCall, CheckedVarArgs,
6144
- UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers);
6145
-
6185
+ StringLiteralCheckType Right = check(C->getFalseExpr(), Offset);
6146
6186
return (CheckLeft && Left < Right) ? Left : Right;
6147
6187
}
6148
6188
6149
6189
case Stmt::ImplicitCastExprClass:
6150
- E = cast<ImplicitCastExpr>(E)->getSubExpr();
6151
- goto tryAgain;
6190
+ return check(cast<ImplicitCastExpr>(E)->getSubExpr(), Offset);
6152
6191
6153
6192
case Stmt::OpaqueValueExprClass:
6154
6193
if (const Expr *src = cast<OpaqueValueExpr>(E)->getSourceExpr()) {
6155
- E = src;
6156
- goto tryAgain;
6194
+ return check(src, Offset);
6157
6195
}
6158
6196
return SLCT_NotALiteral;
6159
6197
@@ -6184,15 +6222,17 @@ static StringLiteralCheckType checkFormatStringExpr(
6184
6222
}
6185
6223
6186
6224
if (isConstant) {
6187
- if (const Expr *Init = VD->getAnyInitializer()) {
6188
- // Look through initializers like const char c[] = { "foo" }
6189
- if (const InitListExpr *InitList = dyn_cast<InitListExpr>(Init)) {
6190
- if (InitList->isStringLiteralInit())
6191
- Init = InitList->getInit(0)->IgnoreParenImpCasts();
6192
- }
6225
+ // If the variable has C++ constant initialization, we need to jump out
6226
+ // of format string handling ad-hoc constant evaluation and follow the
6227
+ // standard rules.
6228
+ const Expr *Init = VD->getInit();
6229
+ if (Init && VD->hasConstantInitialization()) {
6230
+ return checkEvaluateLiteral(Init, Offset, true);
6231
+ }
6232
+ Init = VD->getAnyInitializer();
6233
+ if (Init) {
6193
6234
InFunctionCall = false;
6194
- E = Init;
6195
- goto tryAgain;
6235
+ return check(Init, Offset);
6196
6236
}
6197
6237
}
6198
6238
@@ -6265,9 +6305,8 @@ static StringLiteralCheckType checkFormatStringExpr(
6265
6305
}
6266
6306
return SLCT_UncheckedLiteral;
6267
6307
}
6268
- E = PVFormatMatches->getFormatString();
6269
6308
InFunctionCall = false;
6270
- goto tryAgain ;
6309
+ return check(PVFormatMatches->getFormatString(), Offset) ;
6271
6310
}
6272
6311
}
6273
6312
@@ -6319,10 +6358,7 @@ static StringLiteralCheckType checkFormatStringExpr(
6319
6358
StringLiteralCheckType CommonResult;
6320
6359
for (const auto *FA : ND->specific_attrs<FormatArgAttr>()) {
6321
6360
const Expr *Arg = CE->getArg(FA->getFormatIdx().getASTIndex());
6322
- StringLiteralCheckType Result = checkFormatStringExpr(
6323
- S, ReferenceFormatString, Arg, Args, APK, format_idx, firstDataArg,
6324
- Type, CallType, InFunctionCall, CheckedVarArgs, UncoveredArg,
6325
- Offset, IgnoreStringsWithoutSpecifiers);
6361
+ StringLiteralCheckType Result = check(Arg, Offset);
6326
6362
if (IsFirst) {
6327
6363
CommonResult = Result;
6328
6364
IsFirst = false;
@@ -6335,13 +6371,11 @@ static StringLiteralCheckType checkFormatStringExpr(
6335
6371
unsigned BuiltinID = FD->getBuiltinID();
6336
6372
if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString ||
6337
6373
BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) {
6338
- E = CE->getArg(0);
6339
- goto tryAgain;
6374
+ return check(CE->getArg(0), Offset);
6340
6375
}
6341
6376
}
6342
6377
}
6343
- // try to constant-evaluate the string
6344
- break;
6378
+ return checkEvaluateLiteral(E, Offset);
6345
6379
}
6346
6380
case Stmt::ObjCMessageExprClass: {
6347
6381
const auto *ME = cast<ObjCMessageExpr>(E);
@@ -6362,8 +6396,7 @@ static StringLiteralCheckType checkFormatStringExpr(
6362
6396
IgnoreStringsWithoutSpecifiers = true;
6363
6397
}
6364
6398
6365
- E = ME->getArg(FA->getFormatIdx().getASTIndex());
6366
- goto tryAgain;
6399
+ return check(ME->getArg(FA->getFormatIdx().getASTIndex()), Offset);
6367
6400
}
6368
6401
}
6369
6402
@@ -6414,19 +6447,16 @@ static StringLiteralCheckType checkFormatStringExpr(
6414
6447
if (LIsInt) {
6415
6448
if (BinOpKind == BO_Add) {
6416
6449
sumOffsets(Offset, LResult.Val.getInt(), BinOpKind, RIsInt);
6417
- E = BinOp->getRHS();
6418
- goto tryAgain;
6450
+ return check(BinOp->getRHS(), Offset);
6419
6451
}
6420
6452
} else {
6421
6453
sumOffsets(Offset, RResult.Val.getInt(), BinOpKind, RIsInt);
6422
- E = BinOp->getLHS();
6423
- goto tryAgain;
6454
+ return check(BinOp->getLHS(), Offset);
6424
6455
}
6425
6456
}
6426
6457
}
6427
6458
6428
- // try to constant-evaluate the string
6429
- break;
6459
+ return checkEvaluateLiteral(E, Offset);
6430
6460
}
6431
6461
case Stmt::UnaryOperatorClass: {
6432
6462
const UnaryOperator *UnaOp = cast<UnaryOperator>(E);
@@ -6438,23 +6468,25 @@ static StringLiteralCheckType checkFormatStringExpr(
6438
6468
S.isConstantEvaluatedContext())) {
6439
6469
sumOffsets(Offset, IndexResult.Val.getInt(), BO_Add,
6440
6470
/*RHS is int*/ true);
6441
- E = ASE->getBase();
6442
- goto tryAgain;
6471
+ return check(ASE->getBase(), Offset);
6443
6472
}
6444
6473
}
6445
6474
6446
- // try to constant-evaluate the string
6447
- break;
6475
+ return checkEvaluateLiteral(E, Offset);
6448
6476
}
6449
6477
6450
6478
default:
6451
- // try to constant-evaluate the string
6452
- break;
6479
+ return checkEvaluateLiteral(E, Offset);
6453
6480
}
6481
+ }
6454
6482
6483
+ StringLiteralCheckType
6484
+ FormatStringFinder::checkEvaluateLiteral(const Expr *E, llvm::APSInt Offset,
6485
+ bool IsConstantEvaluation) {
6455
6486
uint64_t EvalOffset = 0;
6456
6487
const StringLiteral *FakeLiteral = nullptr;
6457
- switch (EvaluateStringAndCreateLiteral(S, E, FakeLiteral, EvalOffset)) {
6488
+ switch (EvaluateStringAndCreateLiteral(S, E, IsConstantEvaluation,
6489
+ FakeLiteral, EvalOffset)) {
6458
6490
case SLCER_NotEvaluated:
6459
6491
return SLCT_NotALiteral;
6460
6492
@@ -6470,18 +6502,20 @@ static StringLiteralCheckType checkFormatStringExpr(
6470
6502
6471
6503
case SLCER_Evaluated:
6472
6504
InFunctionCall = false;
6473
- E = FakeLiteral;
6474
- Offset = EvalOffset;
6475
- goto tryAgain ;
6505
+ if (EvalOffset)
6506
+ sumOffsets( Offset, llvm::APSInt(64, false) = EvalOffset, BO_Add, true) ;
6507
+ return check(FakeLiteral, Offset) ;
6476
6508
}
6477
6509
}
6478
6510
6479
- static StringLiteralConstantEvaluationResult
6480
- EvaluateStringAndCreateLiteral(Sema &S, const Expr *E, const StringLiteral *&SL,
6481
- uint64_t &Offset) {
6511
+ FormatStringFinder::StringLiteralConstantEvaluationResult
6512
+ FormatStringFinder::EvaluateStringAndCreateLiteral(Sema &S, const Expr *E,
6513
+ bool IsConstantEvaluation,
6514
+ const StringLiteral *&SL,
6515
+ uint64_t &Offset) {
6482
6516
// As a last resort, try to constant-evaluate the format string.
6483
6517
bool HasNul;
6484
- auto SER = E->tryEvaluateString(S.Context, &HasNul);
6518
+ auto SER = E->tryEvaluateString(S.Context, &HasNul, IsConstantEvaluation );
6485
6519
if (!SER)
6486
6520
return SLCER_NotEvaluated;
6487
6521
if (!HasNul)
@@ -6624,11 +6658,9 @@ bool Sema::CheckFormatArguments(ArrayRef<const Expr *> Args,
6624
6658
// strings by the compiler and thereby (2) can practically remove the source
6625
6659
// of many format string exploits.
6626
6660
UncoveredArgHandler UncoveredArg;
6627
- StringLiteralCheckType CT = checkFormatStringExpr(
6628
- *this, ReferenceFormatString, OrigFormatExpr, Args, APK, format_idx,
6629
- firstDataArg, Type, CallType,
6630
- /*IsFunctionCall*/ true, CheckedVarArgs, UncoveredArg,
6631
- /*no string offset*/ llvm::APSInt(64, false) = 0);
6661
+ StringLiteralCheckType CT = FormatStringFinder::check(
6662
+ *this, ReferenceFormatString, Args, format_idx, firstDataArg, APK, Type,
6663
+ CallType, CheckedVarArgs, UncoveredArg);
6632
6664
6633
6665
// Generate a diagnostic where an uncovered argument is detected.
6634
6666
if (UncoveredArg.hasUncoveredArg()) {
0 commit comments