@@ -5938,37 +5938,80 @@ static void CheckFormatString(
5938
5938
llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
5939
5939
bool IgnoreStringsWithoutSpecifiers);
5940
5940
5941
- enum StringLiteralConstantEvaluationResult {
5942
- SLCER_NotEvaluated,
5943
- SLCER_NotNullTerminated,
5944
- SLCER_Evaluated,
5941
+ class FormatStringFinder {
5942
+ enum StringLiteralConstantEvaluationResult {
5943
+ SLCER_NotEvaluated,
5944
+ SLCER_NotNullTerminated,
5945
+ SLCER_Evaluated,
5946
+ };
5947
+
5948
+ Sema &S;
5949
+ const StringLiteral *ReferenceFormatString;
5950
+ ArrayRef<const Expr *> Args;
5951
+ llvm::SmallBitVector &CheckedVarArgs;
5952
+ UncoveredArgHandler &UncoveredArg;
5953
+ Sema::FormatArgumentPassingKind APK;
5954
+ Sema::FormatStringType Type;
5955
+ Sema::VariadicCallType CallType;
5956
+ unsigned format_idx;
5957
+ unsigned firstDataArg;
5958
+ bool InFunctionCall;
5959
+ bool IgnoreStringsWithoutSpecifiers;
5960
+
5961
+ FormatStringFinder(Sema &S, const StringLiteral *ReferenceFormatString,
5962
+ ArrayRef<const Expr *> Args,
5963
+ llvm::SmallBitVector &CheckedVarArgs,
5964
+ UncoveredArgHandler &UncoveredArg,
5965
+ Sema::FormatArgumentPassingKind APK,
5966
+ Sema::FormatStringType Type,
5967
+ Sema::VariadicCallType CallType, unsigned format_idx,
5968
+ unsigned firstDataArg, bool InFunctionCall)
5969
+ : S(S), ReferenceFormatString(ReferenceFormatString), Args(Args),
5970
+ CheckedVarArgs(CheckedVarArgs), UncoveredArg(UncoveredArg), APK(APK),
5971
+ Type(Type), CallType(CallType), format_idx(format_idx),
5972
+ firstDataArg(firstDataArg), InFunctionCall(InFunctionCall),
5973
+ IgnoreStringsWithoutSpecifiers(false) {}
5974
+
5975
+ /// Attempt to fold \c E into a constant string that \c checkFormatStringExpr
5976
+ /// can use. If \c E folds to a string literal, that string literal will be
5977
+ /// used for diagnostics. If \c E has a constant string value but it does not
5978
+ /// fold to a literal (for instance, ("%"s + "i"s).c_str() constant-folds to
5979
+ /// "%i"), a <scratch space> pseudo-source file will be allocated, containing
5980
+ /// a string literal representation of the constant string, and format
5981
+ /// diagnostics will point to it.
5982
+ static StringLiteralConstantEvaluationResult
5983
+ EvaluateStringAndCreateLiteral(Sema &S, const Expr *E,
5984
+ bool IsConstantEvaluation,
5985
+ const StringLiteral *&SL, uint64_t &Offset);
5986
+
5987
+ StringLiteralCheckType
5988
+ checkEvaluateLiteral(const Expr *E, llvm::APSInt Offset,
5989
+ bool IsConstantInitialization = false);
5990
+ StringLiteralCheckType check(const Expr *E, llvm::APSInt Offset);
5991
+
5992
+ public:
5993
+ // Determine if an expression is a string literal or constant string.
5994
+ // If this function returns false on the arguments to a function expecting a
5995
+ // format string, we will usually need to emit a warning.
5996
+ // True string literals are then checked by CheckFormatString.
5997
+ static StringLiteralCheckType
5998
+ check(Sema &S, const StringLiteral *ReferenceFormatString,
5999
+ ArrayRef<const Expr *> Args, unsigned FormatIdx, unsigned FirstDataArg,
6000
+ Sema::FormatArgumentPassingKind APK, Sema::FormatStringType FST,
6001
+ Sema::VariadicCallType CallType, llvm::SmallBitVector &CheckedVarArgs,
6002
+ UncoveredArgHandler &UncoveredArg) {
6003
+ FormatStringFinder Finder(S, ReferenceFormatString, Args, CheckedVarArgs,
6004
+ UncoveredArg, APK, FST, CallType, FormatIdx,
6005
+ FirstDataArg, true);
6006
+ const Expr *E = Args[FormatIdx];
6007
+ return S.isConstantEvaluatedContext()
6008
+ ? Finder.checkEvaluateLiteral(E, llvm::APSInt(64, false) = 0)
6009
+ : Finder.check(E, llvm::APSInt(64, false) = 0);
6010
+ }
5945
6011
};
5946
6012
5947
- /// Attempt to fold \c E into a constant string that \c checkFormatStringExpr
5948
- /// can use. If \c E folds to a string literal, that string literal will be used
5949
- /// for diagnostics. If \c E has a constant string value but it does not fold to
5950
- /// a literal (for instance, ("%"s + "i"s).c_str() constant-folds to "%i"), a
5951
- /// <scratch space> pseudo-source file will be allocated, containing a string
5952
- /// literal representation of the constant string, and format diagnostics will
5953
- /// point to it.
5954
- static StringLiteralConstantEvaluationResult
5955
- EvaluateStringAndCreateLiteral(Sema &S, const Expr *E, const StringLiteral *&SL,
5956
- uint64_t &Offset);
5957
-
5958
- // Determine if an expression is a string literal or constant string.
5959
- // If this function returns false on the arguments to a function expecting a
5960
- // format string, we will usually need to emit a warning.
5961
- // True string literals are then checked by CheckFormatString.
5962
- static StringLiteralCheckType checkFormatStringExpr(
5963
- Sema &S, const StringLiteral *ReferenceFormatString, const Expr *E,
5964
- ArrayRef<const Expr *> Args, Sema::FormatArgumentPassingKind APK,
5965
- unsigned format_idx, unsigned firstDataArg, Sema::FormatStringType Type,
5966
- Sema::VariadicCallType CallType, bool InFunctionCall,
5967
- llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
5968
- llvm::APSInt Offset, bool IgnoreStringsWithoutSpecifiers = false) {
5969
- if (S.isConstantEvaluatedContext())
5970
- return SLCT_NotALiteral;
5971
- tryAgain:
6013
+ StringLiteralCheckType FormatStringFinder::check(const Expr *E,
6014
+ llvm::APSInt Offset) {
5972
6015
assert(Offset.isSigned() && "invalid offset");
5973
6016
5974
6017
if (E->isTypeDependent() || E->isValueDependent())
@@ -5984,9 +6027,14 @@ static StringLiteralCheckType checkFormatStringExpr(
5984
6027
return SLCT_UncheckedLiteral;
5985
6028
5986
6029
switch (E->getStmtClass()) {
5987
- case Stmt::InitListExprClass:
5988
- // try to constant-evaluate the string
5989
- break;
6030
+ case Stmt::InitListExprClass: {
6031
+ const InitListExpr *InitList = cast<InitListExpr>(E);
6032
+ // Look through initializers like const char c[] = { "foo" }
6033
+ if (InitList->isStringLiteralInit())
6034
+ return check(InitList->getInit(0), Offset);
6035
+
6036
+ return checkEvaluateLiteral(E, Offset);
6037
+ }
5990
6038
5991
6039
case Stmt::BinaryConditionalOperatorClass:
5992
6040
case Stmt::ConditionalOperatorClass: {
@@ -6001,8 +6049,7 @@ static StringLiteralCheckType checkFormatStringExpr(
6001
6049
bool CheckLeft = true, CheckRight = true;
6002
6050
6003
6051
bool Cond;
6004
- if (C->getCond()->EvaluateAsBooleanCondition(
6005
- Cond, S.getASTContext(), S.isConstantEvaluatedContext())) {
6052
+ if (C->getCond()->EvaluateAsBooleanCondition(Cond, S.getASTContext())) {
6006
6053
if (Cond)
6007
6054
CheckRight = false;
6008
6055
else
@@ -6017,31 +6064,22 @@ static StringLiteralCheckType checkFormatStringExpr(
6017
6064
if (!CheckLeft)
6018
6065
Left = SLCT_UncheckedLiteral;
6019
6066
else {
6020
- Left = checkFormatStringExpr(
6021
- S, ReferenceFormatString, C->getTrueExpr(), Args, APK, format_idx,
6022
- firstDataArg, Type, CallType, InFunctionCall, CheckedVarArgs,
6023
- UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers);
6067
+ Left = check(C->getTrueExpr(), Offset);
6024
6068
if (Left == SLCT_NotALiteral || !CheckRight) {
6025
6069
return Left;
6026
6070
}
6027
6071
}
6028
6072
6029
- StringLiteralCheckType Right = checkFormatStringExpr(
6030
- S, ReferenceFormatString, C->getFalseExpr(), Args, APK, format_idx,
6031
- firstDataArg, Type, CallType, InFunctionCall, CheckedVarArgs,
6032
- UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers);
6033
-
6073
+ StringLiteralCheckType Right = check(C->getFalseExpr(), Offset);
6034
6074
return (CheckLeft && Left < Right) ? Left : Right;
6035
6075
}
6036
6076
6037
6077
case Stmt::ImplicitCastExprClass:
6038
- E = cast<ImplicitCastExpr>(E)->getSubExpr();
6039
- goto tryAgain;
6078
+ return check(cast<ImplicitCastExpr>(E)->getSubExpr(), Offset);
6040
6079
6041
6080
case Stmt::OpaqueValueExprClass:
6042
6081
if (const Expr *src = cast<OpaqueValueExpr>(E)->getSourceExpr()) {
6043
- E = src;
6044
- goto tryAgain;
6082
+ return check(src, Offset);
6045
6083
}
6046
6084
return SLCT_NotALiteral;
6047
6085
@@ -6072,15 +6110,17 @@ static StringLiteralCheckType checkFormatStringExpr(
6072
6110
}
6073
6111
6074
6112
if (isConstant) {
6075
- if (const Expr *Init = VD->getAnyInitializer()) {
6076
- // Look through initializers like const char c[] = { "foo" }
6077
- if (const InitListExpr *InitList = dyn_cast<InitListExpr>(Init)) {
6078
- if (InitList->isStringLiteralInit())
6079
- Init = InitList->getInit(0)->IgnoreParenImpCasts();
6080
- }
6113
+ // If the variable has C++ constant initialization, we need to jump out
6114
+ // of format string handling ad-hoc constant evaluation and follow the
6115
+ // standard rules.
6116
+ const Expr *Init = VD->getInit();
6117
+ if (Init && VD->hasConstantInitialization()) {
6118
+ return checkEvaluateLiteral(Init, Offset, true);
6119
+ }
6120
+ Init = VD->getAnyInitializer();
6121
+ if (Init) {
6081
6122
InFunctionCall = false;
6082
- E = Init;
6083
- goto tryAgain;
6123
+ return check(Init, Offset);
6084
6124
}
6085
6125
}
6086
6126
@@ -6153,9 +6193,8 @@ static StringLiteralCheckType checkFormatStringExpr(
6153
6193
}
6154
6194
return SLCT_UncheckedLiteral;
6155
6195
}
6156
- E = PVFormatMatches->getFormatString();
6157
6196
InFunctionCall = false;
6158
- goto tryAgain ;
6197
+ return check(PVFormatMatches->getFormatString(), Offset) ;
6159
6198
}
6160
6199
}
6161
6200
@@ -6207,10 +6246,7 @@ static StringLiteralCheckType checkFormatStringExpr(
6207
6246
StringLiteralCheckType CommonResult;
6208
6247
for (const auto *FA : ND->specific_attrs<FormatArgAttr>()) {
6209
6248
const Expr *Arg = CE->getArg(FA->getFormatIdx().getASTIndex());
6210
- StringLiteralCheckType Result = checkFormatStringExpr(
6211
- S, ReferenceFormatString, Arg, Args, APK, format_idx, firstDataArg,
6212
- Type, CallType, InFunctionCall, CheckedVarArgs, UncoveredArg,
6213
- Offset, IgnoreStringsWithoutSpecifiers);
6249
+ StringLiteralCheckType Result = check(Arg, Offset);
6214
6250
if (IsFirst) {
6215
6251
CommonResult = Result;
6216
6252
IsFirst = false;
@@ -6223,13 +6259,11 @@ static StringLiteralCheckType checkFormatStringExpr(
6223
6259
unsigned BuiltinID = FD->getBuiltinID();
6224
6260
if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString ||
6225
6261
BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) {
6226
- E = CE->getArg(0);
6227
- goto tryAgain;
6262
+ return check(CE->getArg(0), Offset);
6228
6263
}
6229
6264
}
6230
6265
}
6231
- // try to constant-evaluate the string
6232
- break;
6266
+ return checkEvaluateLiteral(E, Offset);
6233
6267
}
6234
6268
case Stmt::ObjCMessageExprClass: {
6235
6269
const auto *ME = cast<ObjCMessageExpr>(E);
@@ -6250,8 +6284,7 @@ static StringLiteralCheckType checkFormatStringExpr(
6250
6284
IgnoreStringsWithoutSpecifiers = true;
6251
6285
}
6252
6286
6253
- E = ME->getArg(FA->getFormatIdx().getASTIndex());
6254
- goto tryAgain;
6287
+ return check(ME->getArg(FA->getFormatIdx().getASTIndex()), Offset);
6255
6288
}
6256
6289
}
6257
6290
@@ -6302,19 +6335,16 @@ static StringLiteralCheckType checkFormatStringExpr(
6302
6335
if (LIsInt) {
6303
6336
if (BinOpKind == BO_Add) {
6304
6337
sumOffsets(Offset, LResult.Val.getInt(), BinOpKind, RIsInt);
6305
- E = BinOp->getRHS();
6306
- goto tryAgain;
6338
+ return check(BinOp->getRHS(), Offset);
6307
6339
}
6308
6340
} else {
6309
6341
sumOffsets(Offset, RResult.Val.getInt(), BinOpKind, RIsInt);
6310
- E = BinOp->getLHS();
6311
- goto tryAgain;
6342
+ return check(BinOp->getLHS(), Offset);
6312
6343
}
6313
6344
}
6314
6345
}
6315
6346
6316
- // try to constant-evaluate the string
6317
- break;
6347
+ return checkEvaluateLiteral(E, Offset);
6318
6348
}
6319
6349
case Stmt::UnaryOperatorClass: {
6320
6350
const UnaryOperator *UnaOp = cast<UnaryOperator>(E);
@@ -6326,23 +6356,25 @@ static StringLiteralCheckType checkFormatStringExpr(
6326
6356
S.isConstantEvaluatedContext())) {
6327
6357
sumOffsets(Offset, IndexResult.Val.getInt(), BO_Add,
6328
6358
/*RHS is int*/ true);
6329
- E = ASE->getBase();
6330
- goto tryAgain;
6359
+ return check(ASE->getBase(), Offset);
6331
6360
}
6332
6361
}
6333
6362
6334
- // try to constant-evaluate the string
6335
- break;
6363
+ return checkEvaluateLiteral(E, Offset);
6336
6364
}
6337
6365
6338
6366
default:
6339
- // try to constant-evaluate the string
6340
- break;
6367
+ return checkEvaluateLiteral(E, Offset);
6341
6368
}
6369
+ }
6342
6370
6371
+ StringLiteralCheckType
6372
+ FormatStringFinder::checkEvaluateLiteral(const Expr *E, llvm::APSInt Offset,
6373
+ bool IsConstantEvaluation) {
6343
6374
uint64_t EvalOffset = 0;
6344
6375
const StringLiteral *FakeLiteral = nullptr;
6345
- switch (EvaluateStringAndCreateLiteral(S, E, FakeLiteral, EvalOffset)) {
6376
+ switch (EvaluateStringAndCreateLiteral(S, E, IsConstantEvaluation,
6377
+ FakeLiteral, EvalOffset)) {
6346
6378
case SLCER_NotEvaluated:
6347
6379
return SLCT_NotALiteral;
6348
6380
@@ -6358,18 +6390,20 @@ static StringLiteralCheckType checkFormatStringExpr(
6358
6390
6359
6391
case SLCER_Evaluated:
6360
6392
InFunctionCall = false;
6361
- E = FakeLiteral;
6362
- Offset = EvalOffset;
6363
- goto tryAgain ;
6393
+ if (EvalOffset)
6394
+ sumOffsets( Offset, llvm::APSInt(64, false) = EvalOffset, BO_Add, true) ;
6395
+ return check(FakeLiteral, Offset) ;
6364
6396
}
6365
6397
}
6366
6398
6367
- static StringLiteralConstantEvaluationResult
6368
- EvaluateStringAndCreateLiteral(Sema &S, const Expr *E, const StringLiteral *&SL,
6369
- uint64_t &Offset) {
6399
+ FormatStringFinder::StringLiteralConstantEvaluationResult
6400
+ FormatStringFinder::EvaluateStringAndCreateLiteral(Sema &S, const Expr *E,
6401
+ bool IsConstantEvaluation,
6402
+ const StringLiteral *&SL,
6403
+ uint64_t &Offset) {
6370
6404
// As a last resort, try to constant-evaluate the format string.
6371
6405
bool HasNul;
6372
- auto SER = E->tryEvaluateString(S.Context, &HasNul);
6406
+ auto SER = E->tryEvaluateString(S.Context, &HasNul, IsConstantEvaluation );
6373
6407
if (!SER)
6374
6408
return SLCER_NotEvaluated;
6375
6409
if (!HasNul)
@@ -6511,11 +6545,9 @@ bool Sema::CheckFormatArguments(ArrayRef<const Expr *> Args,
6511
6545
// strings by the compiler and thereby (2) can practically remove the source
6512
6546
// of many format string exploits.
6513
6547
UncoveredArgHandler UncoveredArg;
6514
- StringLiteralCheckType CT = checkFormatStringExpr(
6515
- *this, ReferenceFormatString, OrigFormatExpr, Args, APK, format_idx,
6516
- firstDataArg, Type, CallType,
6517
- /*IsFunctionCall*/ true, CheckedVarArgs, UncoveredArg,
6518
- /*no string offset*/ llvm::APSInt(64, false) = 0);
6548
+ StringLiteralCheckType CT = FormatStringFinder::check(
6549
+ *this, ReferenceFormatString, Args, format_idx, firstDataArg, APK, Type,
6550
+ CallType, CheckedVarArgs, UncoveredArg);
6519
6551
6520
6552
// Generate a diagnostic where an uncovered argument is detected.
6521
6553
if (UncoveredArg.hasUncoveredArg()) {
0 commit comments