10b57cec5SDimitry Andric // MmapWriteExecChecker.cpp - Check for the prot argument -----------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This checker tests the 3rd argument of mmap's calls to check if 100b57cec5SDimitry Andric // it is writable and executable in the same time. It's somehow 110b57cec5SDimitry Andric // an optional checker since for example in JIT libraries it is pretty common. 120b57cec5SDimitry Andric // 130b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 140b57cec5SDimitry Andric 150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 160b57cec5SDimitry Andric 170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 18647cbc5dSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" 190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 21349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" 220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric using namespace clang; 260b57cec5SDimitry Andric using namespace ento; 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric namespace { 290b57cec5SDimitry Andric class MmapWriteExecChecker : public Checker<check::PreCall> { 30*0fca6ea1SDimitry Andric CallDescription MmapFn{CDM::CLibrary, {"mmap"}, 6}; 31*0fca6ea1SDimitry Andric CallDescription MprotectFn{CDM::CLibrary, {"mprotect"}, 3}; 320b57cec5SDimitry Andric static int ProtWrite; 330b57cec5SDimitry Andric static int ProtExec; 340b57cec5SDimitry Andric static int ProtRead; 35647cbc5dSDimitry Andric const BugType BT{this, "W^X check fails, Write Exec prot flags set", 36647cbc5dSDimitry Andric "Security"}; 37647cbc5dSDimitry Andric 380b57cec5SDimitry Andric public: 390b57cec5SDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 400b57cec5SDimitry Andric int ProtExecOv; 410b57cec5SDimitry Andric int ProtReadOv; 420b57cec5SDimitry Andric }; 430b57cec5SDimitry Andric } 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric int MmapWriteExecChecker::ProtWrite = 0x02; 460b57cec5SDimitry Andric int MmapWriteExecChecker::ProtExec = 0x04; 470b57cec5SDimitry Andric int MmapWriteExecChecker::ProtRead = 0x01; 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric void MmapWriteExecChecker::checkPreCall(const CallEvent &Call, 500b57cec5SDimitry Andric CheckerContext &C) const { 51349cc55cSDimitry Andric if (matchesAny(Call, MmapFn, MprotectFn)) { 520b57cec5SDimitry Andric SVal ProtVal = Call.getArgSVal(2); 535f757f3fSDimitry Andric auto ProtLoc = ProtVal.getAs<nonloc::ConcreteInt>(); 545f757f3fSDimitry Andric if (!ProtLoc) 555f757f3fSDimitry Andric return; 565f757f3fSDimitry Andric int64_t Prot = ProtLoc->getValue().getSExtValue(); 570b57cec5SDimitry Andric if (ProtExecOv != ProtExec) 580b57cec5SDimitry Andric ProtExec = ProtExecOv; 590b57cec5SDimitry Andric if (ProtReadOv != ProtRead) 600b57cec5SDimitry Andric ProtRead = ProtReadOv; 610b57cec5SDimitry Andric 620b57cec5SDimitry Andric // Wrong settings 630b57cec5SDimitry Andric if (ProtRead == ProtExec) 640b57cec5SDimitry Andric return; 650b57cec5SDimitry Andric 660b57cec5SDimitry Andric if ((Prot & (ProtWrite | ProtExec)) == (ProtWrite | ProtExec)) { 670b57cec5SDimitry Andric ExplodedNode *N = C.generateNonFatalErrorNode(); 680b57cec5SDimitry Andric if (!N) 690b57cec5SDimitry Andric return; 700b57cec5SDimitry Andric 71a7dea167SDimitry Andric auto Report = std::make_unique<PathSensitiveBugReport>( 72647cbc5dSDimitry Andric BT, 73647cbc5dSDimitry Andric "Both PROT_WRITE and PROT_EXEC flags are set. This can " 740b57cec5SDimitry Andric "lead to exploitable memory regions, which could be overwritten " 75647cbc5dSDimitry Andric "with malicious code", 76647cbc5dSDimitry Andric N); 770b57cec5SDimitry Andric Report->addRange(Call.getArgSourceRange(2)); 780b57cec5SDimitry Andric C.emitReport(std::move(Report)); 790b57cec5SDimitry Andric } 800b57cec5SDimitry Andric } 810b57cec5SDimitry Andric } 820b57cec5SDimitry Andric 830b57cec5SDimitry Andric void ento::registerMmapWriteExecChecker(CheckerManager &mgr) { 840b57cec5SDimitry Andric MmapWriteExecChecker *Mwec = 850b57cec5SDimitry Andric mgr.registerChecker<MmapWriteExecChecker>(); 860b57cec5SDimitry Andric Mwec->ProtExecOv = 870b57cec5SDimitry Andric mgr.getAnalyzerOptions() 880b57cec5SDimitry Andric .getCheckerIntegerOption(Mwec, "MmapProtExec"); 890b57cec5SDimitry Andric Mwec->ProtReadOv = 900b57cec5SDimitry Andric mgr.getAnalyzerOptions() 910b57cec5SDimitry Andric .getCheckerIntegerOption(Mwec, "MmapProtRead"); 920b57cec5SDimitry Andric } 930b57cec5SDimitry Andric 945ffd83dbSDimitry Andric bool ento::shouldRegisterMmapWriteExecChecker(const CheckerManager &mgr) { 950b57cec5SDimitry Andric return true; 960b57cec5SDimitry Andric } 97