Skip to content

Commit 0424546

Browse files
authored
[analyzer] Use AllocaRegion in MallocChecker (#72402)
...to model the results of alloca() and _alloca() calls. Previously it acted as if these functions were returning memory from the heap, which led to alpha.security.ArrayBoundV2 producing incorrect messages.
1 parent a3ae7b6 commit 0424546

File tree

7 files changed

+56
-30
lines changed

7 files changed

+56
-30
lines changed

clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,15 @@ class SValBuilder {
215215
const LocationContext *LCtx,
216216
QualType type, unsigned Count);
217217

218+
/// Create an SVal representing the result of an alloca()-like call, that is,
219+
/// an AllocaRegion on the stack.
220+
///
221+
/// After calling this function, it's a good idea to set the extent of the
222+
/// returned AllocaRegion.
223+
loc::MemRegionVal getAllocaRegionVal(const Expr *E,
224+
const LocationContext *LCtx,
225+
unsigned Count);
226+
218227
DefinedOrUnknownSVal getDerivedRegionValueSymbolVal(
219228
SymbolRef parentSymbol, const TypedValueRegion *region);
220229

clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -81,22 +81,20 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
8181

8282
case Builtin::BI__builtin_alloca_with_align:
8383
case Builtin::BI__builtin_alloca: {
84-
// FIXME: Refactor into StoreManager itself?
85-
MemRegionManager& RM = C.getStoreManager().getRegionManager();
86-
const AllocaRegion* R =
87-
RM.getAllocaRegion(CE, C.blockCount(), C.getLocationContext());
88-
89-
// Set the extent of the region in bytes. This enables us to use the
90-
// SVal of the argument directly. If we save the extent in bits, we
91-
// cannot represent values like symbol*8.
92-
auto Size = Call.getArgSVal(0);
93-
if (Size.isUndef())
94-
return true; // Return true to model purity.
95-
96-
state = setDynamicExtent(state, R, Size.castAs<DefinedOrUnknownSVal>(),
97-
C.getSValBuilder());
84+
SValBuilder &SVB = C.getSValBuilder();
85+
const loc::MemRegionVal R =
86+
SVB.getAllocaRegionVal(CE, C.getLocationContext(), C.blockCount());
9887

99-
C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R)));
88+
// Set the extent of the region in bytes. This enables us to use the SVal
89+
// of the argument directly. If we saved the extent in bits, it'd be more
90+
// difficult to reason about values like symbol*8.
91+
auto Size = Call.getArgSVal(0);
92+
if (auto DefSize = Size.getAs<DefinedOrUnknownSVal>()) {
93+
// This `getAs()` is mostly paranoia, because core.CallAndMessage reports
94+
// undefined function arguments (unless it's disabled somehow).
95+
state = setDynamicExtent(state, R.getRegion(), *DefSize, SVB);
96+
}
97+
C.addTransition(state->BindExpr(CE, LCtx, R));
10098
return true;
10199
}
102100

clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,13 +1728,15 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
17281728
return nullptr;
17291729

17301730
// Bind the return value to the symbolic value from the heap region.
1731-
// TODO: We could rewrite post visit to eval call; 'malloc' does not have
1732-
// side effects other than what we model here.
1731+
// TODO: move use of this functions to an EvalCall callback, becasue
1732+
// BindExpr() should'nt be used elsewhere.
17331733
unsigned Count = C.blockCount();
1734-
SValBuilder &svalBuilder = C.getSValBuilder();
1734+
SValBuilder &SVB = C.getSValBuilder();
17351735
const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
1736-
DefinedSVal RetVal = svalBuilder.getConjuredHeapSymbolVal(CE, LCtx, Count)
1737-
.castAs<DefinedSVal>();
1736+
DefinedSVal RetVal =
1737+
((Family == AF_Alloca) ? SVB.getAllocaRegionVal(CE, LCtx, Count)
1738+
: SVB.getConjuredHeapSymbolVal(CE, LCtx, Count)
1739+
.castAs<DefinedSVal>());
17381740
State = State->BindExpr(CE, C.getLocationContext(), RetVal);
17391741

17401742
// Fill the region with the initialization value.
@@ -1746,7 +1748,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
17461748

17471749
// Set the region's extent.
17481750
State = setDynamicExtent(State, RetVal.getAsRegion(),
1749-
Size.castAs<DefinedOrUnknownSVal>(), svalBuilder);
1751+
Size.castAs<DefinedOrUnknownSVal>(), SVB);
17501752

17511753
return MallocUpdateRefState(C, CE, State, Family);
17521754
}

clang/lib/StaticAnalyzer/Core/SValBuilder.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,14 @@ SValBuilder::getConjuredHeapSymbolVal(const Expr *E,
231231
return loc::MemRegionVal(MemMgr.getSymbolicHeapRegion(sym));
232232
}
233233

234+
loc::MemRegionVal SValBuilder::getAllocaRegionVal(const Expr *E,
235+
const LocationContext *LCtx,
236+
unsigned VisitCount) {
237+
const AllocaRegion *R =
238+
getRegionManager().getAllocaRegion(E, VisitCount, LCtx);
239+
return loc::MemRegionVal(R);
240+
}
241+
234242
DefinedSVal SValBuilder::getMetadataSymbolVal(const void *symbolTag,
235243
const MemRegion *region,
236244
const Expr *expr, QualType type,

clang/test/Analysis/malloc.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,13 +266,21 @@ void CheckUseZeroAllocated1(void) {
266266
}
267267

268268
char CheckUseZeroAllocated2(void) {
269+
// NOTE: The `AllocaRegion` that models the return value of `alloca()`
270+
// doesn't have an associated symbol, so the current implementation of
271+
// `MallocChecker::checkUseZeroAllocated()` cannot provide warnings for it.
272+
// However, other checkers like core.uninitialized.UndefReturn (that
273+
// activates in these TCs) or the array bound checkers provide more generic,
274+
// but still sufficient warnings in these cases, so I think it isn't
275+
// important to cover this in MallocChecker.
269276
char *p = alloca(0);
270-
return *p; // expected-warning {{Use of memory allocated with size zero}}
277+
return *p; // expected-warning {{Undefined or garbage value returned to caller}}
271278
}
272279

273280
char CheckUseZeroWinAllocated2(void) {
281+
// Note: Same situation as `CheckUseZeroAllocated2()`.
274282
char *p = _alloca(0);
275-
return *p; // expected-warning {{Use of memory allocated with size zero}}
283+
return *p; // expected-warning {{Undefined or garbage value returned to caller}}
276284
}
277285

278286
void UseZeroAllocated(int *p) {
@@ -727,6 +735,11 @@ void paramFree(int *p) {
727735
myfoo(p); // expected-warning {{Use of memory after it is freed}}
728736
}
729737

738+
void allocaFree(void) {
739+
int *p = alloca(sizeof(int));
740+
free(p); // expected-warning {{Memory allocated by alloca() should not be deallocated}}
741+
}
742+
730743
int* mallocEscapeRet(void) {
731744
int *p = malloc(12);
732745
return p; // no warning

clang/test/Analysis/memory-model.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ void symbolic_malloc() {
9797

9898
void symbolic_alloca() {
9999
int *a = (int *)alloca(12);
100-
clang_analyzer_dump(a); // expected-warning {{Element{HeapSymRegion{conj}}
100+
clang_analyzer_dump(a); // expected-warning {{Element{alloca{}}
101101
clang_analyzer_dumpExtent(a); // expected-warning {{12 S64b}}
102102
clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}}
103103
}

clang/test/Analysis/out-of-bounds-diagnostics.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,8 @@ void *alloca(size_t size);
107107
int allocaRegion(void) {
108108
int *mem = (int*)alloca(2*sizeof(int));
109109
mem[3] = -2;
110-
// expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
111-
// expected-note@-2 {{Access of the heap area at index 3, while it holds only 2 'int' elements}}
112-
// FIXME: this should be
113-
// {{Out of bound access to memory after the end of the memory returned by 'alloca'}}
114-
// {{Access of the memory returned by 'alloca' at index 3, while it holds only 2 'int' elements}}
115-
// but apparently something models 'alloca' as if it was allocating on the heap
110+
// expected-warning@-1 {{Out of bound access to memory after the end of the memory returned by 'alloca'}}
111+
// expected-note@-2 {{Access of the memory returned by 'alloca' at index 3, while it holds only 2 'int' elements}}
116112
return *mem;
117113
}
118114

0 commit comments

Comments
 (0)