1 //===--- StringLiteralWithEmbeddedNulCheck.cpp - clang-tidy----------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "StringLiteralWithEmbeddedNulCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 using namespace clang::ast_matchers
;
15 namespace clang::tidy::bugprone
{
18 AST_MATCHER(StringLiteral
, containsNul
) {
19 for (size_t I
= 0; I
< Node
.getLength(); ++I
)
20 if (Node
.getCodeUnit(I
) == '\0')
26 void StringLiteralWithEmbeddedNulCheck::registerMatchers(MatchFinder
*Finder
) {
27 // Match a string that contains embedded NUL character. Extra-checks are
28 // applied in |check| to find incorrectly escaped characters.
29 Finder
->addMatcher(stringLiteral(containsNul()).bind("strlit"), this);
31 // The remaining checks only apply to C++.
32 if (!getLangOpts().CPlusPlus
)
35 const auto StrLitWithNul
=
36 ignoringParenImpCasts(stringLiteral(containsNul()).bind("truncated"));
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 not
43 // be present explicitly.
44 cxxConstructExpr(argumentCountIs(2),
45 hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
46 hasArgument(1, cxxDefaultArgExpr()))));
48 // Detect passing a suspicious string literal to a string constructor.
49 // example: std::string str = "abc\0def";
50 Finder
->addMatcher(traverse(TK_AsIs
,
51 cxxConstructExpr(StringConstructorExpr
, hasArgument(0, StrLitWithNul
))),
54 // Detect passing a suspicious string literal through an overloaded operator.
55 Finder
->addMatcher(cxxOperatorCallExpr(hasAnyArgument(StrLitWithNul
)), this);
58 void StringLiteralWithEmbeddedNulCheck::check(
59 const MatchFinder::MatchResult
&Result
) {
60 if (const auto *SL
= Result
.Nodes
.getNodeAs
<StringLiteral
>("strlit")) {
61 for (size_t Offset
= 0, Length
= SL
->getLength(); Offset
< Length
;
63 // Find a sequence of character like "\0x12".
64 if (Offset
+ 3 < Length
&& SL
->getCodeUnit(Offset
) == '\0' &&
65 SL
->getCodeUnit(Offset
+ 1) == 'x' &&
66 isDigit(SL
->getCodeUnit(Offset
+ 2)) &&
67 isDigit(SL
->getCodeUnit(Offset
+ 3))) {
68 diag(SL
->getBeginLoc(), "suspicious embedded NUL character");
74 if (const auto *SL
= Result
.Nodes
.getNodeAs
<StringLiteral
>("truncated")) {
75 diag(SL
->getBeginLoc(),
76 "truncated string literal with embedded NUL character");
80 } // namespace clang::tidy::bugprone