1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
15 | #include "clang/AST/Expr.h" |
16 | #include "clang/AST/OperationKinds.h" |
17 | #include "clang/AST/StmtVisitor.h" |
18 | #include "clang/Analysis/AnalysisDeclContext.h" |
19 | #include "clang/Basic/TargetInfo.h" |
20 | #include "clang/Basic/TypeTraits.h" |
21 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
22 | #include "clang/StaticAnalyzer/Core/Checker.h" |
23 | #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" |
24 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
25 | #include "llvm/ADT/SmallString.h" |
26 | #include "llvm/Support/raw_ostream.h" |
27 | |
28 | using namespace clang; |
29 | using namespace ento; |
30 | |
31 | namespace { |
32 | class WalkAST: public StmtVisitor<WalkAST> { |
33 | const CheckerBase *Checker; |
34 | BugReporter &BR; |
35 | AnalysisDeclContext* AC; |
36 | |
37 | |
38 | bool sameDecl(const Expr *A1, const Expr *A2) { |
39 | if (const auto *D1 = dyn_cast<DeclRefExpr>(A1->IgnoreParenCasts())) |
40 | if (const auto *D2 = dyn_cast<DeclRefExpr>(A2->IgnoreParenCasts())) |
41 | return D1->getDecl() == D2->getDecl(); |
42 | return false; |
43 | } |
44 | |
45 | |
46 | bool isSizeof(const Expr *E, const Expr *WithArg) { |
47 | if (const auto *UE = dyn_cast<UnaryExprOrTypeTraitExpr>(E)) |
48 | if (UE->getKind() == UETT_SizeOf && !UE->isArgumentType()) |
49 | return sameDecl(UE->getArgumentExpr(), WithArg); |
50 | return false; |
51 | } |
52 | |
53 | |
54 | bool isStrlen(const Expr *E, const Expr *WithArg) { |
55 | if (const auto *CE = dyn_cast<CallExpr>(E)) { |
56 | const FunctionDecl *FD = CE->getDirectCallee(); |
57 | if (!FD) |
58 | return false; |
59 | return (CheckerContext::isCLibraryFunction(FD, "strlen") && |
60 | sameDecl(CE->getArg(0), WithArg)); |
61 | } |
62 | return false; |
63 | } |
64 | |
65 | |
66 | bool isOne(const Expr *E) { |
67 | if (const auto *IL = dyn_cast<IntegerLiteral>(E)) |
68 | return (IL->getValue().isIntN(1)); |
69 | return false; |
70 | } |
71 | |
72 | StringRef getPrintableName(const Expr *E) { |
73 | if (const auto *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) |
74 | return D->getDecl()->getName(); |
75 | return StringRef(); |
76 | } |
77 | |
78 | |
79 | |
80 | bool containsBadStrncatPattern(const CallExpr *CE); |
81 | |
82 | |
83 | |
84 | |
85 | |
86 | |
87 | |
88 | |
89 | |
90 | |
91 | |
92 | |
93 | |
94 | |
95 | |
96 | |
97 | |
98 | |
99 | |
100 | |
101 | bool containsBadStrlcpyStrlcatPattern(const CallExpr *CE); |
102 | |
103 | public: |
104 | WalkAST(const CheckerBase *Checker, BugReporter &BR, AnalysisDeclContext *AC) |
105 | : Checker(Checker), BR(BR), AC(AC) {} |
106 | |
107 | |
108 | void VisitChildren(Stmt *S); |
109 | void VisitStmt(Stmt *S) { |
110 | VisitChildren(S); |
111 | } |
112 | void VisitCallExpr(CallExpr *CE); |
113 | }; |
114 | } |
115 | |
116 | |
117 | |
118 | |
119 | |
120 | |
121 | |
122 | bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) { |
123 | if (CE->getNumArgs() != 3) |
124 | return false; |
125 | const Expr *DstArg = CE->getArg(0); |
126 | const Expr *SrcArg = CE->getArg(1); |
127 | const Expr *LenArg = CE->getArg(2); |
128 | |
129 | |
130 | if (const auto *BE = dyn_cast<BinaryOperator>(LenArg->IgnoreParenCasts())) { |
131 | |
132 | if (BE->getOpcode() == BO_Sub) { |
133 | const Expr *L = BE->getLHS(); |
134 | const Expr *R = BE->getRHS(); |
135 | if (isSizeof(L, DstArg) && isStrlen(R, DstArg)) |
136 | return true; |
137 | |
138 | |
139 | if (isSizeof(L, DstArg) && isOne(R->IgnoreParenCasts())) |
140 | return true; |
141 | } |
142 | } |
143 | |
144 | if (isSizeof(LenArg, DstArg)) |
145 | return true; |
146 | |
147 | |
148 | if (isSizeof(LenArg, SrcArg)) |
149 | return true; |
150 | return false; |
151 | } |
152 | |
153 | bool WalkAST::containsBadStrlcpyStrlcatPattern(const CallExpr *CE) { |
154 | if (CE->getNumArgs() != 3) |
155 | return false; |
156 | const Expr *DstArg = CE->getArg(0); |
157 | const Expr *LenArg = CE->getArg(2); |
158 | |
159 | const auto *DstArgDecl = dyn_cast<DeclRefExpr>(DstArg->IgnoreParenImpCasts()); |
160 | const auto *LenArgDecl = dyn_cast<DeclRefExpr>(LenArg->IgnoreParenLValueCasts()); |
161 | uint64_t DstOff = 0; |
162 | if (isSizeof(LenArg, DstArg)) |
163 | return false; |
164 | |
165 | if (LenArgDecl) { |
166 | const auto *LenArgVal = dyn_cast<VarDecl>(LenArgDecl->getDecl()); |
167 | if (LenArgVal->getInit()) |
168 | LenArg = LenArgVal->getInit(); |
169 | } |
170 | |
171 | |
172 | |
173 | |
174 | if (const auto *IL = dyn_cast<IntegerLiteral>(LenArg->IgnoreParenImpCasts())) { |
175 | uint64_t ILRawVal = IL->getValue().getZExtValue(); |
176 | |
177 | |
178 | |
179 | |
180 | if (!DstArgDecl) { |
181 | if (const auto *BE = dyn_cast<BinaryOperator>(DstArg->IgnoreParenImpCasts())) { |
182 | DstArgDecl = dyn_cast<DeclRefExpr>(BE->getLHS()->IgnoreParenImpCasts()); |
183 | if (BE->getOpcode() == BO_Add) { |
184 | if ((IL = dyn_cast<IntegerLiteral>(BE->getRHS()->IgnoreParenImpCasts()))) { |
185 | DstOff = IL->getValue().getZExtValue(); |
186 | } |
187 | } |
188 | } |
189 | } |
190 | if (DstArgDecl) { |
191 | if (const auto *Buffer = dyn_cast<ConstantArrayType>(DstArgDecl->getType())) { |
192 | ASTContext &C = BR.getContext(); |
193 | uint64_t BufferLen = C.getTypeSize(Buffer) / 8; |
194 | auto RemainingBufferLen = BufferLen - DstOff; |
195 | if (RemainingBufferLen < ILRawVal) |
196 | return true; |
197 | } |
198 | } |
199 | } |
200 | |
201 | return false; |
202 | } |
203 | |
204 | void WalkAST::VisitCallExpr(CallExpr *CE) { |
205 | const FunctionDecl *FD = CE->getDirectCallee(); |
206 | if (!FD) |
207 | return; |
208 | |
209 | if (CheckerContext::isCLibraryFunction(FD, "strncat")) { |
210 | if (containsBadStrncatPattern(CE)) { |
211 | const Expr *DstArg = CE->getArg(0); |
212 | const Expr *LenArg = CE->getArg(2); |
213 | PathDiagnosticLocation Loc = |
214 | PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC); |
215 | |
216 | StringRef DstName = getPrintableName(DstArg); |
217 | |
218 | SmallString<256> S; |
219 | llvm::raw_svector_ostream os(S); |
220 | os << "Potential buffer overflow. "; |
221 | if (!DstName.empty()) { |
222 | os << "Replace with 'sizeof(" << DstName << ") " |
223 | "- strlen(" << DstName <<") - 1'"; |
224 | os << " or u"; |
225 | } else |
226 | os << "U"; |
227 | os << "se a safer 'strlcat' API"; |
228 | |
229 | BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument", |
230 | "C String API", os.str(), Loc, |
231 | LenArg->getSourceRange()); |
232 | } |
233 | } else if (CheckerContext::isCLibraryFunction(FD, "strlcpy") || |
234 | CheckerContext::isCLibraryFunction(FD, "strlcat")) { |
235 | if (containsBadStrlcpyStrlcatPattern(CE)) { |
236 | const Expr *DstArg = CE->getArg(0); |
237 | const Expr *LenArg = CE->getArg(2); |
238 | PathDiagnosticLocation Loc = |
239 | PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC); |
240 | |
241 | StringRef DstName = getPrintableName(DstArg); |
242 | |
243 | SmallString<256> S; |
244 | llvm::raw_svector_ostream os(S); |
245 | os << "The third argument allows to potentially copy more bytes than it should. "; |
246 | os << "Replace with the value "; |
247 | if (!DstName.empty()) |
248 | os << "sizeof(" << DstName << ")"; |
249 | else |
250 | os << "sizeof(<destination buffer>)"; |
251 | os << " or lower"; |
252 | |
253 | BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument", |
254 | "C String API", os.str(), Loc, |
255 | LenArg->getSourceRange()); |
256 | } |
257 | } |
258 | |
259 | |
260 | VisitChildren(CE); |
261 | } |
262 | |
263 | void WalkAST::VisitChildren(Stmt *S) { |
264 | for (Stmt *Child : S->children()) |
265 | if (Child) |
266 | Visit(Child); |
267 | } |
268 | |
269 | namespace { |
270 | class CStringSyntaxChecker: public Checker<check::ASTCodeBody> { |
271 | public: |
272 | |
273 | void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, |
274 | BugReporter &BR) const { |
275 | WalkAST walker(this, BR, Mgr.getAnalysisDeclContext(D)); |
276 | walker.Visit(D->getBody()); |
277 | } |
278 | }; |
279 | } |
280 | |
281 | void ento::registerCStringSyntaxChecker(CheckerManager &mgr) { |
282 | mgr.registerChecker<CStringSyntaxChecker>(); |
283 | } |
284 | |
285 | bool ento::shouldRegisterCStringSyntaxChecker(const LangOptions &LO) { |
286 | return true; |
287 | } |
288 | |