Skip to content

[analyzer] [MallocChecker] Fix Store modification in checkPreCall #109337

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 30 additions & 25 deletions clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,8 @@ class MallocChecker
[[nodiscard]] ProgramStateRef
FreeMemAux(CheckerContext &C, const CallEvent &Call, ProgramStateRef State,
unsigned Num, bool Hold, bool &IsKnownToBeAllocated,
AllocationFamily Family, bool ReturnsNullOnFailure = false) const;
AllocationFamily Family, bool Invalidate = true,
bool ReturnsNullOnFailure = false) const;

/// Models memory deallocation.
///
Expand All @@ -694,7 +695,8 @@ class MallocChecker
[[nodiscard]] ProgramStateRef
FreeMemAux(CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call,
ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated,
AllocationFamily Family, bool ReturnsNullOnFailure = false,
AllocationFamily Family, bool Invalidate = true,
bool ReturnsNullOnFailure = false,
std::optional<SVal> ArgValOpt = {}) const;

// TODO: Needs some refactoring, as all other deallocation modeling
Expand Down Expand Up @@ -1474,9 +1476,10 @@ void MallocChecker::preGetdelim(ProgramStateRef State, const CallEvent &Call,
// We do not need this value here, as FreeMemAux will take care
// of reporting any violation of the preconditions.
bool IsKnownToBeAllocated = false;
State = FreeMemAux(C, Call.getArgExpr(0), Call, State, false,
IsKnownToBeAllocated, AllocationFamily(AF_Malloc), false,
LinePtr);
State =
FreeMemAux(C, Call.getArgExpr(0), Call, State, false,
IsKnownToBeAllocated, AllocationFamily(AF_Malloc),
/*Invalidate=*/false, /*ReturnsNullOnFailure=*/false, LinePtr);
if (State)
C.addTransition(State);
}
Expand Down Expand Up @@ -1793,6 +1796,7 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0), Call, C.getState(),
/*Hold=*/true, IsKnownToBeAllocatedMemory,
AllocationFamily(AF_Malloc),
/*Invalidate=*/false,
/*ReturnsNullOnFailure=*/true);

C.addTransition(State);
Expand Down Expand Up @@ -1986,20 +1990,20 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
return State;
}

ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
const CallEvent &Call,
ProgramStateRef State, unsigned Num,
bool Hold, bool &IsKnownToBeAllocated,
AllocationFamily Family,
bool ReturnsNullOnFailure) const {
ProgramStateRef
MallocChecker::FreeMemAux(CheckerContext &C, const CallEvent &Call,
ProgramStateRef State, unsigned Num, bool Hold,
bool &IsKnownToBeAllocated, AllocationFamily Family,
bool Invalidate, bool ReturnsNullOnFailure) const {
if (!State)
return nullptr;

if (Call.getNumArgs() < (Num + 1))
return nullptr;

return FreeMemAux(C, Call.getArgExpr(Num), Call, State, Hold,
IsKnownToBeAllocated, Family, ReturnsNullOnFailure);
IsKnownToBeAllocated, Family, Invalidate,
ReturnsNullOnFailure);
}

/// Checks if the previous call to free on the given symbol failed - if free
Expand Down Expand Up @@ -2134,12 +2138,11 @@ static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) {
}
}

ProgramStateRef
MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
const CallEvent &Call, ProgramStateRef State,
bool Hold, bool &IsKnownToBeAllocated,
AllocationFamily Family, bool ReturnsNullOnFailure,
std::optional<SVal> ArgValOpt) const {
ProgramStateRef MallocChecker::FreeMemAux(
CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call,
ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated,
AllocationFamily Family, bool Invalidate, bool ReturnsNullOnFailure,
std::optional<SVal> ArgValOpt) const {

if (!State)
return nullptr;
Expand Down Expand Up @@ -2299,13 +2302,15 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
// that.
assert(!RsBase || (RsBase && RsBase->getAllocationFamily() == Family));

// Assume that after memory is freed, it contains unknown values. This
// conforts languages standards, since reading from freed memory is considered
// UB and may result in arbitrary value.
State = State->invalidateRegions({location}, Call.getOriginExpr(),
C.blockCount(), C.getLocationContext(),
/*CausesPointerEscape=*/false,
/*InvalidatedSymbols=*/nullptr);
if (Invalidate) {
// Assume that after memory is freed, it contains unknown values. This
// conforts languages standards, since reading from freed memory is
// considered UB and may result in arbitrary value.
State = State->invalidateRegions({location}, Call.getOriginExpr(),
C.blockCount(), C.getLocationContext(),
/*CausesPointerEscape=*/false,
/*InvalidatedSymbols=*/nullptr);
}

// Normal free.
if (Hold)
Expand Down
9 changes: 9 additions & 0 deletions clang/test/Analysis/NewDelete-intersections.mm
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "Inputs/system-header-simulator-cxx.h"
#include "Inputs/system-header-simulator-objc.h"
#include "Inputs/system-header-simulator.h"

typedef __typeof__(sizeof(int)) size_t;
extern "C" void *malloc(size_t);
Expand Down Expand Up @@ -86,3 +87,11 @@ void testStandardPlacementNewAfterDelete() {
delete p;
p = new (p) int; // newdelete-warning{{Use of memory after it is freed}}
}

void testGetLine(FILE *f) {
char *buffer = (char *)malloc(10);
char *old = buffer;
size_t n = 0;
getline(&buffer, &n, f);
int a = *old; // no-warn
}
Loading