Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions clang/lib/Analysis/UnsafeBufferUsage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -781,9 +781,25 @@ struct LibcFunNamePrefixSuffixParser {
}
};

// Constant fold a conditional expression 'cond ? A : B' to
// - 'A', if 'cond' has constant true value;
// - 'B', if 'cond' has constant false value.
static const Expr *tryConstantFoldConditionalExpr(const Expr *E,
const ASTContext &Ctx) {
// FIXME: more places can use this function
if (const auto *CE = dyn_cast<ConditionalOperator>(E)) {
bool CondEval;

if (CE->getCond()->EvaluateAsBooleanCondition(CondEval, Ctx))
return CondEval ? CE->getLHS() : CE->getRHS();
}
return E;
}

// A pointer type expression is known to be null-terminated, if it has the
// form: E.c_str(), for any expression E of `std::string` type.
static bool isNullTermPointer(const Expr *Ptr) {
static bool isNullTermPointer(const Expr *Ptr, ASTContext &Ctx) {
Ptr = tryConstantFoldConditionalExpr(Ptr, Ctx);
if (isa<clang::StringLiteral>(Ptr->IgnoreParenImpCasts()))
return true;
if (isa<PredefinedExpr>(Ptr->IgnoreParenImpCasts()))
Expand Down Expand Up @@ -874,7 +890,7 @@ static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg,

const Expr *Arg = Call->getArg(ArgIdx);

if (isNullTermPointer(Arg))
if (isNullTermPointer(Arg, Ctx))
// If Arg is a null-terminated pointer, it is safe anyway.
return true; // continue parsing

Expand Down Expand Up @@ -922,8 +938,8 @@ static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg,
// (including the format argument) is unsafe pointer.
return llvm::any_of(
llvm::make_range(Call->arg_begin() + FmtArgIdx, Call->arg_end()),
[&UnsafeArg](const Expr *Arg) -> bool {
if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg)) {
[&UnsafeArg, &Ctx](const Expr *Arg) -> bool {
if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg, Ctx)) {
UnsafeArg = Arg;
return true;
}
Expand Down Expand Up @@ -1175,7 +1191,7 @@ static bool hasUnsafePrintfStringArg(const CallExpr &Node, ASTContext &Ctx,
// We don't really recognize this "normal" printf, the only thing we
// can do is to require all pointers to be null-terminated:
for (const auto *Arg : Node.arguments())
if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg)) {
if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg, Ctx)) {
Result.addNode(Tag, DynTypedNode::create(*Arg));
return true;
}
Expand Down
31 changes: 31 additions & 0 deletions clang/test/SemaCXX/warn-unsafe-buffer-usage-fold-conditional.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// RUN: %clang_cc1 -fsyntax-only -Wno-all -Wunsafe-buffer-usage -verify %s -std=c++20
// RUN: %clang_cc1 -fsyntax-only -Wno-all -Wunsafe-buffer-usage -verify %s -x c
// expected-no-diagnostics

typedef struct {} FILE;
int fprintf( FILE* stream, const char* format, ... );
FILE * stderr;

#define DEBUG_ASSERT_MESSAGE(name, assertion, label, message, file, line, value) \
fprintf(stderr, "AssertMacros: %s, %s file: %s, line: %d, value: %lld\n", \
assertion, (message!=0) ? message : "", file, line, (long long) (value));


#define Require(assertion, exceptionLabel) \
do \
{ \
if ( __builtin_expect(!(assertion), 0) ) { \
DEBUG_ASSERT_MESSAGE( \
"DEBUG_ASSERT_COMPONENT_NAME_STRING", \
#assertion, #exceptionLabel, 0, __FILE__, __LINE__, 0); \
goto exceptionLabel; \
} \
} while ( 0 )


void f(int x, int y) {
Require(x == y, L1);
L1:
return;
}

Loading