r326405 - [analyzer] Add a checker for mmap()s which are both writable and executable.

Artem Dergachev via cfe-commits cfe-commits at lists.llvm.org
Wed Feb 28 17:27:46 PST 2018


Author: dergachev
Date: Wed Feb 28 17:27:46 2018
New Revision: 326405

URL: http://llvm.org/viewvc/llvm-project?rev=326405&view=rev
Log:
[analyzer] Add a checker for mmap()s which are both writable and executable.

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

Added:
    cfe/trunk/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
    cfe/trunk/test/Analysis/mmap-writeexec.c
Modified:
    cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td
    cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt

Modified: cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td?rev=326405&r1=326404&r2=326405&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td Wed Feb 28 17:27:46 2018
@@ -414,6 +414,13 @@ def MallocOverflowSecurityChecker : Chec
   HelpText<"Check for overflows in the arguments to malloc()">,
   DescFile<"MallocOverflowSecurityChecker.cpp">;
 
+// Operating systems specific PROT_READ/PROT_WRITE values is not implemented,
+// the defaults are correct for several common operating systems though, 
+// but may need to be overridden via the related analyzer-config flags.
+def MmapWriteExecChecker : Checker<"MmapWriteExec">,
+  HelpText<"Warn on mmap() calls that are both writable and executable">,
+  DescFile<"MmapWriteExecChecker.cpp">;
+
 } // end "alpha.security"
 
 //===----------------------------------------------------------------------===//

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt?rev=326405&r1=326404&r2=326405&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt Wed Feb 28 17:27:46 2018
@@ -49,6 +49,7 @@ add_clang_library(clangStaticAnalyzerChe
   MallocChecker.cpp
   MallocOverflowSecurityChecker.cpp
   MallocSizeofChecker.cpp
+  MmapWriteExecChecker.cpp
   MisusedMovedObjectChecker.cpp
   MPI-Checker/MPIBugReporter.cpp
   MPI-Checker/MPIChecker.cpp

Added: cfe/trunk/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp?rev=326405&view=auto
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp (added)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp Wed Feb 28 17:27:46 2018
@@ -0,0 +1,87 @@
+// 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);
+}

Added: cfe/trunk/test/Analysis/mmap-writeexec.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/mmap-writeexec.c?rev=326405&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/mmap-writeexec.c (added)
+++ cfe/trunk/test/Analysis/mmap-writeexec.c Wed Feb 28 17:27:46 2018
@@ -0,0 +1,36 @@
+// RUN: %clang_analyze_cc1 -triple i686-unknown-linux -analyzer-checker=alpha.security.MmapWriteExec -analyzer-config alpha.security.MmapWriteExec:MmapProtExec=1 -analyzer-config alpha.security.MmapWriteExec:MmapProtRead=4 -DUSE_ALTERNATIVE_PROT_EXEC_DEFINITION -verify %s
+// RUN: %clang_analyze_cc1 -triple x86_64-unknown-apple-darwin10 -analyzer-checker=alpha.security.MmapWriteExec -verify %s
+
+#define PROT_WRITE  0x02
+#ifndef USE_ALTERNATIVE_PROT_EXEC_DEFINITION
+#define PROT_EXEC   0x04
+#define PROT_READ   0x01
+#else
+#define PROT_EXEC   0x01
+#define PROT_READ   0x04
+#endif
+#define MAP_PRIVATE 0x0002
+#define MAP_ANON    0x1000
+#define MAP_FIXED   0x0010
+#define NULL        ((void *)0)
+
+typedef __typeof(sizeof(int)) size_t;
+void *mmap(void *, size_t, int, int, int, long);
+
+void f1()
+{
+  void *a = mmap(NULL, 16, PROT_READ | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0); // no-warning
+  void *b = mmap(a, 16, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_ANON, -1, 0); // no-warning
+  void *c = mmap(NULL, 32, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0); // expected-warning{{Both PROT_WRITE and PROT_EXEC flags are set. This can lead to exploitable memory regions, which could be overwritten with malicious code}}
+  (void)a;
+  (void)b;
+  (void)c;
+}
+
+void f2()
+{
+  void *(*callm)(void *, size_t, int, int, int, long);
+  callm = mmap;
+  int prot = PROT_WRITE | PROT_EXEC;
+  (void)callm(NULL, 1024, prot, MAP_PRIVATE | MAP_ANON, -1, 0); // expected-warning{{Both PROT_WRITE and PROT_EXEC flags are set. This can lead to exploitable memory regions, which could be overwritten with malicious code}}
+}




More information about the cfe-commits mailing list