1414#include " clang/AST/RecursiveASTVisitor.h"
1515#include " clang/AST/Stmt.h"
1616#include " clang/AST/StmtVisitor.h"
17+ #include " clang/AST/Type.h"
1718#include " clang/ASTMatchers/ASTMatchFinder.h"
1819#include " clang/ASTMatchers/ASTMatchers.h"
1920#include " clang/Basic/SourceLocation.h"
@@ -763,32 +764,27 @@ AST_MATCHER(FunctionDecl, isNormalPrintfFunc) {
763764AST_MATCHER_P (CallExpr, hasUnsafePrintfStringArg,
764765 clang::ast_matchers::internal::Matcher<Expr>,
765766 UnsafeStringArgMatcher) {
766- // Determine what printf it is:
767- const Expr *FirstArg = Node.getArg (0 );
768- ASTContext &Ctx = Finder->getASTContext ();
767+ // Determine what printf it is by examining formal parameters:
768+ const FunctionDecl *FD = Node.getDirectCallee ();
769769
770- if (isa<StringLiteral>(FirstArg->IgnoreParenImpCasts ())) {
771- // It is a printf/kprintf. And, the format is a string literal:
772- bool isKprintf = false ;
773- const Expr *UnsafeArg;
770+ assert (FD && " It should have been checked that FD is non-null." );
774771
775- if (auto *Callee = Node.getDirectCallee ())
776- if (auto *II = Callee->getIdentifier ())
777- isKprintf = II->getName () == " kprintf" ;
778- if (hasUnsafeFormatOrSArg (&Node, UnsafeArg, 0 , Ctx, isKprintf))
779- return UnsafeStringArgMatcher.matches (*UnsafeArg, Finder, Builder);
780- return false ;
781- }
772+ unsigned NumParms = FD->getNumParams ();
773+
774+ if (NumParms < 1 )
775+ return false ; // possibly some user-defined printf function
782776
783- QualType PtrTy = FirstArg->getType ();
777+ ASTContext &Ctx = Finder->getASTContext ();
778+ QualType FristParmTy = FD->getParamDecl (0 )->getType ();
784779
785- assert (PtrTy->isPointerType ());
780+ if (!FristParmTy->isPointerType ())
781+ return false ; // possibly some user-defined printf function
786782
787- QualType PteTy = (cast<PointerType>(PtrTy ))->getPointeeType ();
783+ QualType FirstPteTy = (cast<PointerType>(FristParmTy ))->getPointeeType ();
788784
789- if (!Ctx.getFILEType (). isNull () /* If `FILE *` is not ever in the ASTContext,
790- there can't be any file pointer then */
791- && PteTy .getCanonicalType () == Ctx.getFILEType ().getCanonicalType ()) {
785+ if (!Ctx.getFILEType ()
786+ . isNull () && // `FILE *` must be in the context if it is fprintf
787+ FirstPteTy .getCanonicalType () == Ctx.getFILEType ().getCanonicalType ()) {
792788 // It is a fprintf:
793789 const Expr *UnsafeArg;
794790
@@ -797,17 +793,32 @@ AST_MATCHER_P(CallExpr, hasUnsafePrintfStringArg,
797793 return false ;
798794 }
799795
800- const Expr *SecondArg = Node.getArg (1 );
801-
802- if (SecondArg->getType ()->isIntegerType ()) {
803- // It is a snprintf:
796+ if (FirstPteTy.isConstQualified ()) {
797+ // If the first parameter is a `const char *`, it is a printf/kprintf:
798+ bool isKprintf = false ;
804799 const Expr *UnsafeArg;
805800
806- if (hasUnsafeFormatOrSArg (&Node, UnsafeArg, 2 , Ctx, false ))
801+ if (auto *II = FD->getIdentifier ())
802+ isKprintf = II->getName () == " kprintf" ;
803+ if (hasUnsafeFormatOrSArg (&Node, UnsafeArg, 0 , Ctx, isKprintf))
807804 return UnsafeStringArgMatcher.matches (*UnsafeArg, Finder, Builder);
808805 return false ;
809806 }
810- // It is printf but the format string is passed by pointer. The only thing we
807+
808+ if (NumParms > 2 ) {
809+ QualType SecondParmTy = FD->getParamDecl (1 )->getType ();
810+
811+ if (!FirstPteTy.isConstQualified () && SecondParmTy->isIntegerType ()) {
812+ // If the first parameter type is non-const qualified `char *` and the
813+ // second is an integer, it is a snprintf:
814+ const Expr *UnsafeArg;
815+
816+ if (hasUnsafeFormatOrSArg (&Node, UnsafeArg, 2 , Ctx, false ))
817+ return UnsafeStringArgMatcher.matches (*UnsafeArg, Finder, Builder);
818+ return false ;
819+ }
820+ }
821+ // We don't really recognize this "normal" printf, the only thing we
811822 // can do is to require all pointers to be null-terminated:
812823 for (auto Arg : Node.arguments ())
813824 if (Arg->getType ()->isPointerType () && !isNullTermPointer (Arg))
@@ -826,12 +837,23 @@ AST_MATCHER_P(CallExpr, hasUnsafePrintfStringArg,
826837// size:= DRE.size()/DRE.size_bytes()
827838// And DRE is a hardened container or view.
828839AST_MATCHER (CallExpr, hasUnsafeSnprintfBuffer) {
829- if (Node.getNumArgs () < 3 )
830- return false ; // not an snprintf call
840+ const FunctionDecl *FD = Node.getDirectCallee ();
841+
842+ assert (FD && " It should have been checked that FD is non-null." );
843+
844+ if (FD->getNumParams () < 3 )
845+ return false ; // Not an snprint
846+
847+ QualType FirstParmTy = FD->getParamDecl (0 )->getType ();
848+
849+ if (!FirstParmTy->isPointerType ())
850+ return false ; // Not an snprint
831851
852+ QualType FirstPteTy = cast<PointerType>(FirstParmTy)->getPointeeType ();
832853 const Expr *Buf = Node.getArg (0 ), *Size = Node.getArg (1 );
833854
834- if (!Buf->getType ()->isPointerType () || !Size->getType ()->isIntegerType ())
855+ if (FirstPteTy.isConstQualified () || !Buf->getType ()->isPointerType () ||
856+ !Size->getType ()->isIntegerType ())
835857 return false ; // not an snprintf call
836858
837859 static StringRef SizedObjs[] = {" span" , " array" , " vector" ,
0 commit comments