// MmapWriteExecChecker.cpp - Check for the prot argument -----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This checker tests the 3rd argument of mmap's calls to check if // it is writable and executable in the same time. It's somehow // an optional checker since for example in JIT libraries it is pretty common. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" using namespace clang; using namespace ento; namespace { class MmapWriteExecChecker : public Checker, check::PreCall> { CallDescription MmapFn{CDM::CLibrary, {"mmap"}, 6}; CallDescription MprotectFn{CDM::CLibrary, {"mprotect"}, 3}; const BugType BT{this, "W^X check fails, Write Exec prot flags set", "Security"}; // Default values are used if definition of the flags is not found. mutable int ProtRead = 0x01; mutable int ProtWrite = 0x02; mutable int ProtExec = 0x04; public: void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager &Mgr, BugReporter &BR) const; void checkPreCall(const CallEvent &Call, CheckerContext &C) const; }; } void MmapWriteExecChecker::checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager &Mgr, BugReporter &BR) const { Preprocessor &PP = Mgr.getPreprocessor(); const std::optional FoundProtRead = tryExpandAsInteger("PROT_READ", PP); const std::optional FoundProtWrite = tryExpandAsInteger("PROT_WRITE", PP); const std::optional FoundProtExec = tryExpandAsInteger("PROT_EXEC", PP); if (FoundProtRead && FoundProtWrite && FoundProtExec) { ProtRead = *FoundProtRead; ProtWrite = *FoundProtWrite; ProtExec = *FoundProtExec; } } void MmapWriteExecChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (matchesAny(Call, MmapFn, MprotectFn)) { SVal ProtVal = Call.getArgSVal(2); auto ProtLoc = ProtVal.getAs(); if (!ProtLoc) return; int64_t Prot = ProtLoc->getValue()->getSExtValue(); if ((Prot & ProtWrite) && (Prot & ProtExec)) { ExplodedNode *N = C.generateNonFatalErrorNode(); if (!N) return; auto Report = std::make_unique( BT, "Both PROT_WRITE and PROT_EXEC flags are set. This can " "lead to exploitable memory regions, which could be overwritten " "with malicious code", N); Report->addRange(Call.getArgSourceRange(2)); C.emitReport(std::move(Report)); } } } void ento::registerMmapWriteExecChecker(CheckerManager &Mgr) { Mgr.registerChecker(); } bool ento::shouldRegisterMmapWriteExecChecker(const CheckerManager &) { return true; }