Skip to content

Commit 00dfaeb

Browse files
Fix (part of) the constexpr problem
1 parent 7b57085 commit 00dfaeb

File tree

4 files changed

+150
-106
lines changed

4 files changed

+150
-106
lines changed

clang/include/clang/AST/Expr.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,8 @@ class Expr : public ValueStmt {
809809
/// \c NullTerminated is supplied, it is set to whether there is at least one
810810
/// NUL character in the string.
811811
std::optional<StringEvalResult>
812-
tryEvaluateString(ASTContext &Ctx, bool *NullTerminated = nullptr) const;
812+
tryEvaluateString(ASTContext &Ctx, bool *NullTerminated = nullptr,
813+
bool InConstantContext = false) const;
813814

814815
/// Enumeration used to describe the kind of Null pointer constant
815816
/// returned from \c isNullPointerConstant().

clang/lib/AST/ExprConstant.cpp

+8-2
Original file line numberDiff line numberDiff line change
@@ -18081,12 +18081,18 @@ bool Expr::StringEvalResult::getStringLiteral(const StringLiteral *&SL,
1808118081
}
1808218082

1808318083
std::optional<Expr::StringEvalResult>
18084-
Expr::tryEvaluateString(ASTContext &Ctx, bool *NullTerminated) const {
18084+
Expr::tryEvaluateString(ASTContext &Ctx, bool *NullTerminated,
18085+
bool InConstantContext) const {
1808518086
if (NullTerminated)
1808618087
*NullTerminated = false;
1808718088

1808818089
Expr::EvalStatus Status;
18089-
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold);
18090+
EvalInfo Info(Ctx, Status,
18091+
(InConstantContext &&
18092+
(Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
18093+
? EvalInfo::EM_ConstantExpression
18094+
: EvalInfo::EM_ConstantFold);
18095+
Info.InConstantContext = InConstantContext;
1809018096
LValue String;
1809118097
QualType CharTy;
1809218098
if (!EvaluateStringAsLValue(Info, this, CharTy, String))

clang/lib/Sema/SemaChecking.cpp

+124-92
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)