Skip to content
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

Fix #4626 (new check: potential NULL pointer dereference, when memory allocation fails) #7068

Merged
merged 6 commits into from
Dec 8, 2024
Merged
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
34 changes: 32 additions & 2 deletions lib/checknullpointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,8 @@ void CheckNullPointer::nullPointerError(const Token *tok, const std::string &var
reportError(tok, Severity::error, "nullPointer", "Null pointer dereference", CWE_NULL_POINTER_DEREFERENCE, Certainty::normal);
reportError(tok, Severity::warning, "nullPointerDefaultArg", errmsgdefarg, CWE_NULL_POINTER_DEREFERENCE, Certainty::normal);
reportError(tok, Severity::warning, "nullPointerRedundantCheck", errmsgcond, CWE_NULL_POINTER_DEREFERENCE, Certainty::normal);
reportError(tok, Severity::warning, "nullPointerOutOfMemory", "Null pointer dereference", CWE_NULL_POINTER_DEREFERENCE, Certainty::normal);
reportError(tok, Severity::warning, "nullPointerOutOfResources", "Null pointer dereference", CWE_NULL_POINTER_DEREFERENCE, Certainty::normal);
return;
}

Expand All @@ -461,12 +463,29 @@ void CheckNullPointer::nullPointerError(const Token *tok, const std::string &var
reportError(errorPath, Severity::warning, "nullPointerDefaultArg", errmsgdefarg, CWE_NULL_POINTER_DEREFERENCE, inconclusive || value->isInconclusive() ? Certainty::inconclusive : Certainty::normal);
} else {
std::string errmsg = std::string(value->isKnown() ? "Null" : "Possible null") + " pointer dereference";

std::string id = "nullPointer";
if (value->unknownFunctionReturn == ValueFlow::Value::UnknownFunctionReturn::outOfMemory) {
if (errmsg.compare(0, 9, "Possible ") == 0)
errmsg = "If memory allocation fail: " + errmsg.substr(9);
else
errmsg = "If memory allocation fail: " + errmsg;
id += "OutOfMemory";
}
else if (value->unknownFunctionReturn == ValueFlow::Value::UnknownFunctionReturn::outOfResources) {
if (errmsg.compare(0, 9, "Possible ") == 0)
errmsg = "If resource allocation fail: " + errmsg.substr(9);
else
errmsg = "If resource allocation fail: " + errmsg;
id += "OutOfResources";
}

if (!varname.empty())
errmsg = "$symbol:" + varname + '\n' + errmsg + ": $symbol";

reportError(errorPath,
value->isKnown() ? Severity::error : Severity::warning,
"nullPointer",
id.c_str(),
errmsg,
CWE_NULL_POINTER_DEREFERENCE, inconclusive || value->isInconclusive() ? Certainty::inconclusive : Certainty::normal);
}
Expand Down Expand Up @@ -529,10 +548,21 @@ void CheckNullPointer::pointerArithmeticError(const Token* tok, const ValueFlow:
} else {
errmsg = "Pointer " + arithmetic + " with NULL pointer.";
}

std::string id = "nullPointerArithmetic";
if (value && value->unknownFunctionReturn == ValueFlow::Value::UnknownFunctionReturn::outOfMemory) {
errmsg = "If memory allocation fail: " + ((char)std::tolower(errmsg[0]) + errmsg.substr(1));
id += "OutOfMemory";
}
else if (value && value->unknownFunctionReturn == ValueFlow::Value::UnknownFunctionReturn::outOfResources) {
errmsg = "If resource allocation fail: " + ((char)std::tolower(errmsg[0]) + errmsg.substr(1));
id += "OutOfResources";
}

const ErrorPath errorPath = getErrorPath(tok, value, "Null pointer " + arithmetic);
reportError(errorPath,
Severity::error,
"nullPointerArithmetic",
id.c_str(),
errmsg,
CWE_INCORRECT_CALCULATION,
inconclusive ? Certainty::inconclusive : Certainty::normal);
Expand Down
19 changes: 17 additions & 2 deletions lib/valueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6945,11 +6945,26 @@ static void valueFlowSafeFunctions(const TokenList& tokenlist, const SymbolDatab

static void valueFlowUnknownFunctionReturn(TokenList& tokenlist, const Settings& settings)
{
if (settings.checkUnknownFunctionReturn.empty())
if (!tokenlist.front())
return;
for (Token* tok = tokenlist.front(); tok; tok = tok->next()) {
for (Token* tok = tokenlist.front()->next(); tok; tok = tok->next()) {
if (!tok->astParent() || tok->str() != "(" || !tok->previous()->isName())
continue;

if (settings.library.getAllocFuncInfo(tok->astOperand1()) && settings.library.returnValueType(tok->astOperand1()).find('*') != std::string::npos) {
// Allocation function that returns a pointer
ValueFlow::Value value(0);
value.setPossible();
value.errorPath.emplace_back(tok, "Assuming allocation function fails");
const auto* f = settings.library.getAllocFuncInfo(tok->astOperand1());
if (Library::ismemory(f->groupId))
value.unknownFunctionReturn = ValueFlow::Value::UnknownFunctionReturn::outOfMemory;
else
value.unknownFunctionReturn = ValueFlow::Value::UnknownFunctionReturn::outOfResources;
setTokenValue(tok, value, settings);
continue;
}

if (settings.checkUnknownFunctionReturn.find(tok->strAt(-1)) == settings.checkUnknownFunctionReturn.end())
continue;
std::vector<MathLib::bigint> unknownValues = settings.library.unknownReturnValues(tok->astOperand1());
Expand Down
10 changes: 9 additions & 1 deletion lib/vfvalue.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,14 @@ namespace ValueFlow
/** For calculated values - varId that calculated value depends on */
nonneg int varId{};

enum class UnknownFunctionReturn : uint8_t {
no, // not unknown function return
outOfMemory, // out of memory
outOfResources, // out of resource
other // other
};
UnknownFunctionReturn unknownFunctionReturn{UnknownFunctionReturn::no};

/** value relies on safe checking */
bool safe{};

Expand All @@ -296,7 +304,7 @@ namespace ValueFlow
/** Is this value passed as default parameter to the function? */
bool defaultArg{};

int indirect{};
int8_t indirect{};

/** kind of moved */
enum class MoveKind : std::uint8_t { NonMovedVariable, MovedVariable, ForwardedVariable } moveKind = MoveKind::NonMovedVariable;
Expand Down
8 changes: 5 additions & 3 deletions samples/memleak/bad.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#include <stdlib.h>
int main()
{
int result;
int result = 0;
char *a = malloc(10);
a[0] = 0;
result = a[0];
if (a) {
a[0] = 0;
result = a[0];
}
return result;
}
10 changes: 6 additions & 4 deletions samples/memleak/good.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#include <stdlib.h>
int main()
{
int result;
int result = 0;
char *a = malloc(10);
a[0] = 0;
result = a[0];
free(a);
if (a) {
a[0] = 0;
result = a[0];
free(a);
}
return result;
}
2 changes: 1 addition & 1 deletion samples/memleak/out.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
samples\memleak\bad.c:8:5: error: Memory leak: a [memleak]
samples\memleak\bad.c:10:5: error: Memory leak: a [memleak]
return result;
^
2 changes: 2 additions & 0 deletions test/cfg/bsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,10 @@ void uninitvar(void)
void arrayIndexOutOfBounds(void)
{
char * pAlloc = calloc(2, 3);
// cppcheck-suppress nullPointerOutOfMemory
pAlloc[5] = 'a';
// cppcheck-suppress arrayIndexOutOfBounds
// cppcheck-suppress nullPointerOutOfMemory
pAlloc[6] = 1;
// cppcheck-suppress memleakOnRealloc
pAlloc = reallocarray(pAlloc, 3, 3);
Expand Down
3 changes: 2 additions & 1 deletion test/cfg/emscripten.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ void em_asm_ptr_memleak_test()
const char *str = static_cast<char*>(EM_ASM_PTR({
return stringToNewUTF8("Hello");
}));
// cppcheck-suppress nullPointerOutOfMemory
printf("%s", str);

// cppcheck-suppress memleak
Expand All @@ -88,4 +89,4 @@ void em_js_test()
{
two_alerts();
take_args(100, 35.5);
}
}
7 changes: 7 additions & 0 deletions test/cfg/gnu.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ int deallocuse_backtrace(int size) {
void **buffer = (void **)malloc(sizeof(void *) * size);
free(buffer);
// cppcheck-suppress deallocuse
// cppcheck-suppress nullPointerOutOfMemory
int numEntries = backtrace(buffer, size);
return numEntries;
}
Expand Down Expand Up @@ -331,6 +332,7 @@ void valid_code(int argInt1, va_list valist_arg, const int * parg)
// cppcheck-suppress valueFlowBailoutIncompleteVar
const void * p_mmap = mmap(NULL, 1, PROT_NONE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
printf("%p", p_mmap);
// cppcheck-suppress nullPointerOutOfMemory
munmap(p_mmap, 1);

uint16_t i16_1 = 0, i16_2;
Expand Down Expand Up @@ -427,6 +429,7 @@ void memleak_asprintf8(const char *fmt, const int arg) // #12204
void memleak_xmalloc()
{
char *p = (char*)xmalloc(10);
// cppcheck-suppress nullPointerOutOfMemory
p[9] = 0;
// cppcheck-suppress memleak
}
Expand Down Expand Up @@ -464,14 +467,18 @@ void bufferAccessOutOfBounds()
sethostname(buf, 4);

char * pAlloc1 = xcalloc(2, 4);
// cppcheck-suppress nullPointerOutOfMemory
memset(pAlloc1, 0, 8);
// cppcheck-suppress bufferAccessOutOfBounds
// cppcheck-suppress nullPointerOutOfMemory
memset(pAlloc1, 0, 9);
free(pAlloc1);

char * pAlloc2 = xmalloc(4);
// cppcheck-suppress nullPointerOutOfMemory
memset(pAlloc2, 0, 4);
// cppcheck-suppress bufferAccessOutOfBounds
// cppcheck-suppress nullPointerOutOfMemory
memset(pAlloc2, 0, 5);

pAlloc2 = xrealloc(pAlloc2, 10);
Expand Down
2 changes: 2 additions & 0 deletions test/cfg/gtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ void validCode(int argInt, GHashTableIter * hash_table_iter, GHashTable * hash_t
g_printerr("err");

GString * pGStr1 = g_string_new("test");
// cppcheck-suppress nullPointerOutOfMemory
g_string_append(pGStr1, "a");
g_string_free(pGStr1, TRUE);

gchar * pGchar1 = g_strconcat("a", "b", NULL);
// cppcheck-suppress nullPointerOutOfMemory
printf("%s", pGchar1);
g_free(pGchar1);

Expand Down
8 changes: 8 additions & 0 deletions test/cfg/posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -921,11 +921,13 @@ typedef struct {

S_memalign* posix_memalign_memleak(size_t n) { // #12248
S_memalign* s = malloc(sizeof(*s));
// cppcheck-suppress nullPointerOutOfMemory
s->N = n;
if (0 != posix_memalign((void**)&s->data, 16, n * sizeof(int))) {
free(s);
return NULL;
}
// cppcheck-suppress nullPointerOutOfMemory
memset(s->data, 0, n * sizeof(int));
return s;
}
Expand Down Expand Up @@ -1155,13 +1157,16 @@ int munmap_no_double_free(int tofd, // #11396
return -1;
}

// cppcheck-suppress nullPointerOutOfMemory
memcpy(tptr,fptr,len);

// cppcheck-suppress nullPointerOutOfMemory
if ((rc = munmap(fptr,len)) != 0) {
// cppcheck-suppress memleak
return -1;
}

// cppcheck-suppress nullPointerOutOfMemory
if ((rc = munmap(tptr,len)) != 0) {
return -1;
}
Expand All @@ -1181,6 +1186,7 @@ void resourceLeak_fdopen2(const char* fn) // #2767
// cppcheck-suppress valueFlowBailoutIncompleteVar
int fi = open(fn, O_RDONLY);
FILE* fd = fdopen(fi, "r");
// cppcheck-suppress nullPointerOutOfResources
fclose(fd);
}

Expand Down Expand Up @@ -1240,8 +1246,10 @@ void resourceLeak_open2(void)
void noleak(int x, int y, int z)
{
DIR *p1 = fdopendir(x);
// cppcheck-suppress nullPointerOutOfResources
closedir(p1);
DIR *p2 = opendir("abc");
// cppcheck-suppress nullPointerOutOfResources
closedir(p2);
int s = socket(AF_INET,SOCK_STREAM,0);
close(s);
Expand Down
6 changes: 6 additions & 0 deletions test/cfg/selinux.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ void selabel_fail2(void)
struct selabel_handle *hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);

char *ctx;
// cppcheck-suppress nullPointerOutOfResources
selabel_lookup(hnd, &ctx, "/", 0);

// cppcheck-suppress nullPointerOutOfResources
selabel_close(hnd);

// cppcheck-suppress memleak
Expand All @@ -92,14 +94,18 @@ void selabel_success(void)
struct selabel_handle *hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);

char *ctx;
// cppcheck-suppress nullPointerOutOfResources
selabel_lookup(hnd, &ctx, "/", 0);

freecon(ctx);

// cppcheck-suppress nullPointerOutOfResources
(void)selabel_cmp(hnd, hnd);

// cppcheck-suppress nullPointerOutOfResources
selabel_stats(hnd);

// cppcheck-suppress nullPointerOutOfResources
selabel_close(hnd);
}

Expand Down
Loading
Loading