@@ -6050,37 +6050,80 @@ static void CheckFormatString(
60506050 llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
60516051 bool IgnoreStringsWithoutSpecifiers);
60526052
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+ }
60576123};
60586124
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) {
60846127 assert(Offset.isSigned() && "invalid offset");
60856128
60866129 if (E->isTypeDependent() || E->isValueDependent())
@@ -6096,9 +6139,14 @@ static StringLiteralCheckType checkFormatStringExpr(
60966139 return SLCT_UncheckedLiteral;
60976140
60986141 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+ }
61026150
61036151 case Stmt::BinaryConditionalOperatorClass:
61046152 case Stmt::ConditionalOperatorClass: {
@@ -6113,8 +6161,7 @@ static StringLiteralCheckType checkFormatStringExpr(
61136161 bool CheckLeft = true, CheckRight = true;
61146162
61156163 bool Cond;
6116- if (C->getCond()->EvaluateAsBooleanCondition(
6117- Cond, S.getASTContext(), S.isConstantEvaluatedContext())) {
6164+ if (C->getCond()->EvaluateAsBooleanCondition(Cond, S.getASTContext())) {
61186165 if (Cond)
61196166 CheckRight = false;
61206167 else
@@ -6129,31 +6176,22 @@ static StringLiteralCheckType checkFormatStringExpr(
61296176 if (!CheckLeft)
61306177 Left = SLCT_UncheckedLiteral;
61316178 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);
61366180 if (Left == SLCT_NotALiteral || !CheckRight) {
61376181 return Left;
61386182 }
61396183 }
61406184
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);
61466186 return (CheckLeft && Left < Right) ? Left : Right;
61476187 }
61486188
61496189 case Stmt::ImplicitCastExprClass:
6150- E = cast<ImplicitCastExpr>(E)->getSubExpr();
6151- goto tryAgain;
6190+ return check(cast<ImplicitCastExpr>(E)->getSubExpr(), Offset);
61526191
61536192 case Stmt::OpaqueValueExprClass:
61546193 if (const Expr *src = cast<OpaqueValueExpr>(E)->getSourceExpr()) {
6155- E = src;
6156- goto tryAgain;
6194+ return check(src, Offset);
61576195 }
61586196 return SLCT_NotALiteral;
61596197
@@ -6184,15 +6222,17 @@ static StringLiteralCheckType checkFormatStringExpr(
61846222 }
61856223
61866224 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) {
61936234 InFunctionCall = false;
6194- E = Init;
6195- goto tryAgain;
6235+ return check(Init, Offset);
61966236 }
61976237 }
61986238
@@ -6265,9 +6305,8 @@ static StringLiteralCheckType checkFormatStringExpr(
62656305 }
62666306 return SLCT_UncheckedLiteral;
62676307 }
6268- E = PVFormatMatches->getFormatString();
62696308 InFunctionCall = false;
6270- goto tryAgain ;
6309+ return check(PVFormatMatches->getFormatString(), Offset) ;
62716310 }
62726311 }
62736312
@@ -6319,10 +6358,7 @@ static StringLiteralCheckType checkFormatStringExpr(
63196358 StringLiteralCheckType CommonResult;
63206359 for (const auto *FA : ND->specific_attrs<FormatArgAttr>()) {
63216360 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);
63266362 if (IsFirst) {
63276363 CommonResult = Result;
63286364 IsFirst = false;
@@ -6335,13 +6371,11 @@ static StringLiteralCheckType checkFormatStringExpr(
63356371 unsigned BuiltinID = FD->getBuiltinID();
63366372 if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString ||
63376373 BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) {
6338- E = CE->getArg(0);
6339- goto tryAgain;
6374+ return check(CE->getArg(0), Offset);
63406375 }
63416376 }
63426377 }
6343- // try to constant-evaluate the string
6344- break;
6378+ return checkEvaluateLiteral(E, Offset);
63456379 }
63466380 case Stmt::ObjCMessageExprClass: {
63476381 const auto *ME = cast<ObjCMessageExpr>(E);
@@ -6362,8 +6396,7 @@ static StringLiteralCheckType checkFormatStringExpr(
63626396 IgnoreStringsWithoutSpecifiers = true;
63636397 }
63646398
6365- E = ME->getArg(FA->getFormatIdx().getASTIndex());
6366- goto tryAgain;
6399+ return check(ME->getArg(FA->getFormatIdx().getASTIndex()), Offset);
63676400 }
63686401 }
63696402
@@ -6414,19 +6447,16 @@ static StringLiteralCheckType checkFormatStringExpr(
64146447 if (LIsInt) {
64156448 if (BinOpKind == BO_Add) {
64166449 sumOffsets(Offset, LResult.Val.getInt(), BinOpKind, RIsInt);
6417- E = BinOp->getRHS();
6418- goto tryAgain;
6450+ return check(BinOp->getRHS(), Offset);
64196451 }
64206452 } else {
64216453 sumOffsets(Offset, RResult.Val.getInt(), BinOpKind, RIsInt);
6422- E = BinOp->getLHS();
6423- goto tryAgain;
6454+ return check(BinOp->getLHS(), Offset);
64246455 }
64256456 }
64266457 }
64276458
6428- // try to constant-evaluate the string
6429- break;
6459+ return checkEvaluateLiteral(E, Offset);
64306460 }
64316461 case Stmt::UnaryOperatorClass: {
64326462 const UnaryOperator *UnaOp = cast<UnaryOperator>(E);
@@ -6438,23 +6468,25 @@ static StringLiteralCheckType checkFormatStringExpr(
64386468 S.isConstantEvaluatedContext())) {
64396469 sumOffsets(Offset, IndexResult.Val.getInt(), BO_Add,
64406470 /*RHS is int*/ true);
6441- E = ASE->getBase();
6442- goto tryAgain;
6471+ return check(ASE->getBase(), Offset);
64436472 }
64446473 }
64456474
6446- // try to constant-evaluate the string
6447- break;
6475+ return checkEvaluateLiteral(E, Offset);
64486476 }
64496477
64506478 default:
6451- // try to constant-evaluate the string
6452- break;
6479+ return checkEvaluateLiteral(E, Offset);
64536480 }
6481+ }
64546482
6483+ StringLiteralCheckType
6484+ FormatStringFinder::checkEvaluateLiteral(const Expr *E, llvm::APSInt Offset,
6485+ bool IsConstantEvaluation) {
64556486 uint64_t EvalOffset = 0;
64566487 const StringLiteral *FakeLiteral = nullptr;
6457- switch (EvaluateStringAndCreateLiteral(S, E, FakeLiteral, EvalOffset)) {
6488+ switch (EvaluateStringAndCreateLiteral(S, E, IsConstantEvaluation,
6489+ FakeLiteral, EvalOffset)) {
64586490 case SLCER_NotEvaluated:
64596491 return SLCT_NotALiteral;
64606492
@@ -6470,18 +6502,20 @@ static StringLiteralCheckType checkFormatStringExpr(
64706502
64716503 case SLCER_Evaluated:
64726504 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) ;
64766508 }
64776509}
64786510
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) {
64826516 // As a last resort, try to constant-evaluate the format string.
64836517 bool HasNul;
6484- auto SER = E->tryEvaluateString(S.Context, &HasNul);
6518+ auto SER = E->tryEvaluateString(S.Context, &HasNul, IsConstantEvaluation );
64856519 if (!SER)
64866520 return SLCER_NotEvaluated;
64876521 if (!HasNul)
@@ -6624,11 +6658,9 @@ bool Sema::CheckFormatArguments(ArrayRef<const Expr *> Args,
66246658 // strings by the compiler and thereby (2) can practically remove the source
66256659 // of many format string exploits.
66266660 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);
66326664
66336665 // Generate a diagnostic where an uncovered argument is detected.
66346666 if (UncoveredArg.hasUncoveredArg()) {
0 commit comments