1 //===--- StringLiteralWithEmbeddedNulCheck.cpp - clang-tidy----------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "StringLiteralWithEmbeddedNulCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 14 using namespace clang::ast_matchers; 15 16 namespace clang { 17 namespace tidy { 18 namespace bugprone { 19 20 namespace { 21 AST_MATCHER(StringLiteral, containsNul) { 22 for (size_t i = 0; i < Node.getLength(); ++i) 23 if (Node.getCodeUnit(i) == '\0') 24 return true; 25 return false; 26 } 27 } // namespace 28 29 void StringLiteralWithEmbeddedNulCheck::registerMatchers(MatchFinder *Finder) { 30 // Match a string that contains embedded NUL character. Extra-checks are 31 // applied in |check| to find incorectly escaped characters. 32 Finder->addMatcher(stringLiteral(containsNul()).bind("strlit"), this); 33 34 // The remaining checks only apply to C++. 35 if (!getLangOpts().CPlusPlus) 36 return; 37 38 const auto StrLitWithNul = 39 ignoringParenImpCasts(stringLiteral(containsNul()).bind("truncated")); 40 41 // Match string constructor. 42 const auto StringConstructorExpr = expr(anyOf( 43 cxxConstructExpr(argumentCountIs(1), 44 hasDeclaration(cxxMethodDecl(hasName("basic_string")))), 45 // If present, the second argument is the alloc object which must not 46 // be present explicitly. 47 cxxConstructExpr(argumentCountIs(2), 48 hasDeclaration(cxxMethodDecl(hasName("basic_string"))), 49 hasArgument(1, cxxDefaultArgExpr())))); 50 51 // Detect passing a suspicious string literal to a string constructor. 52 // example: std::string str = "abc\0def"; 53 Finder->addMatcher( 54 cxxConstructExpr(StringConstructorExpr, hasArgument(0, StrLitWithNul)), 55 this); 56 57 // Detect passing a suspicious string literal through an overloaded operator. 58 Finder->addMatcher(cxxOperatorCallExpr(hasAnyArgument(StrLitWithNul)), this); 59 } 60 61 void StringLiteralWithEmbeddedNulCheck::check( 62 const MatchFinder::MatchResult &Result) { 63 if (const auto *SL = Result.Nodes.getNodeAs<StringLiteral>("strlit")) { 64 for (size_t Offset = 0, Length = SL->getLength(); Offset < Length; 65 ++Offset) { 66 // Find a sequence of character like "\0x12". 67 if (Offset + 3 < Length && SL->getCodeUnit(Offset) == '\0' && 68 SL->getCodeUnit(Offset + 1) == 'x' && 69 isDigit(SL->getCodeUnit(Offset + 2)) && 70 isDigit(SL->getCodeUnit(Offset + 3))) { 71 diag(SL->getLocStart(), "suspicious embedded NUL character"); 72 return; 73 } 74 } 75 } 76 77 if (const auto *SL = Result.Nodes.getNodeAs<StringLiteral>("truncated")) { 78 diag(SL->getLocStart(), 79 "truncated string literal with embedded NUL character"); 80 } 81 } 82 83 } // namespace bugprone 84 } // namespace tidy 85 } // namespace clang 86