[clang] [clang][analyzer] Add StoreToImmutable checker (PR #150417)
Endre Fülöp via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 2 03:10:08 PDT 2025
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.fulop at sigmatechnology.com>
Date: Thu, 24 Jul 2025 14:49:14 +0200
Subject: [PATCH 01/21] [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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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.fulop at sigmatechnology.com>
Date: Fri, 25 Jul 2025 03:21:19 +0200
Subject: [PATCH 02/21] 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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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.fulop at sigmatechnology.com>
Date: Mon, 28 Jul 2025 13:56:50 +0200
Subject: [PATCH 03/21] [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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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.fulop at sigmatechnology.com>
Date: Mon, 28 Jul 2025 15:17:42 +0200
Subject: [PATCH 04/21] [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.fulop at sigmatechnology.com>
Date: Mon, 28 Jul 2025 15:47:28 +0200
Subject: [PATCH 05/21] [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.fulop at sigmatechnology.com>
Date: Mon, 28 Jul 2025 17:20:32 +0200
Subject: [PATCH 06/21] [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.fulop at sigmatechnology.com>
Date: Mon, 28 Jul 2025 18:02:00 +0200
Subject: [PATCH 07/21] [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.fulop at sigmatechnology.com>
Date: Tue, 29 Jul 2025 16:01:25 +0200
Subject: [PATCH 08/21] [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.fulop at sigmatechnology.com>
Date: Tue, 29 Jul 2025 21:00:37 +0200
Subject: [PATCH 09/21] [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.fulop at sigmatechnology.com>
Date: Tue, 29 Jul 2025 21:01:26 +0200
Subject: [PATCH 10/21] [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.fulop at sigmatechnology.com>
Date: Tue, 29 Jul 2025 21:02:58 +0200
Subject: [PATCH 11/21] [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.fulop at sigmatechnology.com>
Date: Wed, 30 Jul 2025 16:25:15 +0200
Subject: [PATCH 12/21] [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.fulop at sigmatechnology.com>
Date: Wed, 30 Jul 2025 17:31:18 +0200
Subject: [PATCH 13/21] [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.fulop at sigmatechnology.com>
Date: Wed, 30 Jul 2025 17:57:58 +0200
Subject: [PATCH 14/21] [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.fulop at sigmatechnology.com>
Date: Wed, 30 Jul 2025 18:04:54 +0200
Subject: [PATCH 15/21] [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.fulop at sigmatechnology.com>
Date: Wed, 30 Jul 2025 18:07:48 +0200
Subject: [PATCH 16/21] [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.fulop at sigmatechnology.com>
Date: Thu, 31 Jul 2025 01:20:45 +0200
Subject: [PATCH 17/21] [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.fulop at sigmatechnology.com>
Date: Fri, 1 Aug 2025 11:08:54 +0200
Subject: [PATCH 18/21] [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.fulop at sigmatechnology.com>
Date: Fri, 1 Aug 2025 11:11:52 +0200
Subject: [PATCH 19/21] [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.fulop at sigmatechnology.com>
Date: Fri, 1 Aug 2025 11:15:40 +0200
Subject: [PATCH 20/21] [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();
}
>From 89389a5773dc43c8b520c341c0e09503b52c2cb6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Sat, 2 Aug 2025 12:09:44 +0200
Subject: [PATCH 21/21] [review-fix] clarify wording
---
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 2b1ead5c501fa..0c979688f2d9b 100644
--- a/clang/docs/analyzer/checkers/storetoimmutable_example.cpp
+++ b/clang/docs/analyzer/checkers/storetoimmutable_example.cpp
@@ -11,7 +11,7 @@ void immutable_violation_examples() {
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
+ // NOTE: The following is reported 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
More information about the cfe-commits
mailing list