1212#include " clang/AST/DeclCXX.h"
1313#include " clang/AST/Expr.h"
1414#include " clang/AST/ExprCXX.h"
15+ #include " clang/AST/FormatString.h"
1516#include " clang/AST/RecursiveASTVisitor.h"
1617#include " clang/AST/Stmt.h"
1718#include " clang/AST/StmtVisitor.h"
@@ -611,6 +612,38 @@ static bool isNullTermPointer(const Expr *Ptr) {
611612 return false ;
612613}
613614
615+ // Return true iff at least one of following cases holds:
616+ // 1. Format string is a literal and there is an unsafe pointer argument
617+ // corresponding to an `s` specifier;
618+ // 2. Format string is not a literal and there is least an unsafe pointer
619+ // argument (including the formatter argument).
620+ static bool hasUnsafeFormatOrSArg (const Expr *Fmt, unsigned FmtArgIdx,
621+ const CallExpr *Call, ASTContext &Ctx) {
622+ if (auto *SL = dyn_cast<StringLiteral>(Fmt->IgnoreParenImpCasts ())) {
623+ StringRef FmtStr = SL->getString ();
624+ auto I = FmtStr.begin ();
625+ auto E = FmtStr.end ();
626+ unsigned ArgIdx = FmtArgIdx;
627+
628+ do {
629+ ArgIdx = analyze_format_string::ParseFormatStringFirstSArgIndex (
630+ I, E, ArgIdx, Ctx.getLangOpts (), Ctx.getTargetInfo ());
631+ if (ArgIdx && Call->getNumArgs () > ArgIdx &&
632+ !isNullTermPointer (Call->getArg (ArgIdx)))
633+ return true ;
634+ } while (ArgIdx);
635+ return false ;
636+ }
637+ // If format is not a string literal, we cannot analyze the format string.
638+ // In this case, this call is considered unsafe if at least one argument
639+ // (including the format argument) is unsafe pointer.
640+ return llvm::any_of (
641+ llvm::make_range (Call->arg_begin () + FmtArgIdx, Call->arg_end ()),
642+ [](const Expr *Arg) {
643+ return Arg->getType ()->isPointerType () && !isNullTermPointer (Arg);
644+ });
645+ }
646+
614647// Matches a call to one of the `-printf" functions (excluding the ones with
615648// va_list, or `-sprintf`s) that taking pointer-to-char-as-string arguments but
616649// fail to guarantee their null-termination. In other words, these calls are
@@ -626,19 +659,18 @@ AST_MATCHER_P(CallExpr, unsafeStringInPrintfs, StringRef, CoreName) {
626659 if (Prefix.ends_with (" w" ))
627660 Prefix = Prefix.drop_back (1 );
628661
629- auto AnyUnsafeStrPtr = [](const Expr *Arg) -> bool {
630- return Arg->getType ()->isPointerType () && !isNullTermPointer (Arg);
631- };
632-
633662 if (Prefix.empty () ||
634663 Prefix == " k" ) // printf: all pointer args should be null-terminated
635- return any_of (Node.arguments (), AnyUnsafeStrPtr);
636- if (Prefix == " f" && Node.getNumArgs () > 1 )
637- return any_of (llvm::make_range (Node.arg_begin () + 1 , Node.arg_end ()),
638- AnyUnsafeStrPtr);
639- if (Prefix == " sn" && Node.getNumArgs () > 2 ) {
640- return any_of (llvm::make_range (Node.arg_begin () + 2 , Node.arg_end ()),
641- AnyUnsafeStrPtr);
664+ return hasUnsafeFormatOrSArg (Node.getArg (0 ), 0 , &Node,
665+ Finder->getASTContext ());
666+ if (Prefix == " f" )
667+ return hasUnsafeFormatOrSArg (Node.getArg (1 ), 1 , &Node,
668+ Finder->getASTContext ());
669+ if (Prefix == " sn" ) {
670+ // The first two arguments need to be in safe patterns, which is checked
671+ // by `isSafeSizedby`:
672+ return hasUnsafeFormatOrSArg (Node.getArg (2 ), 2 , &Node,
673+ Finder->getASTContext ());
642674 }
643675 return false ; // A call to a "-printf" falls into another category.
644676}
0 commit comments