1 | // MmapWriteExecChecker.cpp - Check for the prot argument -----------------===// |
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 | // This checker tests the 3rd argument of mmap's calls to check if |
10 | // it is writable and executable in the same time. It's somehow |
11 | // an optional checker since for example in JIT libraries it is pretty common. |
12 | // |
13 | //===----------------------------------------------------------------------===// |
14 | |
15 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
16 | |
17 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
18 | #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" |
19 | #include "clang/StaticAnalyzer/Core/Checker.h" |
20 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
21 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" |
22 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
23 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
24 | |
25 | using namespace clang; |
26 | using namespace ento; |
27 | |
28 | namespace { |
29 | class MmapWriteExecChecker : public Checker<check::PreCall> { |
30 | CallDescription MmapFn{CDM::CLibrary, {"mmap" }, 6}; |
31 | CallDescription MprotectFn{CDM::CLibrary, {"mprotect" }, 3}; |
32 | static int ProtWrite; |
33 | static int ProtExec; |
34 | static int ProtRead; |
35 | const BugType BT{this, "W^X check fails, Write Exec prot flags set" , |
36 | "Security" }; |
37 | |
38 | public: |
39 | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
40 | int ProtExecOv; |
41 | int ProtReadOv; |
42 | }; |
43 | } |
44 | |
45 | int MmapWriteExecChecker::ProtWrite = 0x02; |
46 | int MmapWriteExecChecker::ProtExec = 0x04; |
47 | int MmapWriteExecChecker::ProtRead = 0x01; |
48 | |
49 | void MmapWriteExecChecker::checkPreCall(const CallEvent &Call, |
50 | CheckerContext &C) const { |
51 | if (matchesAny(Call, CD1: MmapFn, CDs: MprotectFn)) { |
52 | SVal ProtVal = Call.getArgSVal(Index: 2); |
53 | auto ProtLoc = ProtVal.getAs<nonloc::ConcreteInt>(); |
54 | if (!ProtLoc) |
55 | return; |
56 | int64_t Prot = ProtLoc->getValue().getSExtValue(); |
57 | if (ProtExecOv != ProtExec) |
58 | ProtExec = ProtExecOv; |
59 | if (ProtReadOv != ProtRead) |
60 | ProtRead = ProtReadOv; |
61 | |
62 | // Wrong settings |
63 | if (ProtRead == ProtExec) |
64 | return; |
65 | |
66 | if ((Prot & (ProtWrite | ProtExec)) == (ProtWrite | ProtExec)) { |
67 | ExplodedNode *N = C.generateNonFatalErrorNode(); |
68 | if (!N) |
69 | return; |
70 | |
71 | auto Report = std::make_unique<PathSensitiveBugReport>( |
72 | args: BT, |
73 | args: "Both PROT_WRITE and PROT_EXEC flags are set. This can " |
74 | "lead to exploitable memory regions, which could be overwritten " |
75 | "with malicious code" , |
76 | args&: N); |
77 | Report->addRange(R: Call.getArgSourceRange(Index: 2)); |
78 | C.emitReport(R: std::move(Report)); |
79 | } |
80 | } |
81 | } |
82 | |
83 | void ento::registerMmapWriteExecChecker(CheckerManager &mgr) { |
84 | MmapWriteExecChecker *Mwec = |
85 | mgr.registerChecker<MmapWriteExecChecker>(); |
86 | Mwec->ProtExecOv = |
87 | mgr.getAnalyzerOptions() |
88 | .getCheckerIntegerOption(C: Mwec, OptionName: "MmapProtExec" ); |
89 | Mwec->ProtReadOv = |
90 | mgr.getAnalyzerOptions() |
91 | .getCheckerIntegerOption(C: Mwec, OptionName: "MmapProtRead" ); |
92 | } |
93 | |
94 | bool ento::shouldRegisterMmapWriteExecChecker(const CheckerManager &mgr) { |
95 | return true; |
96 | } |
97 | |