14579bad8SArtem Dergachev // MmapWriteExecChecker.cpp - Check for the prot argument -----------------===//
24579bad8SArtem Dergachev //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64579bad8SArtem Dergachev //
74579bad8SArtem Dergachev //===----------------------------------------------------------------------===//
84579bad8SArtem Dergachev //
94579bad8SArtem Dergachev // This checker tests the 3rd argument of mmap's calls to check if
104579bad8SArtem Dergachev // it is writable and executable in the same time. It's somehow
114579bad8SArtem Dergachev // an optional checker since for example in JIT libraries it is pretty common.
124579bad8SArtem Dergachev //
134579bad8SArtem Dergachev //===----------------------------------------------------------------------===//
144579bad8SArtem Dergachev
1576a21502SKristof Umann #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
164579bad8SArtem Dergachev
174579bad8SArtem Dergachev #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
184579bad8SArtem Dergachev #include "clang/StaticAnalyzer/Core/Checker.h"
194579bad8SArtem Dergachev #include "clang/StaticAnalyzer/Core/CheckerManager.h"
200b9d3a6eSBalazs Benics #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
214579bad8SArtem Dergachev #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
224579bad8SArtem Dergachev #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
234579bad8SArtem Dergachev
244579bad8SArtem Dergachev using namespace clang;
254579bad8SArtem Dergachev using namespace ento;
264579bad8SArtem Dergachev
274579bad8SArtem Dergachev namespace {
284579bad8SArtem Dergachev class MmapWriteExecChecker : public Checker<check::PreCall> {
294579bad8SArtem Dergachev CallDescription MmapFn;
30ce5f2d3dSArtem Dergachev CallDescription MprotectFn;
314579bad8SArtem Dergachev static int ProtWrite;
324579bad8SArtem Dergachev static int ProtExec;
334579bad8SArtem Dergachev static int ProtRead;
344579bad8SArtem Dergachev mutable std::unique_ptr<BugType> BT;
354579bad8SArtem Dergachev public:
MmapWriteExecChecker()36ce5f2d3dSArtem Dergachev MmapWriteExecChecker() : MmapFn("mmap", 6), MprotectFn("mprotect", 3) {}
374579bad8SArtem Dergachev void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
384579bad8SArtem Dergachev int ProtExecOv;
394579bad8SArtem Dergachev int ProtReadOv;
404579bad8SArtem Dergachev };
414579bad8SArtem Dergachev }
424579bad8SArtem Dergachev
434579bad8SArtem Dergachev int MmapWriteExecChecker::ProtWrite = 0x02;
444579bad8SArtem Dergachev int MmapWriteExecChecker::ProtExec = 0x04;
454579bad8SArtem Dergachev int MmapWriteExecChecker::ProtRead = 0x01;
464579bad8SArtem Dergachev
checkPreCall(const CallEvent & Call,CheckerContext & C) const474579bad8SArtem Dergachev void MmapWriteExecChecker::checkPreCall(const CallEvent &Call,
484579bad8SArtem Dergachev CheckerContext &C) const {
49f18da190SBalazs Benics if (matchesAny(Call, MmapFn, MprotectFn)) {
504579bad8SArtem Dergachev SVal ProtVal = Call.getArgSVal(2);
51*96ccb690SBalazs Benics auto ProtLoc = ProtVal.castAs<nonloc::ConcreteInt>();
52*96ccb690SBalazs Benics int64_t Prot = ProtLoc.getValue().getSExtValue();
534579bad8SArtem Dergachev if (ProtExecOv != ProtExec)
544579bad8SArtem Dergachev ProtExec = ProtExecOv;
554579bad8SArtem Dergachev if (ProtReadOv != ProtRead)
564579bad8SArtem Dergachev ProtRead = ProtReadOv;
574579bad8SArtem Dergachev
584579bad8SArtem Dergachev // Wrong settings
594579bad8SArtem Dergachev if (ProtRead == ProtExec)
604579bad8SArtem Dergachev return;
614579bad8SArtem Dergachev
624579bad8SArtem Dergachev if ((Prot & (ProtWrite | ProtExec)) == (ProtWrite | ProtExec)) {
634579bad8SArtem Dergachev if (!BT)
644579bad8SArtem Dergachev BT.reset(new BugType(this, "W^X check fails, Write Exec prot flags set", "Security"));
654579bad8SArtem Dergachev
664579bad8SArtem Dergachev ExplodedNode *N = C.generateNonFatalErrorNode();
674579bad8SArtem Dergachev if (!N)
684579bad8SArtem Dergachev return;
694579bad8SArtem Dergachev
702f169e7cSArtem Dergachev auto Report = std::make_unique<PathSensitiveBugReport>(
714579bad8SArtem Dergachev *BT, "Both PROT_WRITE and PROT_EXEC flags are set. This can "
724579bad8SArtem Dergachev "lead to exploitable memory regions, which could be overwritten "
734579bad8SArtem Dergachev "with malicious code", N);
744579bad8SArtem Dergachev Report->addRange(Call.getArgSourceRange(2));
754579bad8SArtem Dergachev C.emitReport(std::move(Report));
764579bad8SArtem Dergachev }
774579bad8SArtem Dergachev }
784579bad8SArtem Dergachev }
794579bad8SArtem Dergachev
registerMmapWriteExecChecker(CheckerManager & mgr)804579bad8SArtem Dergachev void ento::registerMmapWriteExecChecker(CheckerManager &mgr) {
814579bad8SArtem Dergachev MmapWriteExecChecker *Mwec =
824579bad8SArtem Dergachev mgr.registerChecker<MmapWriteExecChecker>();
834579bad8SArtem Dergachev Mwec->ProtExecOv =
840a1f91c8SKristof Umann mgr.getAnalyzerOptions()
8583cc1b35SKristof Umann .getCheckerIntegerOption(Mwec, "MmapProtExec");
864579bad8SArtem Dergachev Mwec->ProtReadOv =
870a1f91c8SKristof Umann mgr.getAnalyzerOptions()
8883cc1b35SKristof Umann .getCheckerIntegerOption(Mwec, "MmapProtRead");
894579bad8SArtem Dergachev }
90058a7a45SKristof Umann
shouldRegisterMmapWriteExecChecker(const CheckerManager & mgr)91bda3dd0dSKirstóf Umann bool ento::shouldRegisterMmapWriteExecChecker(const CheckerManager &mgr) {
92058a7a45SKristof Umann return true;
93058a7a45SKristof Umann }
94