146 lines · cpp
1//=== StoreToImmutableChecker.cpp - Store to immutable memory ---*- C++ -*-===//2//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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines StoreToImmutableChecker, a checker that detects writes10// to immutable memory regions. This implements part of SEI CERT Rule ENV30-C.11//12//===----------------------------------------------------------------------===//13 14#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"15#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"16#include "clang/StaticAnalyzer/Core/Checker.h"17#include "clang/StaticAnalyzer/Core/CheckerManager.h"18#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"19#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"20 21using namespace clang;22using namespace ento;23 24namespace {25class StoreToImmutableChecker : public Checker<check::Bind> {26 const BugType BT{this, "Write to immutable memory", "CERT Environment (ENV)"};27 28public:29 void checkBind(SVal Loc, SVal Val, const Stmt *S, bool AtDeclInit,30 CheckerContext &C) const;31};32} // end anonymous namespace33 34static bool isEffectivelyConstRegion(const MemRegion *MR, CheckerContext &C) {35 if (isa<GlobalImmutableSpaceRegion>(MR))36 return true;37 38 // Check if this is a TypedRegion with a const-qualified type39 if (const auto *TR = dyn_cast<TypedRegion>(MR)) {40 QualType LocationType = TR->getDesugaredLocationType(C.getASTContext());41 if (LocationType->isPointerOrReferenceType())42 LocationType = LocationType->getPointeeType();43 if (LocationType.isConstQualified())44 return true;45 }46 47 // Check if this is a SymbolicRegion with a const-qualified pointee type48 if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) {49 QualType PointeeType = SR->getPointeeStaticType();50 if (PointeeType.isConstQualified())51 return true;52 }53 54 // NOTE: The above branches do not cover AllocaRegion. We do not need to check55 // AllocaRegion, as it models untyped memory, that is allocated on the stack.56 57 return false;58}59 60static const MemRegion *getInnermostConstRegion(const MemRegion *MR,61 CheckerContext &C) {62 while (true) {63 if (isEffectivelyConstRegion(MR, C))64 return MR;65 if (auto *SR = dyn_cast<SubRegion>(MR))66 MR = SR->getSuperRegion();67 else68 return nullptr;69 }70}71 72static const DeclRegion *73getInnermostEnclosingConstDeclRegion(const MemRegion *MR, CheckerContext &C) {74 while (true) {75 if (const auto *DR = dyn_cast<DeclRegion>(MR)) {76 const ValueDecl *D = DR->getDecl();77 QualType DeclaredType = D->getType();78 if (DeclaredType.isConstQualified())79 return DR;80 }81 if (auto *SR = dyn_cast<SubRegion>(MR))82 MR = SR->getSuperRegion();83 else84 return nullptr;85 }86}87 88void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,89 bool AtDeclInit,90 CheckerContext &C) const {91 // We are only interested in stores to memory regions92 const MemRegion *MR = Loc.getAsRegion();93 if (!MR)94 return;95 96 // Skip variable declarations and initializations - we only want to catch97 // actual writes98 if (AtDeclInit)99 return;100 101 // Check if the region is in the global immutable space102 const MemSpaceRegion *MS = MR->getMemorySpace(C.getState());103 const bool IsGlobalImmutableSpace = isa<GlobalImmutableSpaceRegion>(MS);104 // Check if the region corresponds to a const variable105 const MemRegion *InnermostConstRegion = getInnermostConstRegion(MR, C);106 if (!IsGlobalImmutableSpace && !InnermostConstRegion)107 return;108 109 SmallString<64> WarningMessage{"Trying to write to immutable memory"};110 if (IsGlobalImmutableSpace)111 WarningMessage += " in global read-only storage";112 113 // Generate the bug report114 ExplodedNode *N = C.generateNonFatalErrorNode();115 if (!N)116 return;117 118 auto R = std::make_unique<PathSensitiveBugReport>(BT, WarningMessage, N);119 R->addRange(S->getSourceRange());120 121 // Generate a note if the location that is being written to has a122 // declaration or if it is a subregion of a const region with a declaration.123 const DeclRegion *DR =124 getInnermostEnclosingConstDeclRegion(InnermostConstRegion, C);125 if (DR) {126 const char *NoteMessage =127 (DR != MR) ? "Enclosing memory region is declared as immutable here"128 : "Memory region is declared as immutable here";129 R->addNote(NoteMessage, PathDiagnosticLocation::create(130 DR->getDecl(), C.getSourceManager()));131 }132 133 // For this checker, we are only interested in the value being written, no134 // need to mark the value being assigned interesting.135 136 C.emitReport(std::move(R));137}138 139void ento::registerStoreToImmutableChecker(CheckerManager &mgr) {140 mgr.registerChecker<StoreToImmutableChecker>();141}142 143bool ento::shouldRegisterStoreToImmutableChecker(const CheckerManager &mgr) {144 return true;145}146