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