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