https://github.com/gamesh411 updated https://github.com/llvm/llvm-project/pull/150417
From 0630d81e544319a1a18798996760775d4a13f7ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Thu, 24 Jul 2025 14:49:14 +0200 Subject: [PATCH 01/20] [clang][analyzer] Add StoreToImmutable checker This adds alpha.core.StoreToImmutable, a new alpha checker that detects writes to immutable memory regions, implementing part of SEI CERT Rule ENV30-C. The original proposal only handled global const variables, but this implementation extends it to detect writes to: - Global const variables - Local const variables - String literals - Const parameters and struct members - Const arrays and pointers to const data This checker is the continuation of the work started by @zukatsinadze. Discussion: https://reviews.llvm.org/D124244 --- clang/docs/analyzer/checkers.rst | 17 ++ .../checkers/storetoimmutable_example.cpp | 30 ++++ .../clang/StaticAnalyzer/Checkers/Checkers.td | 5 + .../Core/PathSensitive/MemRegion.h | 2 +- .../StaticAnalyzer/Checkers/CMakeLists.txt | 1 + .../Checkers/StoreToImmutableChecker.cpp | 146 +++++++++++++++ .../Analysis/store-to-immutable-basic.cpp | 166 ++++++++++++++++++ 7 files changed, 366 insertions(+), 1 deletion(-) create mode 100644 clang/docs/analyzer/checkers/storetoimmutable_example.cpp create mode 100644 clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp create mode 100644 clang/test/Analysis/store-to-immutable-basic.cpp diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index 26c5028e04955..a00e9d81ba208 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -3077,6 +3077,23 @@ Either the comparison is useless or there is division by zero. if (x == 0) { } // warn } +.. _alpha-core-StoreToImmutable: + +alpha.core.StoreToImmutable (C, C++) +"""""""""""""""""""""""""""""""""""" +Check for writes to immutable memory regions. This implements part of SEI CERT Rule ENV30-C. + +This checker detects attempts to write to memory regions that are marked as immutable, +including const variables, string literals, and other const-qualified memory. + +.. literalinclude:: checkers/storetoimmutable_example.cpp + :language: cpp + +**Solution** + +Avoid writing to const-qualified memory regions. If you need to modify the data, +remove the const qualifier from the original declaration or use a mutable copy. + alpha.cplusplus ^^^^^^^^^^^^^^^ diff --git a/clang/docs/analyzer/checkers/storetoimmutable_example.cpp b/clang/docs/analyzer/checkers/storetoimmutable_example.cpp new file mode 100644 index 0000000000000..e1a0683ff91e4 --- /dev/null +++ b/clang/docs/analyzer/checkers/storetoimmutable_example.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable %s + +// Global const variable +const int global_const = 42; + +void test_global_const() { + *(int *)&global_const = 100; // warn: Writing to immutable memory +} + +// String literal +void test_string_literal() { + char *str = (char *)"hello"; + str[0] = 'H'; // warn: Writing to immutable memory +} + +// Const parameter +void test_const_param(const int param) { + *(int *)¶m = 100; // warn: Writing to immutable memory +} + +// Const struct member +struct TestStruct { + const int x; + int y; +}; + +void test_const_member() { + TestStruct s = {1, 2}; + *(int *)&s.x = 10; // warn: Writing to immutable memory +} \ No newline at end of file diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 2234143004b6f..8799f1d15b0b6 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -306,6 +306,11 @@ def StackAddrAsyncEscapeChecker : Checker<"StackAddressAsyncEscape">, Dependencies<[StackAddrEscapeBase]>, Documentation<HasDocumentation>; +def StoreToImmutableChecker : Checker<"StoreToImmutable">, + HelpText<"Check for writes to immutable memory regions. " + "This implements part of SEI CERT Rule ENV30-C.">, + Documentation<HasDocumentation>; + def PthreadLockBase : Checker<"PthreadLockBase">, HelpText<"Helper registering multiple checks.">, Documentation<NotDocumented>, diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index 89d306fb94046..0f7bab6839703 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -819,7 +819,7 @@ class SymbolicRegion : public SubRegion { s->getType()->isReferenceType() || s->getType()->isBlockPointerType()); assert(isa<UnknownSpaceRegion>(sreg) || isa<HeapSpaceRegion>(sreg) || - isa<GlobalSystemSpaceRegion>(sreg)); + isa<GlobalSystemSpaceRegion>(sreg) || isa<GlobalImmutableSpaceRegion>(sreg)); } public: diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 22dd3f0374849..78360418a8b81 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -104,6 +104,7 @@ add_clang_library(clangStaticAnalyzerCheckers SmartPtrChecker.cpp SmartPtrModeling.cpp StackAddrEscapeChecker.cpp + StoreToImmutableChecker.cpp StdLibraryFunctionsChecker.cpp StdVariantChecker.cpp STLAlgorithmModeling.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp new file mode 100644 index 0000000000000..a853e2e3f9c4a --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp @@ -0,0 +1,146 @@ +//=== StoreToImmutableChecker.cpp - Store to immutable memory checker -*- C++ +//-*-===// +// +// 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 file defines StoreToImmutableChecker, a checker that detects writes +// to immutable memory regions. This implements part of SEI CERT Rule ENV30-C. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" + +using namespace clang; +using namespace ento; + +namespace { +class StoreToImmutableChecker : public Checker<check::Bind> { + const BugType BT{this, "Write to immutable memory", "CERT Environment (ENV)"}; + +public: + void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; + +private: + bool isConstVariable(const MemRegion *MR, CheckerContext &C) const; + bool isConstQualifiedType(const MemRegion *MR, CheckerContext &C) const; +}; +} // end anonymous namespace + +bool StoreToImmutableChecker::isConstVariable(const MemRegion *MR, + CheckerContext &C) const { + // Check if the region is in the global immutable space + const MemSpaceRegion *MS = MR->getMemorySpace(C.getState()); + if (isa<GlobalImmutableSpaceRegion>(MS)) + return true; + + // Check if this is a VarRegion with a const-qualified type + if (const VarRegion *VR = dyn_cast<VarRegion>(MR)) { + const VarDecl *VD = VR->getDecl(); + if (VD && VD->getType().isConstQualified()) + return true; + } + + // Check if this is a ParamVarRegion with a const-qualified type + if (const ParamVarRegion *PVR = dyn_cast<ParamVarRegion>(MR)) { + const ParmVarDecl *PVD = PVR->getDecl(); + if (PVD && PVD->getType().isConstQualified()) + return true; + } + + // Check if this is a FieldRegion with a const-qualified type + if (const FieldRegion *FR = dyn_cast<FieldRegion>(MR)) { + const FieldDecl *FD = FR->getDecl(); + if (FD && FD->getType().isConstQualified()) + return true; + } + + // Check if this is a StringRegion (string literals are const) + if (isa<StringRegion>(MR)) + return true; + + // Check if this is a SymbolicRegion with a const-qualified pointee type + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) { + QualType PointeeType = SR->getPointeeStaticType(); + if (PointeeType.isConstQualified()) + return true; + } + + // Check if this is an ElementRegion accessing a const array + if (const ElementRegion *ER = dyn_cast<ElementRegion>(MR)) { + return isConstQualifiedType(ER->getSuperRegion(), C); + } + + return false; +} + +bool StoreToImmutableChecker::isConstQualifiedType(const MemRegion *MR, + CheckerContext &C) const { + // Check if the region has a const-qualified type + if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR)) { + QualType Ty = TVR->getValueType(); + return Ty.isConstQualified(); + } + return false; +} + +void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, + CheckerContext &C) const { + // We are only interested in stores to memory regions + const MemRegion *MR = Loc.getAsRegion(); + if (!MR) + return; + + // Skip variable declarations and initializations - we only want to catch + // actual writes + if (isa<DeclStmt>(S) || isa<DeclRefExpr>(S)) + return; + + // Check if the region corresponds to a const variable + if (!isConstVariable(MR, C)) + return; + + const SourceManager &SM = C.getSourceManager(); + // Skip if this is a system macro (likely from system headers) + if (SM.isInSystemMacro(S->getBeginLoc())) + return; + + // Generate the bug report + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; + + constexpr llvm::StringLiteral Msg = + "Writing to immutable memory is undefined behavior. " + "This memory region is marked as immutable and should not be modified."; + + auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); + R->addRange(S->getSourceRange()); + + // If the location that is being written to has a declaration, place a note. + if (const DeclRegion *DR = dyn_cast<DeclRegion>(MR)) { + R->addNote("Memory region is in immutable space", + PathDiagnosticLocation::create(DR->getDecl(), SM)); + } + + // For this checker, we are only interested in the value being written, no + // need to mark the value being assigned interesting. + + C.emitReport(std::move(R)); +} + +void ento::registerStoreToImmutableChecker(CheckerManager &mgr) { + mgr.registerChecker<StoreToImmutableChecker>(); +} + +bool ento::shouldRegisterStoreToImmutableChecker(const CheckerManager &mgr) { + return true; +} \ No newline at end of file diff --git a/clang/test/Analysis/store-to-immutable-basic.cpp b/clang/test/Analysis/store-to-immutable-basic.cpp new file mode 100644 index 0000000000000..2b28b933e97b4 --- /dev/null +++ b/clang/test/Analysis/store-to-immutable-basic.cpp @@ -0,0 +1,166 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable -verify %s + +// Test basic functionality of StoreToImmutable checker +// This tests direct writes to immutable regions without function modeling + +// Direct write to a const global variable +const int global_const = 42; // expected-note {{Memory region is in immutable space}} + +void test_direct_write_to_const_global() { + // This should trigger a warning about writing to immutable memory + *(int*)&global_const = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +// Write through a pointer to const memory +void test_write_through_const_pointer() { + const int local_const = 10; // expected-note {{Memory region is in immutable space}} + int *ptr = (int*)&local_const; + *ptr = 20; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +// Write to string literal (should be in immutable space) +void test_write_to_string_literal() { + char *str = (char*)"hello"; + str[0] = 'H'; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +// Write to const array +void test_write_to_const_array() { + const int arr[5] = {1, 2, 3, 4, 5}; + int *ptr = (int*)arr; + ptr[0] = 10; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +// Write to const struct member +struct TestStruct { + const int x; // expected-note 2{{Memory region is in immutable space}} + int y; +}; + +void test_write_to_const_struct_member() { + TestStruct s = {1, 2}; + int *ptr = (int*)&s.x; + *ptr = 10; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +// Write to const global array +const int global_array[3] = {1, 2, 3}; + +void test_write_to_const_global_array() { + int *ptr = (int*)global_array; + ptr[0] = 10; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +// Write to const global struct +const TestStruct global_struct = {1, 2}; + +void test_write_to_const_global_struct() { + int *ptr = (int*)&global_struct.x; + *ptr = 10; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +// Write to const parameter +void test_write_to_const_param(const int param) { // expected-note {{Memory region is in immutable space}} + *(int*)¶m = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +// Write to const reference parameter +void test_write_to_const_ref_param(const int ¶m) { + *(int*)¶m = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +// Write to const pointer parameter +void test_write_to_const_ptr_param(const int *param) { + *(int*)param = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +// Write to const array parameter +void test_write_to_const_array_param(const int arr[5]) { + *(int*)arr = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +// Write to const struct parameter +struct ParamStruct { + const int z; // expected-note 3{{Memory region is in immutable space}} + int w; +}; + +void test_write_to_const_struct_param(const ParamStruct s) { + *(int*)&s.z = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +// Write to const struct reference parameter +void test_write_to_const_struct_ref_param(const ParamStruct &s) { + *(int*)&s.z = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +// Write to const struct pointer parameter +void test_write_to_const_struct_ptr_param(const ParamStruct *s) { + *(int*)&s->z = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +//===--- NEGATIVE TEST CASES ---===// +// These tests should NOT trigger warnings + +// Write to non-const variable (should not warn) +void test_write_to_nonconst() { + int non_const = 42; + *(int*)&non_const = 100; // No warning expected +} + +// Write to non-const global variable (should not warn) +int global_non_const = 42; + +void test_write_to_nonconst_global() { + *(int*)&global_non_const = 100; // No warning expected +} + +// Write to non-const struct member (should not warn) +struct NonConstStruct { + int x; + int y; +}; + +void test_write_to_nonconst_struct_member() { + NonConstStruct s = {1, 2}; + *(int*)&s.x = 100; // No warning expected +} + +// Write to non-const parameter (should not warn) +void test_write_to_nonconst_param(int param) { + *(int*)¶m = 100; // No warning expected +} + +// Normal assignment to non-const variable (should not warn) +void test_normal_assignment() { + int x = 42; + x = 100; // No warning expected +} + +// Write to non-const data through const pointer (should not warn - underlying memory is non-const) +void test_const_ptr_to_nonconst_data() { + int data = 42; + const int *ptr = &data; + *(int*)ptr = 100; // No warning expected +} + +// Write to non-const data through const reference (should not warn - underlying memory is non-const) +void test_const_ref_to_nonconst_data() { + int data = 42; + const int &ref = data; + *(int*)&ref = 100; // No warning expected +} \ No newline at end of file From fa3f84f2dd3efb811d9c5fa006714279503f1d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Fri, 25 Jul 2025 03:21:19 +0200 Subject: [PATCH 02/20] Apply review suggestiongs by @steakhal --- .../Core/PathSensitive/MemRegion.h | 2 +- .../Checkers/StoreToImmutableChecker.cpp | 18 +- .../test/Analysis/store-to-immutable-basic.c | 138 +++++++++++++++ .../Analysis/store-to-immutable-basic.cpp | 157 ++---------------- 4 files changed, 156 insertions(+), 159 deletions(-) create mode 100644 clang/test/Analysis/store-to-immutable-basic.c diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index 0f7bab6839703..89d306fb94046 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -819,7 +819,7 @@ class SymbolicRegion : public SubRegion { s->getType()->isReferenceType() || s->getType()->isBlockPointerType()); assert(isa<UnknownSpaceRegion>(sreg) || isa<HeapSpaceRegion>(sreg) || - isa<GlobalSystemSpaceRegion>(sreg) || isa<GlobalImmutableSpaceRegion>(sreg)); + isa<GlobalSystemSpaceRegion>(sreg)); } public: diff --git a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp index a853e2e3f9c4a..73c75b74451f1 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp @@ -1,5 +1,4 @@ -//=== StoreToImmutableChecker.cpp - Store to immutable memory checker -*- C++ -//-*-===// +//=== StoreToImmutableChecker.cpp - Store to immutable memory ---*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -49,13 +48,6 @@ bool StoreToImmutableChecker::isConstVariable(const MemRegion *MR, return true; } - // Check if this is a ParamVarRegion with a const-qualified type - if (const ParamVarRegion *PVR = dyn_cast<ParamVarRegion>(MR)) { - const ParmVarDecl *PVD = PVR->getDecl(); - if (PVD && PVD->getType().isConstQualified()) - return true; - } - // Check if this is a FieldRegion with a const-qualified type if (const FieldRegion *FR = dyn_cast<FieldRegion>(MR)) { const FieldDecl *FD = FR->getDecl(); @@ -63,10 +55,6 @@ bool StoreToImmutableChecker::isConstVariable(const MemRegion *MR, return true; } - // Check if this is a StringRegion (string literals are const) - if (isa<StringRegion>(MR)) - return true; - // Check if this is a SymbolicRegion with a const-qualified pointee type if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) { QualType PointeeType = SR->getPointeeStaticType(); @@ -101,7 +89,7 @@ void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, // Skip variable declarations and initializations - we only want to catch // actual writes - if (isa<DeclStmt>(S) || isa<DeclRefExpr>(S)) + if (isa<DeclStmt, DeclRefExpr>(S)) return; // Check if the region corresponds to a const variable @@ -143,4 +131,4 @@ void ento::registerStoreToImmutableChecker(CheckerManager &mgr) { bool ento::shouldRegisterStoreToImmutableChecker(const CheckerManager &mgr) { return true; -} \ No newline at end of file +} diff --git a/clang/test/Analysis/store-to-immutable-basic.c b/clang/test/Analysis/store-to-immutable-basic.c new file mode 100644 index 0000000000000..eee4388c2446c --- /dev/null +++ b/clang/test/Analysis/store-to-immutable-basic.c @@ -0,0 +1,138 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable -verify %s + +// Test basic functionality of StoreToImmutable checker for the C programming language. + +const int tentative_global_const; // expected-note {{Memory region is in immutable space}} + +void test_direct_write_to_tentative_const_global() { + *(int*)&tentative_global_const = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +const int global_const = 42; // expected-note {{Memory region is in immutable space}} + +void test_direct_write_to_const_global() { + // This should trigger a warning about writing to immutable memory + *(int*)&global_const = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +void test_write_through_const_pointer() { + const int local_const = 10; // expected-note {{Memory region is in immutable space}} + int *ptr = (int*)&local_const; + *ptr = 20; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +void test_write_to_const_array() { + const int arr[5] = {1, 2, 3, 4, 5}; + int *ptr = (int*)arr; + ptr[0] = 10; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +struct TestStruct { + const int x; // expected-note 2 {{Memory region is in immutable space}} + int y; +}; + +void test_write_to_const_struct_member() { + struct TestStruct s = {1, 2}; + int *ptr = (int*)&s.x; + *ptr = 10; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +const int global_array[3] = {1, 2, 3}; + +void test_write_to_const_global_array() { + int *ptr = (int*)global_array; + ptr[0] = 10; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + +} + +const struct TestStruct global_struct = {1, 2}; + +void test_write_to_const_global_struct() { + int *ptr = (int*)&global_struct.x; + *ptr = 10; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +void test_write_to_const_param(const int param) { // expected-note {{Memory region is in immutable space}} + *(int*)¶m = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +void test_write_to_const_ptr_param(const int *param) { + *(int*)param = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +void test_write_to_const_array_param(const int arr[5]) { + *(int*)arr = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +struct ParamStruct { + const int z; // expected-note 2 {{Memory region is in immutable space}} + int w; +}; + +void test_write_to_const_struct_param(const struct ParamStruct s) { + *(int*)&s.z = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + +} + +void test_write_to_const_struct_ptr_param(const struct ParamStruct *s) { + *(int*)&s->z = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +void test_write_to_nonconst() { + int non_const = 42; + *(int*)&non_const = 100; // No warning expected +} + +int global_non_const = 42; + +void test_write_to_nonconst_global() { + *(int*)&global_non_const = 100; // No warning expected +} + +struct NonConstStruct { + int x; + int y; +}; + +void test_write_to_nonconst_struct_member() { + struct NonConstStruct s = {1, 2}; + *(int*)&s.x = 100; // No warning expected +} + +void test_write_to_nonconst_param(int param) { + *(int*)¶m = 100; // No warning expected +} + +void test_normal_assignment() { + int x = 42; + x = 100; // No warning expected +} + +void test_const_ptr_to_nonconst_data() { + int data = 42; + const int *ptr = &data; + *(int*)ptr = 100; // No warning expected +} + +void test_const_ptr_to_const_data() { + const int data = 42; // expected-note {{Memory region is in immutable space}} + const int *ptr = &data; + *(int*)ptr = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + + + diff --git a/clang/test/Analysis/store-to-immutable-basic.cpp b/clang/test/Analysis/store-to-immutable-basic.cpp index 2b28b933e97b4..5014ad66ceb7b 100644 --- a/clang/test/Analysis/store-to-immutable-basic.cpp +++ b/clang/test/Analysis/store-to-immutable-basic.cpp @@ -1,166 +1,37 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable -verify %s -// Test basic functionality of StoreToImmutable checker -// This tests direct writes to immutable regions without function modeling +// Test the inteaction of the StoreToImmutable checker with C++ references. -// Direct write to a const global variable -const int global_const = 42; // expected-note {{Memory region is in immutable space}} - -void test_direct_write_to_const_global() { - // This should trigger a warning about writing to immutable memory - *(int*)&global_const = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} - -// Write through a pointer to const memory -void test_write_through_const_pointer() { - const int local_const = 10; // expected-note {{Memory region is in immutable space}} - int *ptr = (int*)&local_const; - *ptr = 20; // expected-warning {{Writing to immutable memory is undefined behavior}} +void test_write_to_const_ref_param(const int ¶m) { + *(int*)¶m = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } -// Write to string literal (should be in immutable space) void test_write_to_string_literal() { char *str = (char*)"hello"; - str[0] = 'H'; // expected-warning {{Writing to immutable memory is undefined behavior}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} - -// Write to const array -void test_write_to_const_array() { - const int arr[5] = {1, 2, 3, 4, 5}; - int *ptr = (int*)arr; - ptr[0] = 10; // expected-warning {{Writing to immutable memory is undefined behavior}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} - -// Write to const struct member -struct TestStruct { - const int x; // expected-note 2{{Memory region is in immutable space}} - int y; -}; - -void test_write_to_const_struct_member() { - TestStruct s = {1, 2}; - int *ptr = (int*)&s.x; - *ptr = 10; // expected-warning {{Writing to immutable memory is undefined behavior}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} - -// Write to const global array -const int global_array[3] = {1, 2, 3}; - -void test_write_to_const_global_array() { - int *ptr = (int*)global_array; - ptr[0] = 10; // expected-warning {{Writing to immutable memory is undefined behavior}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} - -// Write to const global struct -const TestStruct global_struct = {1, 2}; - -void test_write_to_const_global_struct() { - int *ptr = (int*)&global_struct.x; - *ptr = 10; // expected-warning {{Writing to immutable memory is undefined behavior}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} - -// Write to const parameter -void test_write_to_const_param(const int param) { // expected-note {{Memory region is in immutable space}} - *(int*)¶m = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} - -// Write to const reference parameter -void test_write_to_const_ref_param(const int ¶m) { - *(int*)¶m = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} - -// Write to const pointer parameter -void test_write_to_const_ptr_param(const int *param) { - *(int*)param = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} - -// Write to const array parameter -void test_write_to_const_array_param(const int arr[5]) { - *(int*)arr = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} + str[0] = 'H'; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } -// Write to const struct parameter struct ParamStruct { - const int z; // expected-note 3{{Memory region is in immutable space}} + const int z; // expected-note {{Memory region is in immutable space}} int w; }; -void test_write_to_const_struct_param(const ParamStruct s) { - *(int*)&s.z = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} - -// Write to const struct reference parameter void test_write_to_const_struct_ref_param(const ParamStruct &s) { - *(int*)&s.z = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} + *(int*)&s.z = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } -// Write to const struct pointer parameter -void test_write_to_const_struct_ptr_param(const ParamStruct *s) { - *(int*)&s->z = 100; // expected-warning {{Writing to immutable memory is undefined behavior}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} - -//===--- NEGATIVE TEST CASES ---===// -// These tests should NOT trigger warnings - -// Write to non-const variable (should not warn) -void test_write_to_nonconst() { - int non_const = 42; - *(int*)&non_const = 100; // No warning expected -} - -// Write to non-const global variable (should not warn) -int global_non_const = 42; - -void test_write_to_nonconst_global() { - *(int*)&global_non_const = 100; // No warning expected -} - -// Write to non-const struct member (should not warn) -struct NonConstStruct { - int x; - int y; -}; - -void test_write_to_nonconst_struct_member() { - NonConstStruct s = {1, 2}; - *(int*)&s.x = 100; // No warning expected -} - -// Write to non-const parameter (should not warn) -void test_write_to_nonconst_param(int param) { - *(int*)¶m = 100; // No warning expected -} - -// Normal assignment to non-const variable (should not warn) -void test_normal_assignment() { - int x = 42; - x = 100; // No warning expected -} - -// Write to non-const data through const pointer (should not warn - underlying memory is non-const) -void test_const_ptr_to_nonconst_data() { - int data = 42; - const int *ptr = &data; - *(int*)ptr = 100; // No warning expected -} - -// Write to non-const data through const reference (should not warn - underlying memory is non-const) void test_const_ref_to_nonconst_data() { int data = 42; const int &ref = data; *(int*)&ref = 100; // No warning expected -} \ No newline at end of file +} + +void test_const_ref_to_const_data() { + const int data = 42; // expected-note {{Memory region is in immutable space}} + const int &ref = data; + *(int*)&ref = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} From b022182d0eb0f2f5b15d81440e216972ae49bef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Mon, 28 Jul 2025 13:56:50 +0200 Subject: [PATCH 03/20] [review-fix] fix test files add fixme comments add const reference test case remove duplicate expected-notes and analyzer-output=text --- .../test/Analysis/store-to-immutable-basic.c | 20 +------------------ .../Analysis/store-to-immutable-basic.cpp | 18 ++++++++++++----- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/clang/test/Analysis/store-to-immutable-basic.c b/clang/test/Analysis/store-to-immutable-basic.c index eee4388c2446c..f098f7fefa28c 100644 --- a/clang/test/Analysis/store-to-immutable-basic.c +++ b/clang/test/Analysis/store-to-immutable-basic.c @@ -6,7 +6,6 @@ const int tentative_global_const; // expected-note {{Memory region is in immutab void test_direct_write_to_tentative_const_global() { *(int*)&tentative_global_const = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } const int global_const = 42; // expected-note {{Memory region is in immutable space}} @@ -14,21 +13,18 @@ const int global_const = 42; // expected-note {{Memory region is in immutable sp void test_direct_write_to_const_global() { // This should trigger a warning about writing to immutable memory *(int*)&global_const = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } void test_write_through_const_pointer() { const int local_const = 10; // expected-note {{Memory region is in immutable space}} int *ptr = (int*)&local_const; *ptr = 20; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } void test_write_to_const_array() { const int arr[5] = {1, 2, 3, 4, 5}; int *ptr = (int*)arr; ptr[0] = 10; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } struct TestStruct { @@ -40,7 +36,6 @@ void test_write_to_const_struct_member() { struct TestStruct s = {1, 2}; int *ptr = (int*)&s.x; *ptr = 10; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } const int global_array[3] = {1, 2, 3}; @@ -48,8 +43,6 @@ const int global_array[3] = {1, 2, 3}; void test_write_to_const_global_array() { int *ptr = (int*)global_array; ptr[0] = 10; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - } const struct TestStruct global_struct = {1, 2}; @@ -57,22 +50,18 @@ const struct TestStruct global_struct = {1, 2}; void test_write_to_const_global_struct() { int *ptr = (int*)&global_struct.x; *ptr = 10; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } void test_write_to_const_param(const int param) { // expected-note {{Memory region is in immutable space}} *(int*)¶m = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } void test_write_to_const_ptr_param(const int *param) { *(int*)param = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } void test_write_to_const_array_param(const int arr[5]) { *(int*)arr = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } struct ParamStruct { @@ -82,13 +71,10 @@ struct ParamStruct { void test_write_to_const_struct_param(const struct ParamStruct s) { *(int*)&s.z = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - } void test_write_to_const_struct_ptr_param(const struct ParamStruct *s) { *(int*)&s->z = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } void test_write_to_nonconst() { @@ -131,8 +117,4 @@ void test_const_ptr_to_const_data() { const int data = 42; // expected-note {{Memory region is in immutable space}} const int *ptr = &data; *(int*)ptr = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} - - - +} \ No newline at end of file diff --git a/clang/test/Analysis/store-to-immutable-basic.cpp b/clang/test/Analysis/store-to-immutable-basic.cpp index 5014ad66ceb7b..4bfc59c075ed0 100644 --- a/clang/test/Analysis/store-to-immutable-basic.cpp +++ b/clang/test/Analysis/store-to-immutable-basic.cpp @@ -1,16 +1,14 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable -verify %s -// Test the inteaction of the StoreToImmutable checker with C++ references. void test_write_to_const_ref_param(const int ¶m) { *(int*)¶m = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } +// FIXME: This should warn in C mode too. void test_write_to_string_literal() { char *str = (char*)"hello"; str[0] = 'H'; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } struct ParamStruct { @@ -20,7 +18,6 @@ struct ParamStruct { void test_write_to_const_struct_ref_param(const ParamStruct &s) { *(int*)&s.z = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } void test_const_ref_to_nonconst_data() { @@ -33,5 +30,16 @@ void test_const_ref_to_const_data() { const int data = 42; // expected-note {{Memory region is in immutable space}} const int &ref = data; *(int*)&ref = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} - // expected-note@-1 {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } + +void test_ref_to_nonconst_data() { + int data = 42; + int &ref = data; + ref = 100; // No warning expected +} + +void test_ref_to_const_data() { + const int data = 42; // expected-note {{Memory region is in immutable space}} + int &ref = *(int*)&data; + ref = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} \ No newline at end of file From 5190ee0538e081ba1dce269c7ac3d3aea8cbc4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Mon, 28 Jul 2025 15:17:42 +0200 Subject: [PATCH 04/20] [review-fix] add test case for complex memory hierarchy --- .../test/Analysis/store-to-immutable-basic.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/clang/test/Analysis/store-to-immutable-basic.cpp b/clang/test/Analysis/store-to-immutable-basic.cpp index 4bfc59c075ed0..ab0841096d9a1 100644 --- a/clang/test/Analysis/store-to-immutable-basic.cpp +++ b/clang/test/Analysis/store-to-immutable-basic.cpp @@ -42,4 +42,22 @@ void test_ref_to_const_data() { const int data = 42; // expected-note {{Memory region is in immutable space}} int &ref = *(int*)&data; ref = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +struct MultipleLayerStruct { + MultipleLayerStruct(); + const int data; // expected-note {{Memory region is in immutable space}} + const int buf[10]; +}; + +MultipleLayerStruct MLS[10]; // mutable global + +void test_multiple_layer_struct_array_member() { + int *p = (int*)&MLS[2].data; + *p = 4; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +} + +void test_multiple_layer_struct_array_array_member() { + int *p = (int*)&MLS[2].buf[3]; + *p = 4; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} } \ No newline at end of file From 856a8651ed4a5e99eb7f433dfc768f16e4266ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Mon, 28 Jul 2025 15:47:28 +0200 Subject: [PATCH 05/20] [review-fix] remove isInSystemMacro check --- .../Checkers/StoreToImmutableChecker.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp index 73c75b74451f1..e1b687795578d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp @@ -96,11 +96,6 @@ void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, if (!isConstVariable(MR, C)) return; - const SourceManager &SM = C.getSourceManager(); - // Skip if this is a system macro (likely from system headers) - if (SM.isInSystemMacro(S->getBeginLoc())) - return; - // Generate the bug report ExplodedNode *N = C.generateNonFatalErrorNode(); if (!N) @@ -115,8 +110,9 @@ void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, // If the location that is being written to has a declaration, place a note. if (const DeclRegion *DR = dyn_cast<DeclRegion>(MR)) { - R->addNote("Memory region is in immutable space", - PathDiagnosticLocation::create(DR->getDecl(), SM)); + R->addNote( + "Memory region is in immutable space", + PathDiagnosticLocation::create(DR->getDecl(), C.getSourceManager())); } // For this checker, we are only interested in the value being written, no From d8f345649b1ce4b44f1ddbfde76ed05187dbaf07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Mon, 28 Jul 2025 17:20:32 +0200 Subject: [PATCH 06/20] [review-fix] add example note on string literal limitation --- clang/docs/analyzer/checkers/storetoimmutable_example.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/docs/analyzer/checkers/storetoimmutable_example.cpp b/clang/docs/analyzer/checkers/storetoimmutable_example.cpp index e1a0683ff91e4..17b43ce68f327 100644 --- a/clang/docs/analyzer/checkers/storetoimmutable_example.cpp +++ b/clang/docs/analyzer/checkers/storetoimmutable_example.cpp @@ -8,6 +8,7 @@ void test_global_const() { } // String literal +// NOTE: This only works in C++, not in C, as the analyzer treats string literals as non-const char arrays in C mode. void test_string_literal() { char *str = (char *)"hello"; str[0] = 'H'; // warn: Writing to immutable memory From 01d0521b687d6718ef445f42d52d782367c5936d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Mon, 28 Jul 2025 18:02:00 +0200 Subject: [PATCH 07/20] [review-fix] implement hierarchical memregion handling introduced an auxiliary function for checking non-ElementRegions we are now checking ElementRegions by walking up the superregion-chain a better name IMO for this function is isEffectivelyConstRegion --- .../Checkers/StoreToImmutableChecker.cpp | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp index e1b687795578d..eee5c900fd6dd 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp @@ -29,29 +29,24 @@ class StoreToImmutableChecker : public Checker<check::Bind> { void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; private: - bool isConstVariable(const MemRegion *MR, CheckerContext &C) const; + bool isEffectivelyConstRegion(const MemRegion *MR, CheckerContext &C) const; bool isConstQualifiedType(const MemRegion *MR, CheckerContext &C) const; }; } // end anonymous namespace -bool StoreToImmutableChecker::isConstVariable(const MemRegion *MR, - CheckerContext &C) const { +static bool isEffectivelyConstRegionAux(const MemRegion *MR, + CheckerContext &C) { // Check if the region is in the global immutable space const MemSpaceRegion *MS = MR->getMemorySpace(C.getState()); if (isa<GlobalImmutableSpaceRegion>(MS)) return true; - // Check if this is a VarRegion with a const-qualified type - if (const VarRegion *VR = dyn_cast<VarRegion>(MR)) { - const VarDecl *VD = VR->getDecl(); - if (VD && VD->getType().isConstQualified()) - return true; - } - - // Check if this is a FieldRegion with a const-qualified type - if (const FieldRegion *FR = dyn_cast<FieldRegion>(MR)) { - const FieldDecl *FD = FR->getDecl(); - if (FD && FD->getType().isConstQualified()) + // Check if this is a TypedRegion with a const-qualified type + if (const TypedRegion *TR = dyn_cast<TypedRegion>(MR)) { + QualType LocationType = TR->getDesugaredLocationType(C.getASTContext()); + if (LocationType->isPointerOrReferenceType()) + LocationType = LocationType->getPointeeType(); + if (LocationType.isConstQualified()) return true; } @@ -62,22 +57,33 @@ bool StoreToImmutableChecker::isConstVariable(const MemRegion *MR, return true; } - // Check if this is an ElementRegion accessing a const array - if (const ElementRegion *ER = dyn_cast<ElementRegion>(MR)) { - return isConstQualifiedType(ER->getSuperRegion(), C); - } + // NOTE: The only kind of region that is not checked by the above branches is + // AllocaRegion. We do not need to check AllocaRegion, as it models untyped + // memory, that is allocated on the stack. return false; } -bool StoreToImmutableChecker::isConstQualifiedType(const MemRegion *MR, - CheckerContext &C) const { - // Check if the region has a const-qualified type - if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR)) { - QualType Ty = TVR->getValueType(); - return Ty.isConstQualified(); +bool StoreToImmutableChecker::isEffectivelyConstRegion( + const MemRegion *MR, CheckerContext &C) const { + // If the region is an ElementRegion, we need to check if any of the super + // regions have const-qualified type. + if (const ElementRegion *ER = dyn_cast<ElementRegion>(MR)) { + SmallVector<const MemRegion *, 8> SuperRegions; + const MemRegion *Current = MR; + const MemRegion *Base = ER->getBaseRegion(); + while (Current != Base) { + SuperRegions.push_back(Current); + assert(isa<SubRegion>(Current)); + Current = cast<SubRegion>(Current)->getSuperRegion(); + } + SuperRegions.push_back(Base); + return llvm::any_of(SuperRegions, [&C](const MemRegion *MR) { + return isEffectivelyConstRegionAux(MR, C); + }); } - return false; + + return isEffectivelyConstRegionAux(MR, C); } void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, @@ -93,7 +99,7 @@ void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, return; // Check if the region corresponds to a const variable - if (!isConstVariable(MR, C)) + if (!isEffectivelyConstRegion(MR, C)) return; // Generate the bug report From 2aacf92b320689af6979a18814007babe0b48bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Tue, 29 Jul 2025 16:01:25 +0200 Subject: [PATCH 08/20] [cornercase] Lambda initialization gives a false positive in C++14 and below these test cases assert the correct behavior, however without a fix for detecting that we are currently initializing a lambda, these fail --- clang/test/Analysis/store-to-immutable-basic.cpp | 3 +-- clang/test/Analysis/store-to-immutable-lambda-init.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 clang/test/Analysis/store-to-immutable-lambda-init.cpp diff --git a/clang/test/Analysis/store-to-immutable-basic.cpp b/clang/test/Analysis/store-to-immutable-basic.cpp index ab0841096d9a1..232f7ac66179c 100644 --- a/clang/test/Analysis/store-to-immutable-basic.cpp +++ b/clang/test/Analysis/store-to-immutable-basic.cpp @@ -1,5 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable -verify %s - +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable -std=c++17 -verify %s void test_write_to_const_ref_param(const int ¶m) { *(int*)¶m = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} diff --git a/clang/test/Analysis/store-to-immutable-lambda-init.cpp b/clang/test/Analysis/store-to-immutable-lambda-init.cpp new file mode 100644 index 0000000000000..b102b816946ed --- /dev/null +++ b/clang/test/Analysis/store-to-immutable-lambda-init.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable -std=c++14 -verify %s + +// expected-no-diagnostics + +// In C++14 and before, when initializing a lambda, the statement given in the checkBind callback is not the whole DeclExpr, but the CXXConstructExpr of the lambda object. +// FIXME: Once the API of checkBind provides more information about the statement, the checker should be simplified, and this this test case will no longer be a cornercase in the checker. + +void test_const_lambda_initialization_pre_cpp17() { + const auto lambda = [](){}; // No warning expected +} \ No newline at end of file From 6e8a3322cc4344a67a3f996f1730fe808c125770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Tue, 29 Jul 2025 21:00:37 +0200 Subject: [PATCH 09/20] [format] fixed example file code formatting --- clang/docs/analyzer/checkers/storetoimmutable_example.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/docs/analyzer/checkers/storetoimmutable_example.cpp b/clang/docs/analyzer/checkers/storetoimmutable_example.cpp index 17b43ce68f327..cba977b6a6792 100644 --- a/clang/docs/analyzer/checkers/storetoimmutable_example.cpp +++ b/clang/docs/analyzer/checkers/storetoimmutable_example.cpp @@ -8,7 +8,8 @@ void test_global_const() { } // String literal -// NOTE: This only works in C++, not in C, as the analyzer treats string literals as non-const char arrays in C mode. +// NOTE: This only works in C++, not in C, as the analyzer treats string +// literals as non-const char arrays in C mode. void test_string_literal() { char *str = (char *)"hello"; str[0] = 'H'; // warn: Writing to immutable memory From d65aa88776ecc52e271e2affaf3a587068fd19a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Tue, 29 Jul 2025 21:01:26 +0200 Subject: [PATCH 10/20] [review-fix] don't repeat type names --- .../lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp index eee5c900fd6dd..824323cc5bbbf 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp @@ -42,7 +42,7 @@ static bool isEffectivelyConstRegionAux(const MemRegion *MR, return true; // Check if this is a TypedRegion with a const-qualified type - if (const TypedRegion *TR = dyn_cast<TypedRegion>(MR)) { + if (const auto *TR = dyn_cast<TypedRegion>(MR)) { QualType LocationType = TR->getDesugaredLocationType(C.getASTContext()); if (LocationType->isPointerOrReferenceType()) LocationType = LocationType->getPointeeType(); @@ -51,7 +51,7 @@ static bool isEffectivelyConstRegionAux(const MemRegion *MR, } // Check if this is a SymbolicRegion with a const-qualified pointee type - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) { + if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) { QualType PointeeType = SR->getPointeeStaticType(); if (PointeeType.isConstQualified()) return true; @@ -68,7 +68,7 @@ bool StoreToImmutableChecker::isEffectivelyConstRegion( const MemRegion *MR, CheckerContext &C) const { // If the region is an ElementRegion, we need to check if any of the super // regions have const-qualified type. - if (const ElementRegion *ER = dyn_cast<ElementRegion>(MR)) { + if (const auto *ER = dyn_cast<ElementRegion>(MR)) { SmallVector<const MemRegion *, 8> SuperRegions; const MemRegion *Current = MR; const MemRegion *Base = ER->getBaseRegion(); From 7e94b106511bdcf6073cf16f1ad25d075e037dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Tue, 29 Jul 2025 21:02:58 +0200 Subject: [PATCH 11/20] [cornercase] fix false positive cornercase this cornercase has a test case in a previous commit also with the tag [cornercase] --- .../Checkers/StoreToImmutableChecker.cpp | 55 ++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp index 824323cc5bbbf..cbd3362f61bcf 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ParentMap.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -29,11 +30,59 @@ class StoreToImmutableChecker : public Checker<check::Bind> { void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; private: + bool isInitializationContext(const Stmt *S, CheckerContext &C) const; bool isEffectivelyConstRegion(const MemRegion *MR, CheckerContext &C) const; - bool isConstQualifiedType(const MemRegion *MR, CheckerContext &C) const; }; } // end anonymous namespace +bool StoreToImmutableChecker::isInitializationContext(const Stmt *S, + CheckerContext &C) const { + // Check if this is a DeclStmt (variable declaration) + if (isa<DeclStmt>(S)) + return true; + + // This part is specific for initialization of const lambdas pre-C++17. + // Lets look at the AST of the statement: + // ``` + // const auto lambda = [](){}; + // ``` + // + // The relevant part of the AST for this case prior to C++17 is: + // ... + // `-DeclStmt + // `-VarDecl + // `-ExprWithCleanups + // `-CXXConstructExpr + // ... + // In C++17 and later, the AST is different: + // ... + // `-DeclStmt + // `-VarDecl + // `-ImplicitCastExpr + // `-LambdaExpr + // |-CXXRecordDecl + // `-CXXConstructExpr + // ... + // And even beside this, the statement `S` that is given to the checkBind + // callback is the VarDecl in C++17 and later, and the CXXConstructExpr in + // C++14 and before. So in order to support the C++14 we need the following + // ugly hack to detect whether this construction is used to initialize a + // variable. + // + // FIXME: This should be eliminated once the API of checkBind would allow to + // distinguish between initialization and assignment, because this information + // is already available in the engine, it is just not passed to the checker + // API. + if (!isa<CXXConstructExpr>(S)) + return false; + + // We use elidable construction to detect initialization. + if (cast<CXXConstructExpr>(S)->isElidable()) + return true; + + return false; +} + static bool isEffectivelyConstRegionAux(const MemRegion *MR, CheckerContext &C) { // Check if the region is in the global immutable space @@ -95,7 +144,9 @@ void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, // Skip variable declarations and initializations - we only want to catch // actual writes - if (isa<DeclStmt, DeclRefExpr>(S)) + // FIXME: If the API of checkBind would allow to distinguish between + // initialization and assignment, we could use that instead. + if (isInitializationContext(S, C)) return; // Check if the region corresponds to a const variable From 4db2804b69db14625d6b673eb28da58e36cbe15e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Wed, 30 Jul 2025 16:25:15 +0200 Subject: [PATCH 12/20] [review-fix] streamline example file removed runline collapsed the functions into a single one for brevity --- .../checkers/storetoimmutable_example.cpp | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/clang/docs/analyzer/checkers/storetoimmutable_example.cpp b/clang/docs/analyzer/checkers/storetoimmutable_example.cpp index cba977b6a6792..5bd5cc93eee20 100644 --- a/clang/docs/analyzer/checkers/storetoimmutable_example.cpp +++ b/clang/docs/analyzer/checkers/storetoimmutable_example.cpp @@ -1,32 +1,23 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable %s - // Global const variable const int global_const = 42; -void test_global_const() { - *(int *)&global_const = 100; // warn: Writing to immutable memory -} - -// String literal -// NOTE: This only works in C++, not in C, as the analyzer treats string -// literals as non-const char arrays in C mode. -void test_string_literal() { - char *str = (char *)"hello"; - str[0] = 'H'; // warn: Writing to immutable memory -} - -// Const parameter -void test_const_param(const int param) { - *(int *)¶m = 100; // warn: Writing to immutable memory -} - // Const struct member struct TestStruct { const int x; int y; }; -void test_const_member() { +void immutable_violation_examples() { + *(int *)&global_const = 100; // warn: Trying to write to immutable memory + + const int local_const = 42; + *(int *)&local_const = 43; // warn: Trying to write to immutable memory + + // NOTE: The following works in C++, but not in C, as the analyzer treats + // string literals as non-const char arrays in C mode. + char *ptr_to_str_literal = (char *)"hello"; + ptr_to_str_literal[0] = 'H'; // warn: Trying to write to immutable memory + TestStruct s = {1, 2}; - *(int *)&s.x = 10; // warn: Writing to immutable memory -} \ No newline at end of file + *(int *)&s.x = 10; // warn: Trying to write to immutable memory +} From 7e731770c9433e06746ede2e63b01c899629a7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Wed, 30 Jul 2025 17:31:18 +0200 Subject: [PATCH 13/20] [review-fix] add more C++ standard versions --- clang/test/Analysis/store-to-immutable-lambda-init.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clang/test/Analysis/store-to-immutable-lambda-init.cpp b/clang/test/Analysis/store-to-immutable-lambda-init.cpp index b102b816946ed..bb93dd8784e38 100644 --- a/clang/test/Analysis/store-to-immutable-lambda-init.cpp +++ b/clang/test/Analysis/store-to-immutable-lambda-init.cpp @@ -1,4 +1,7 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable -std=c++11 -verify %s // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable -std=c++14 -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable -std=c++17 -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable -std=c++20 -verify %s // expected-no-diagnostics @@ -7,4 +10,4 @@ void test_const_lambda_initialization_pre_cpp17() { const auto lambda = [](){}; // No warning expected -} \ No newline at end of file +} From cac94fe04bf6e3f93f32dcfa7dc3ade272b57d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Wed, 30 Jul 2025 17:57:58 +0200 Subject: [PATCH 14/20] [review-fix] streamline implementation change warning and note message simplify cornercase detection --- .../Checkers/StoreToImmutableChecker.cpp | 17 ++------ .../test/Analysis/store-to-immutable-basic.c | 42 +++++++++---------- .../Analysis/store-to-immutable-basic.cpp | 26 ++++++------ 3 files changed, 38 insertions(+), 47 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp index cbd3362f61bcf..8868e290c85bb 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp @@ -11,7 +11,6 @@ // //===----------------------------------------------------------------------===// -#include "clang/AST/ParentMap.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -73,14 +72,8 @@ bool StoreToImmutableChecker::isInitializationContext(const Stmt *S, // distinguish between initialization and assignment, because this information // is already available in the engine, it is just not passed to the checker // API. - if (!isa<CXXConstructExpr>(S)) - return false; - - // We use elidable construction to detect initialization. - if (cast<CXXConstructExpr>(S)->isElidable()) - return true; - - return false; + const auto *ConstructExp = dyn_cast<CXXConstructExpr>(S); + return ConstructExp && ConstructExp->isElidable(); } static bool isEffectivelyConstRegionAux(const MemRegion *MR, @@ -158,9 +151,7 @@ void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, if (!N) return; - constexpr llvm::StringLiteral Msg = - "Writing to immutable memory is undefined behavior. " - "This memory region is marked as immutable and should not be modified."; + constexpr llvm::StringLiteral Msg = "Trying to write to immutable memory."; auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); R->addRange(S->getSourceRange()); @@ -168,7 +159,7 @@ void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, // If the location that is being written to has a declaration, place a note. if (const DeclRegion *DR = dyn_cast<DeclRegion>(MR)) { R->addNote( - "Memory region is in immutable space", + "Memory region is declared as immutable here", PathDiagnosticLocation::create(DR->getDecl(), C.getSourceManager())); } diff --git a/clang/test/Analysis/store-to-immutable-basic.c b/clang/test/Analysis/store-to-immutable-basic.c index f098f7fefa28c..995c047f0a093 100644 --- a/clang/test/Analysis/store-to-immutable-basic.c +++ b/clang/test/Analysis/store-to-immutable-basic.c @@ -2,79 +2,79 @@ // Test basic functionality of StoreToImmutable checker for the C programming language. -const int tentative_global_const; // expected-note {{Memory region is in immutable space}} +const int tentative_global_const; // expected-note {{Memory region is declared as immutable here}} void test_direct_write_to_tentative_const_global() { - *(int*)&tentative_global_const = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + *(int*)&tentative_global_const = 100; // expected-warning {{Trying to write to immutable memory}} } -const int global_const = 42; // expected-note {{Memory region is in immutable space}} +const int global_const = 42; // expected-note {{Memory region is declared as immutable here}} void test_direct_write_to_const_global() { // This should trigger a warning about writing to immutable memory - *(int*)&global_const = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + *(int*)&global_const = 100; // expected-warning {{Trying to write to immutable memory}} } void test_write_through_const_pointer() { - const int local_const = 10; // expected-note {{Memory region is in immutable space}} + const int local_const = 10; // expected-note {{Memory region is declared as immutable here}} int *ptr = (int*)&local_const; - *ptr = 20; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + *ptr = 20; // expected-warning {{Trying to write to immutable memory}} } void test_write_to_const_array() { const int arr[5] = {1, 2, 3, 4, 5}; int *ptr = (int*)arr; - ptr[0] = 10; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + ptr[0] = 10; // expected-warning {{Trying to write to immutable memory}} } struct TestStruct { - const int x; // expected-note 2 {{Memory region is in immutable space}} + const int x; // expected-note 2 {{Memory region is declared as immutable here}} int y; }; void test_write_to_const_struct_member() { struct TestStruct s = {1, 2}; int *ptr = (int*)&s.x; - *ptr = 10; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + *ptr = 10; // expected-warning {{Trying to write to immutable memory}} } const int global_array[3] = {1, 2, 3}; void test_write_to_const_global_array() { int *ptr = (int*)global_array; - ptr[0] = 10; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + ptr[0] = 10; // expected-warning {{Trying to write to immutable memory}} } const struct TestStruct global_struct = {1, 2}; void test_write_to_const_global_struct() { int *ptr = (int*)&global_struct.x; - *ptr = 10; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + *ptr = 10; // expected-warning {{Trying to write to immutable memory}} } -void test_write_to_const_param(const int param) { // expected-note {{Memory region is in immutable space}} - *(int*)¶m = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} +void test_write_to_const_param(const int param) { // expected-note {{Memory region is declared as immutable here}} + *(int*)¶m = 100; // expected-warning {{Trying to write to immutable memory}} } void test_write_to_const_ptr_param(const int *param) { - *(int*)param = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + *(int*)param = 100; // expected-warning {{Trying to write to immutable memory}} } void test_write_to_const_array_param(const int arr[5]) { - *(int*)arr = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + *(int*)arr = 100; // expected-warning {{Trying to write to immutable memory}} } struct ParamStruct { - const int z; // expected-note 2 {{Memory region is in immutable space}} + const int z; // expected-note 2 {{Memory region is declared as immutable here}} int w; }; void test_write_to_const_struct_param(const struct ParamStruct s) { - *(int*)&s.z = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + *(int*)&s.z = 100; // expected-warning {{Trying to write to immutable memory}} } void test_write_to_const_struct_ptr_param(const struct ParamStruct *s) { - *(int*)&s->z = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + *(int*)&s->z = 100; // expected-warning {{Trying to write to immutable memory}} } void test_write_to_nonconst() { @@ -114,7 +114,7 @@ void test_const_ptr_to_nonconst_data() { } void test_const_ptr_to_const_data() { - const int data = 42; // expected-note {{Memory region is in immutable space}} + const int data = 42; // expected-note {{Memory region is declared as immutable here}} const int *ptr = &data; - *(int*)ptr = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} \ No newline at end of file + *(int*)ptr = 100; // expected-warning {{Trying to write to immutable memory}} +} diff --git a/clang/test/Analysis/store-to-immutable-basic.cpp b/clang/test/Analysis/store-to-immutable-basic.cpp index 232f7ac66179c..637c91caa9247 100644 --- a/clang/test/Analysis/store-to-immutable-basic.cpp +++ b/clang/test/Analysis/store-to-immutable-basic.cpp @@ -1,22 +1,22 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable -std=c++17 -verify %s void test_write_to_const_ref_param(const int ¶m) { - *(int*)¶m = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + *(int*)¶m = 100; // expected-warning {{Trying to write to immutable memory}} } // FIXME: This should warn in C mode too. void test_write_to_string_literal() { char *str = (char*)"hello"; - str[0] = 'H'; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + str[0] = 'H'; // expected-warning {{Trying to write to immutable memory}} } struct ParamStruct { - const int z; // expected-note {{Memory region is in immutable space}} + const int z; // expected-note {{Memory region is declared as immutable here}} int w; }; void test_write_to_const_struct_ref_param(const ParamStruct &s) { - *(int*)&s.z = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + *(int*)&s.z = 100; // expected-warning {{Trying to write to immutable memory}} } void test_const_ref_to_nonconst_data() { @@ -26,9 +26,9 @@ void test_const_ref_to_nonconst_data() { } void test_const_ref_to_const_data() { - const int data = 42; // expected-note {{Memory region is in immutable space}} + const int data = 42; // expected-note {{Memory region is declared as immutable here}} const int &ref = data; - *(int*)&ref = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + *(int*)&ref = 100; // expected-warning {{Trying to write to immutable memory}} } void test_ref_to_nonconst_data() { @@ -38,25 +38,25 @@ void test_ref_to_nonconst_data() { } void test_ref_to_const_data() { - const int data = 42; // expected-note {{Memory region is in immutable space}} + const int data = 42; // expected-note {{Memory region is declared as immutable here}} int &ref = *(int*)&data; - ref = 100; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + ref = 100; // expected-warning {{Trying to write to immutable memory}} } struct MultipleLayerStruct { MultipleLayerStruct(); - const int data; // expected-note {{Memory region is in immutable space}} + const int data; // expected-note {{Memory region is declared as immutable here}} const int buf[10]; }; -MultipleLayerStruct MLS[10]; // mutable global +MultipleLayerStruct MLS[10]; void test_multiple_layer_struct_array_member() { int *p = (int*)&MLS[2].data; - *p = 4; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} + *p = 4; // expected-warning {{Trying to write to immutable memory}} } void test_multiple_layer_struct_array_array_member() { int *p = (int*)&MLS[2].buf[3]; - *p = 4; // expected-warning {{Writing to immutable memory is undefined behavior. This memory region is marked as immutable and should not be modified}} -} \ No newline at end of file + *p = 4; // expected-warning {{Trying to write to immutable memory}} +} From 373679b4b121781925265ae28a41160bc059ebf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Wed, 30 Jul 2025 18:04:54 +0200 Subject: [PATCH 15/20] [review-fix] support SubRegions not just ElementRegions --- .../Checkers/StoreToImmutableChecker.cpp | 24 ++++++------------- .../Analysis/store-to-immutable-basic.cpp | 11 +++++++++ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp index 8868e290c85bb..0b91f6a1b744a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp @@ -108,24 +108,14 @@ static bool isEffectivelyConstRegionAux(const MemRegion *MR, bool StoreToImmutableChecker::isEffectivelyConstRegion( const MemRegion *MR, CheckerContext &C) const { - // If the region is an ElementRegion, we need to check if any of the super - // regions have const-qualified type. - if (const auto *ER = dyn_cast<ElementRegion>(MR)) { - SmallVector<const MemRegion *, 8> SuperRegions; - const MemRegion *Current = MR; - const MemRegion *Base = ER->getBaseRegion(); - while (Current != Base) { - SuperRegions.push_back(Current); - assert(isa<SubRegion>(Current)); - Current = cast<SubRegion>(Current)->getSuperRegion(); - } - SuperRegions.push_back(Base); - return llvm::any_of(SuperRegions, [&C](const MemRegion *MR) { - return isEffectivelyConstRegionAux(MR, C); - }); + while (true) { + if (isEffectivelyConstRegionAux(MR, C)) + return true; + if (auto *SR = dyn_cast<SubRegion>(MR)) + MR = SR->getSuperRegion(); + else + return false; } - - return isEffectivelyConstRegionAux(MR, C); } void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, diff --git a/clang/test/Analysis/store-to-immutable-basic.cpp b/clang/test/Analysis/store-to-immutable-basic.cpp index 637c91caa9247..3c5ae937aee57 100644 --- a/clang/test/Analysis/store-to-immutable-basic.cpp +++ b/clang/test/Analysis/store-to-immutable-basic.cpp @@ -60,3 +60,14 @@ void test_multiple_layer_struct_array_array_member() { int *p = (int*)&MLS[2].buf[3]; *p = 4; // expected-warning {{Trying to write to immutable memory}} } + +struct StructWithNonConstMember { + int x; // expected-note {{Memory region is declared as immutable here}} + // FIXME: this note should really appear on the line where the const struct is declared. +}; + +const StructWithNonConstMember SWNCM{0}; + +void test_write_to_non_const_member_of_const_struct() { + *(int*)&SWNCM.x = 100; // expected-warning {{Trying to write to immutable memory}} +} From 7cbabf8193b20a2ecc9075da9fb27a0896500da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Wed, 30 Jul 2025 18:07:48 +0200 Subject: [PATCH 16/20] [review-fix] fix typo --- clang/test/Analysis/store-to-immutable-lambda-init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Analysis/store-to-immutable-lambda-init.cpp b/clang/test/Analysis/store-to-immutable-lambda-init.cpp index bb93dd8784e38..764ce3fdbeb50 100644 --- a/clang/test/Analysis/store-to-immutable-lambda-init.cpp +++ b/clang/test/Analysis/store-to-immutable-lambda-init.cpp @@ -6,7 +6,7 @@ // expected-no-diagnostics // In C++14 and before, when initializing a lambda, the statement given in the checkBind callback is not the whole DeclExpr, but the CXXConstructExpr of the lambda object. -// FIXME: Once the API of checkBind provides more information about the statement, the checker should be simplified, and this this test case will no longer be a cornercase in the checker. +// FIXME: Once the API of checkBind provides more information about the statement, the checker should be simplified, and this test case will no longer be a cornercase in the checker. void test_const_lambda_initialization_pre_cpp17() { const auto lambda = [](){}; // No warning expected From e377b196ec830f262bacf72ad7484d9cb4a6b875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Thu, 31 Jul 2025 01:20:45 +0200 Subject: [PATCH 17/20] [review-fix] delete stray whitespace --- clang/docs/analyzer/checkers/storetoimmutable_example.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/docs/analyzer/checkers/storetoimmutable_example.cpp b/clang/docs/analyzer/checkers/storetoimmutable_example.cpp index 5bd5cc93eee20..4fbec94bb0283 100644 --- a/clang/docs/analyzer/checkers/storetoimmutable_example.cpp +++ b/clang/docs/analyzer/checkers/storetoimmutable_example.cpp @@ -9,7 +9,7 @@ struct TestStruct { void immutable_violation_examples() { *(int *)&global_const = 100; // warn: Trying to write to immutable memory - + const int local_const = 42; *(int *)&local_const = 43; // warn: Trying to write to immutable memory From cfedf88f50f4589ea2b47ec4c05b2512ac868d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Fri, 1 Aug 2025 11:08:54 +0200 Subject: [PATCH 18/20] [review-fix] more elaborate notes Now we mention in the bug message whether the target is a global immutable memory, and thus the write is "fundamentally" bad, or it is just touching a const qualified type (still bad, but for another reason). Also added notes where the MemRegion being written to does not have a declaration. We now try to get the immediate cause of why the memory should not be written, and we put a note there. As a minor improvement, moved non-static member functions to free functions wherever possible, we are not using anything from the class. --- .../Checkers/StoreToImmutableChecker.cpp | 74 ++++++++++++------- .../test/Analysis/store-to-immutable-basic.c | 13 ++-- .../Analysis/store-to-immutable-basic.cpp | 9 +-- 3 files changed, 57 insertions(+), 39 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp index 0b91f6a1b744a..57ec49f28e799 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp @@ -27,15 +27,10 @@ class StoreToImmutableChecker : public Checker<check::Bind> { public: void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; - -private: - bool isInitializationContext(const Stmt *S, CheckerContext &C) const; - bool isEffectivelyConstRegion(const MemRegion *MR, CheckerContext &C) const; }; } // end anonymous namespace -bool StoreToImmutableChecker::isInitializationContext(const Stmt *S, - CheckerContext &C) const { +static bool isInitializationContext(const Stmt *S, CheckerContext &C) { // Check if this is a DeclStmt (variable declaration) if (isa<DeclStmt>(S)) return true; @@ -76,11 +71,8 @@ bool StoreToImmutableChecker::isInitializationContext(const Stmt *S, return ConstructExp && ConstructExp->isElidable(); } -static bool isEffectivelyConstRegionAux(const MemRegion *MR, - CheckerContext &C) { - // Check if the region is in the global immutable space - const MemSpaceRegion *MS = MR->getMemorySpace(C.getState()); - if (isa<GlobalImmutableSpaceRegion>(MS)) +static bool isEffectivelyConstRegion(const MemRegion *MR, CheckerContext &C) { + if (isa<GlobalImmutableSpaceRegion>(MR)) return true; // Check if this is a TypedRegion with a const-qualified type @@ -99,22 +91,37 @@ static bool isEffectivelyConstRegionAux(const MemRegion *MR, return true; } - // NOTE: The only kind of region that is not checked by the above branches is - // AllocaRegion. We do not need to check AllocaRegion, as it models untyped - // memory, that is allocated on the stack. + // NOTE: The above branches do not cover AllocaRegion. We do not need to check + // AllocaRegion, as it models untyped memory, that is allocated on the stack. return false; } -bool StoreToImmutableChecker::isEffectivelyConstRegion( - const MemRegion *MR, CheckerContext &C) const { +static const MemRegion *getInnermostConstRegion(const MemRegion *MR, + CheckerContext &C) { while (true) { - if (isEffectivelyConstRegionAux(MR, C)) - return true; + if (isEffectivelyConstRegion(MR, C)) + return MR; if (auto *SR = dyn_cast<SubRegion>(MR)) MR = SR->getSuperRegion(); else - return false; + return nullptr; + } +} + +static const DeclRegion * +getInnermostEnclosingConstDeclRegion(const MemRegion *MR, CheckerContext &C) { + while (true) { + if (const auto *DR = dyn_cast<DeclRegion>(MR)) { + const ValueDecl *D = DR->getDecl(); + QualType DeclaredType = D->getType(); + if (DeclaredType.isConstQualified()) + return DR; + } + if (auto *SR = dyn_cast<SubRegion>(MR)) + MR = SR->getSuperRegion(); + else + return nullptr; } } @@ -132,25 +139,36 @@ void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, if (isInitializationContext(S, C)) return; + // Check if the region is in the global immutable space + const MemSpaceRegion *MS = MR->getMemorySpace(C.getState()); + const bool IsGlobalImmutableSpace = isa<GlobalImmutableSpaceRegion>(MS); // Check if the region corresponds to a const variable - if (!isEffectivelyConstRegion(MR, C)) + const MemRegion *InnermostConstRegion = getInnermostConstRegion(MR, C); + if (!IsGlobalImmutableSpace && !InnermostConstRegion) return; + SmallString<64> WarningMessage{"Trying to write to immutable memory"}; + if (IsGlobalImmutableSpace) + WarningMessage += " in global read-only storage"; + // Generate the bug report ExplodedNode *N = C.generateNonFatalErrorNode(); if (!N) return; - constexpr llvm::StringLiteral Msg = "Trying to write to immutable memory."; - - auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); + auto R = std::make_unique<PathSensitiveBugReport>(BT, WarningMessage, N); R->addRange(S->getSourceRange()); - // If the location that is being written to has a declaration, place a note. - if (const DeclRegion *DR = dyn_cast<DeclRegion>(MR)) { - R->addNote( - "Memory region is declared as immutable here", - PathDiagnosticLocation::create(DR->getDecl(), C.getSourceManager())); + // Generate a note if the location that is being written to has a + // declaration or if it is a subregion of a const region with a declaration. + const DeclRegion *DR = + getInnermostEnclosingConstDeclRegion(InnermostConstRegion, C); + if (DR) { + const char *NoteMessage = + (DR != MR) ? "Enclosing memory region is declared as immutable here" + : "Memory region is declared as immutable here"; + R->addNote(NoteMessage, PathDiagnosticLocation::create( + DR->getDecl(), C.getSourceManager())); } // For this checker, we are only interested in the value being written, no diff --git a/clang/test/Analysis/store-to-immutable-basic.c b/clang/test/Analysis/store-to-immutable-basic.c index 995c047f0a093..f7d887819714f 100644 --- a/clang/test/Analysis/store-to-immutable-basic.c +++ b/clang/test/Analysis/store-to-immutable-basic.c @@ -5,14 +5,14 @@ const int tentative_global_const; // expected-note {{Memory region is declared as immutable here}} void test_direct_write_to_tentative_const_global() { - *(int*)&tentative_global_const = 100; // expected-warning {{Trying to write to immutable memory}} + *(int*)&tentative_global_const = 100; // expected-warning {{Trying to write to immutable memory in global read-only storage}} } const int global_const = 42; // expected-note {{Memory region is declared as immutable here}} void test_direct_write_to_const_global() { // This should trigger a warning about writing to immutable memory - *(int*)&global_const = 100; // expected-warning {{Trying to write to immutable memory}} + *(int*)&global_const = 100; // expected-warning {{Trying to write to immutable memory in global read-only storage}} } void test_write_through_const_pointer() { @@ -22,7 +22,7 @@ void test_write_through_const_pointer() { } void test_write_to_const_array() { - const int arr[5] = {1, 2, 3, 4, 5}; + const int arr[5] = {1, 2, 3, 4, 5}; // expected-note {{Enclosing memory region is declared as immutable here}} int *ptr = (int*)arr; ptr[0] = 10; // expected-warning {{Trying to write to immutable memory}} } @@ -38,20 +38,21 @@ void test_write_to_const_struct_member() { *ptr = 10; // expected-warning {{Trying to write to immutable memory}} } -const int global_array[3] = {1, 2, 3}; +const int global_array[3] = {1, 2, 3}; // expected-note {{Enclosing memory region is declared as immutable here}} void test_write_to_const_global_array() { int *ptr = (int*)global_array; - ptr[0] = 10; // expected-warning {{Trying to write to immutable memory}} + ptr[0] = 10; // expected-warning {{Trying to write to immutable memory in global read-only storage}} } const struct TestStruct global_struct = {1, 2}; void test_write_to_const_global_struct() { int *ptr = (int*)&global_struct.x; - *ptr = 10; // expected-warning {{Trying to write to immutable memory}} + *ptr = 10; // expected-warning {{Trying to write to immutable memory in global read-only storage}} } + void test_write_to_const_param(const int param) { // expected-note {{Memory region is declared as immutable here}} *(int*)¶m = 100; // expected-warning {{Trying to write to immutable memory}} } diff --git a/clang/test/Analysis/store-to-immutable-basic.cpp b/clang/test/Analysis/store-to-immutable-basic.cpp index 3c5ae937aee57..63319d9dfb7b1 100644 --- a/clang/test/Analysis/store-to-immutable-basic.cpp +++ b/clang/test/Analysis/store-to-immutable-basic.cpp @@ -46,7 +46,7 @@ void test_ref_to_const_data() { struct MultipleLayerStruct { MultipleLayerStruct(); const int data; // expected-note {{Memory region is declared as immutable here}} - const int buf[10]; + const int buf[10]; // expected-note {{Enclosing memory region is declared as immutable here}} }; MultipleLayerStruct MLS[10]; @@ -62,12 +62,11 @@ void test_multiple_layer_struct_array_array_member() { } struct StructWithNonConstMember { - int x; // expected-note {{Memory region is declared as immutable here}} - // FIXME: this note should really appear on the line where the const struct is declared. + int x; }; -const StructWithNonConstMember SWNCM{0}; +const StructWithNonConstMember SWNCM{0}; // expected-note {{Enclosing memory region is declared as immutable here}} void test_write_to_non_const_member_of_const_struct() { - *(int*)&SWNCM.x = 100; // expected-warning {{Trying to write to immutable memory}} + *(int*)&SWNCM.x = 100; // expected-warning {{Trying to write to immutable memory in global read-only storage}} } From fa0a379739535f79066951f5c43314597e115c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Fri, 1 Aug 2025 11:11:52 +0200 Subject: [PATCH 19/20] [review-fix] remove redundant comments from example --- clang/docs/analyzer/checkers/storetoimmutable_example.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/clang/docs/analyzer/checkers/storetoimmutable_example.cpp b/clang/docs/analyzer/checkers/storetoimmutable_example.cpp index 4fbec94bb0283..2b1ead5c501fa 100644 --- a/clang/docs/analyzer/checkers/storetoimmutable_example.cpp +++ b/clang/docs/analyzer/checkers/storetoimmutable_example.cpp @@ -1,7 +1,5 @@ -// Global const variable const int global_const = 42; -// Const struct member struct TestStruct { const int x; int y; From 4e6c98805f5f6080c5b0ed22568ad13a37c9101a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fu...@sigmatechnology.com> Date: Fri, 1 Aug 2025 11:15:40 +0200 Subject: [PATCH 20/20] [review-fix] document our options for the fixme --- .../Checkers/StoreToImmutableChecker.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp index 57ec49f28e799..afad41939cdca 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp @@ -63,10 +63,12 @@ static bool isInitializationContext(const Stmt *S, CheckerContext &C) { // ugly hack to detect whether this construction is used to initialize a // variable. // - // FIXME: This should be eliminated once the API of checkBind would allow to - // distinguish between initialization and assignment, because this information - // is already available in the engine, it is just not passed to the checker - // API. + // FIXME: This should be eliminated by improving the API of checkBind to + // ensure that it consistently passes the `VarDecl` (instead of the + // `CXXConstructExpr`) when the constructor call denotes the initialization + // of a variable with a lambda, or maybe less preferably, try the more + // invasive approach of passing the information forward to the checkers + // whether the current bind is an initialization or an assignment. const auto *ConstructExp = dyn_cast<CXXConstructExpr>(S); return ConstructExp && ConstructExp->isElidable(); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits