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