This is a security check that warns when both PROT_WRITE and PROT_EXEC are set during mmap(). If mmap()ed memory is both writable and executable, it makes it easier for the attacker to execute arbitrary code when contents of this memory are compromised. Some applications require such mmap()s though, such as different sorts of JIT. Re-applied after a revert in r324167. Temporarily stays in the alpha package because it needs a better way of determining macro values that are not immediately available in the AST. Patch by David Carlier! Differential Revision: https://reviews.llvm.org/D42645 llvm-svn: 326405
88 lines
3.0 KiB
C++
88 lines
3.0 KiB
C++
// MmapWriteExecChecker.cpp - Check for the prot argument -----------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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 "ClangSACheckers.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
|
|
|
using namespace clang;
|
|
using namespace ento;
|
|
using llvm::APSInt;
|
|
|
|
namespace {
|
|
class MmapWriteExecChecker : public Checker<check::PreCall> {
|
|
CallDescription MmapFn;
|
|
static int ProtWrite;
|
|
static int ProtExec;
|
|
static int ProtRead;
|
|
mutable std::unique_ptr<BugType> BT;
|
|
public:
|
|
MmapWriteExecChecker() : MmapFn("mmap", 6) {}
|
|
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
|
|
int ProtExecOv;
|
|
int ProtReadOv;
|
|
};
|
|
}
|
|
|
|
int MmapWriteExecChecker::ProtWrite = 0x02;
|
|
int MmapWriteExecChecker::ProtExec = 0x04;
|
|
int MmapWriteExecChecker::ProtRead = 0x01;
|
|
|
|
void MmapWriteExecChecker::checkPreCall(const CallEvent &Call,
|
|
CheckerContext &C) const {
|
|
if (Call.isCalled(MmapFn)) {
|
|
SVal ProtVal = Call.getArgSVal(2);
|
|
Optional<nonloc::ConcreteInt> ProtLoc = ProtVal.getAs<nonloc::ConcreteInt>();
|
|
int64_t Prot = ProtLoc->getValue().getSExtValue();
|
|
if (ProtExecOv != ProtExec)
|
|
ProtExec = ProtExecOv;
|
|
if (ProtReadOv != ProtRead)
|
|
ProtRead = ProtReadOv;
|
|
|
|
// Wrong settings
|
|
if (ProtRead == ProtExec)
|
|
return;
|
|
|
|
if ((Prot & (ProtWrite | ProtExec)) == (ProtWrite | ProtExec)) {
|
|
if (!BT)
|
|
BT.reset(new BugType(this, "W^X check fails, Write Exec prot flags set", "Security"));
|
|
|
|
ExplodedNode *N = C.generateNonFatalErrorNode();
|
|
if (!N)
|
|
return;
|
|
|
|
auto Report = llvm::make_unique<BugReport>(
|
|
*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) {
|
|
MmapWriteExecChecker *Mwec =
|
|
mgr.registerChecker<MmapWriteExecChecker>();
|
|
Mwec->ProtExecOv =
|
|
mgr.getAnalyzerOptions().getOptionAsInteger("MmapProtExec", 0x04, Mwec);
|
|
Mwec->ProtReadOv =
|
|
mgr.getAnalyzerOptions().getOptionAsInteger("MmapProtRead", 0x01, Mwec);
|
|
}
|