1 // MmapWriteExecChecker.cpp - Check for the prot argument -----------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This checker tests the 3rd argument of mmap's calls to check if
10 // it is writable and executable in the same time. It's somehow
11 // an optional checker since for example in JIT libraries it is pretty common.
13 //===----------------------------------------------------------------------===//
15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
17 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18 #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
19 #include "clang/StaticAnalyzer/Core/Checker.h"
20 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
26 using namespace clang
;
30 class MmapWriteExecChecker
31 : public Checker
<check::ASTDecl
<TranslationUnitDecl
>, check::PreCall
> {
32 CallDescription MmapFn
{CDM::CLibrary
, {"mmap"}, 6};
33 CallDescription MprotectFn
{CDM::CLibrary
, {"mprotect"}, 3};
34 const BugType BT
{this, "W^X check fails, Write Exec prot flags set",
37 // Default values are used if definition of the flags is not found.
38 mutable int ProtRead
= 0x01;
39 mutable int ProtWrite
= 0x02;
40 mutable int ProtExec
= 0x04;
43 void checkASTDecl(const TranslationUnitDecl
*TU
, AnalysisManager
&Mgr
,
44 BugReporter
&BR
) const;
45 void checkPreCall(const CallEvent
&Call
, CheckerContext
&C
) const;
49 void MmapWriteExecChecker::checkASTDecl(const TranslationUnitDecl
*TU
,
51 BugReporter
&BR
) const {
52 Preprocessor
&PP
= Mgr
.getPreprocessor();
53 const std::optional
<int> FoundProtRead
= tryExpandAsInteger("PROT_READ", PP
);
54 const std::optional
<int> FoundProtWrite
=
55 tryExpandAsInteger("PROT_WRITE", PP
);
56 const std::optional
<int> FoundProtExec
= tryExpandAsInteger("PROT_EXEC", PP
);
57 if (FoundProtRead
&& FoundProtWrite
&& FoundProtExec
) {
58 ProtRead
= *FoundProtRead
;
59 ProtWrite
= *FoundProtWrite
;
60 ProtExec
= *FoundProtExec
;
64 void MmapWriteExecChecker::checkPreCall(const CallEvent
&Call
,
65 CheckerContext
&C
) const {
66 if (matchesAny(Call
, MmapFn
, MprotectFn
)) {
67 SVal ProtVal
= Call
.getArgSVal(2);
68 auto ProtLoc
= ProtVal
.getAs
<nonloc::ConcreteInt
>();
71 int64_t Prot
= ProtLoc
->getValue().getSExtValue();
73 if ((Prot
& ProtWrite
) && (Prot
& ProtExec
)) {
74 ExplodedNode
*N
= C
.generateNonFatalErrorNode();
78 auto Report
= std::make_unique
<PathSensitiveBugReport
>(
80 "Both PROT_WRITE and PROT_EXEC flags are set. This can "
81 "lead to exploitable memory regions, which could be overwritten "
82 "with malicious code",
84 Report
->addRange(Call
.getArgSourceRange(2));
85 C
.emitReport(std::move(Report
));
90 void ento::registerMmapWriteExecChecker(CheckerManager
&Mgr
) {
91 Mgr
.registerChecker
<MmapWriteExecChecker
>();
94 bool ento::shouldRegisterMmapWriteExecChecker(const CheckerManager
&) {