-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[clang][analyzer] Add StoreToImmutable checker #150417
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
0630d81
fa3f84f
b022182
5190ee0
856a865
d8f3456
01d0521
2aacf92
6e8a332
d65aa88
7e94b10
4db2804
7e73177
cac94fe
373679b
7cbabf8
e377b19
cfedf88
fa0a379
4e6c988
17a0e9c
89389a5
4d883f1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.StoreToImmutable %s | ||
|
||
// Global const variable | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer removing this comment and "// Const struct member" because they just state the obvious. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed redundant comments from the example. |
||
const int global_const = 42; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't emphasise "global" in variable names, as globals and local variables are handled the same way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not true. Globals live in the global memory space where we have a suitable sub-space for immutable globals, like constants. In contrast to this, in the stack space (where locals live) has no sub-space for constants. So there is an important semantic difference even for your checker because you also check the mspaces. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @steakhal You are right that globals and locals are handled differently by the checker implementation, what we meant under "handled the same way" is the user experience. This is an example file that will be included in the documentation so we shouldn't highlight the global/local nature of variables if the user will see the same behavior (modifying Alternatively including a global and a local variable to show that both produce the same behavior is also OK (I feel that it's a bit too verbose, but I'm not opposed to it). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. I misinterpreted the context. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have made the simplification, and it still has an example for a global and a local variable, demonstrating that misuses of both are detected. |
||
|
||
void test_global_const() { | ||
NagyDonat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*(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 | ||
gamesh411 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
//=== StoreToImmutableChecker.cpp - Store to immutable memory checker -*- C++ | ||
//-*-===// | ||
steakhal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// | ||
// 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 { | ||
steakhal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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; | ||
} | ||
steakhal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// 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)) { | ||
steakhal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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); | ||
} | ||
steakhal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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; | ||
} | ||
steakhal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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)) | ||
steakhal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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; | ||
steakhal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// 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; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.