14ba319b5SDimitry Andric // MmapWriteExecChecker.cpp - Check for the prot argument -----------------===//
24ba319b5SDimitry Andric //
34ba319b5SDimitry Andric //                     The LLVM Compiler Infrastructure
44ba319b5SDimitry Andric //
54ba319b5SDimitry Andric // This file is distributed under the University of Illinois Open Source
64ba319b5SDimitry Andric // License. See LICENSE.TXT for details.
74ba319b5SDimitry Andric //
84ba319b5SDimitry Andric //===----------------------------------------------------------------------===//
94ba319b5SDimitry Andric //
104ba319b5SDimitry Andric // This checker tests the 3rd argument of mmap's calls to check if
114ba319b5SDimitry Andric // it is writable and executable in the same time. It's somehow
124ba319b5SDimitry Andric // an optional checker since for example in JIT libraries it is pretty common.
134ba319b5SDimitry Andric //
144ba319b5SDimitry Andric //===----------------------------------------------------------------------===//
154ba319b5SDimitry Andric 
16*b5893f02SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
174ba319b5SDimitry Andric 
184ba319b5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
194ba319b5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
204ba319b5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
214ba319b5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
224ba319b5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
234ba319b5SDimitry Andric 
244ba319b5SDimitry Andric using namespace clang;
254ba319b5SDimitry Andric using namespace ento;
264ba319b5SDimitry Andric using llvm::APSInt;
274ba319b5SDimitry Andric 
284ba319b5SDimitry Andric namespace {
294ba319b5SDimitry Andric class MmapWriteExecChecker : public Checker<check::PreCall> {
304ba319b5SDimitry Andric   CallDescription MmapFn;
314ba319b5SDimitry Andric   CallDescription MprotectFn;
324ba319b5SDimitry Andric   static int ProtWrite;
334ba319b5SDimitry Andric   static int ProtExec;
344ba319b5SDimitry Andric   static int ProtRead;
354ba319b5SDimitry Andric   mutable std::unique_ptr<BugType> BT;
364ba319b5SDimitry Andric public:
MmapWriteExecChecker()374ba319b5SDimitry Andric   MmapWriteExecChecker() : MmapFn("mmap", 6), MprotectFn("mprotect", 3) {}
384ba319b5SDimitry Andric   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
394ba319b5SDimitry Andric   int ProtExecOv;
404ba319b5SDimitry Andric   int ProtReadOv;
414ba319b5SDimitry Andric };
424ba319b5SDimitry Andric }
434ba319b5SDimitry Andric 
444ba319b5SDimitry Andric int MmapWriteExecChecker::ProtWrite = 0x02;
454ba319b5SDimitry Andric int MmapWriteExecChecker::ProtExec  = 0x04;
464ba319b5SDimitry Andric int MmapWriteExecChecker::ProtRead  = 0x01;
474ba319b5SDimitry Andric 
checkPreCall(const CallEvent & Call,CheckerContext & C) const484ba319b5SDimitry Andric void MmapWriteExecChecker::checkPreCall(const CallEvent &Call,
494ba319b5SDimitry Andric                                          CheckerContext &C) const {
504ba319b5SDimitry Andric   if (Call.isCalled(MmapFn) || Call.isCalled(MprotectFn)) {
514ba319b5SDimitry Andric     SVal ProtVal = Call.getArgSVal(2);
524ba319b5SDimitry Andric     Optional<nonloc::ConcreteInt> ProtLoc = ProtVal.getAs<nonloc::ConcreteInt>();
534ba319b5SDimitry Andric     int64_t Prot = ProtLoc->getValue().getSExtValue();
544ba319b5SDimitry Andric     if (ProtExecOv != ProtExec)
554ba319b5SDimitry Andric       ProtExec = ProtExecOv;
564ba319b5SDimitry Andric     if (ProtReadOv != ProtRead)
574ba319b5SDimitry Andric       ProtRead = ProtReadOv;
584ba319b5SDimitry Andric 
594ba319b5SDimitry Andric     // Wrong settings
604ba319b5SDimitry Andric     if (ProtRead == ProtExec)
614ba319b5SDimitry Andric       return;
624ba319b5SDimitry Andric 
634ba319b5SDimitry Andric     if ((Prot & (ProtWrite | ProtExec)) == (ProtWrite | ProtExec)) {
644ba319b5SDimitry Andric       if (!BT)
654ba319b5SDimitry Andric         BT.reset(new BugType(this, "W^X check fails, Write Exec prot flags set", "Security"));
664ba319b5SDimitry Andric 
674ba319b5SDimitry Andric       ExplodedNode *N = C.generateNonFatalErrorNode();
684ba319b5SDimitry Andric       if (!N)
694ba319b5SDimitry Andric         return;
704ba319b5SDimitry Andric 
714ba319b5SDimitry Andric       auto Report = llvm::make_unique<BugReport>(
724ba319b5SDimitry Andric           *BT, "Both PROT_WRITE and PROT_EXEC flags are set. This can "
734ba319b5SDimitry Andric                "lead to exploitable memory regions, which could be overwritten "
744ba319b5SDimitry Andric                "with malicious code", N);
754ba319b5SDimitry Andric       Report->addRange(Call.getArgSourceRange(2));
764ba319b5SDimitry Andric       C.emitReport(std::move(Report));
774ba319b5SDimitry Andric     }
784ba319b5SDimitry Andric   }
794ba319b5SDimitry Andric }
804ba319b5SDimitry Andric 
registerMmapWriteExecChecker(CheckerManager & mgr)814ba319b5SDimitry Andric void ento::registerMmapWriteExecChecker(CheckerManager &mgr) {
824ba319b5SDimitry Andric   MmapWriteExecChecker *Mwec =
834ba319b5SDimitry Andric       mgr.registerChecker<MmapWriteExecChecker>();
844ba319b5SDimitry Andric   Mwec->ProtExecOv =
85*b5893f02SDimitry Andric     mgr.getAnalyzerOptions()
86*b5893f02SDimitry Andric       .getCheckerIntegerOption("MmapProtExec", 0x04, Mwec);
874ba319b5SDimitry Andric   Mwec->ProtReadOv =
88*b5893f02SDimitry Andric     mgr.getAnalyzerOptions()
89*b5893f02SDimitry Andric       .getCheckerIntegerOption("MmapProtRead", 0x01, Mwec);
904ba319b5SDimitry Andric }
91