From ec1f650caf7dfcdc86d639675ae2d7b67caaf7b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 28 Aug 2026 15:06:10 +0200 Subject: [PATCH 1/5] test --- test/testnullpointer.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/testnullpointer.cpp b/test/testnullpointer.cpp index 81c44d58811..2fa258867ff 100644 --- a/test/testnullpointer.cpp +++ b/test/testnullpointer.cpp @@ -148,6 +148,9 @@ class TestNullPointer : public TestFixture { TEST_CASE(nullpointer108); TEST_CASE(nullpointer109); TEST_CASE(nullpointer110); // #14937 + TEST_CASE(nullpointer111); + TEST_CASE(nullpointer112); + TEST_CASE(nullpointer113); TEST_CASE(nullpointer_addressOf); // address of TEST_CASE(nullpointerSwitch); // #2626 TEST_CASE(nullpointer_cast); // #4692 @@ -3147,6 +3150,34 @@ class TestNullPointer : public TestFixture { ASSERT_EQUALS("", errout_str()); } + void nullpointer111() + { + check("void f(void) {\n" + " char *str = getenv(\"TMP\");\n" + " char *c = *str;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3:16]: (warning) Possible null pointer dereference: str [nullPointer]\n", errout_str()); + } + + void nullpointer112() + { + check("void f(void) {\n" + " char *str = std::getenv(\"TMP\");\n" + " char *c = *str;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3:16]: (warning) Possible null pointer dereference: str [nullPointer]\n", errout_str()); + } + + void nullpointer113() + { + check("void f(void) {\n" + " char *str = std::getenv(\"TMP\");\n" + " if (!str) return;\n" + " char *c = *str;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + void nullpointer_addressOf() { // address of check("void f() {\n" " struct X *x = 0;\n" From 881fa79e6ef911a214587943b674f3f93a81646c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 27 Aug 2026 15:31:29 +0200 Subject: [PATCH 2/5] add possible-null to returnValue in library config --- lib/library.cpp | 2 ++ lib/library.h | 1 + lib/valueflow.cpp | 11 +++++++++++ 3 files changed, 14 insertions(+) diff --git a/lib/library.cpp b/lib/library.cpp index 4e542bc55ef..3942633ac56 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -900,6 +900,8 @@ Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, co mData->mReturnValue[name] = expr; if (const char *type = functionnode->Attribute("type")) mData->mReturnValueType[name] = type; + if (functionnode->BoolAttribute("possible-null", false)) + func.isPossibleNull = true; if (const char *container = functionnode->Attribute("container")) mData->mReturnValueContainer[name] = strToInt(container); // cppcheck-suppress shadowFunction - TODO: fix this diff --git a/lib/library.h b/lib/library.h index 709a0db2b90..1d0f7c71f56 100644 --- a/lib/library.h +++ b/lib/library.h @@ -314,6 +314,7 @@ class CPPCHECKLIB Library { bool leakignore{}; bool isconst{}; bool ispure{}; + bool isPossibleNull{}; UseRetValType useretval = UseRetValType::NONE; bool ignore{}; // ignore functions/macros from a library (gtk, qt etc) bool formatstr{}; diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 325a55e98e5..8827ad156e7 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -7234,6 +7234,17 @@ static void valueFlowUnknownFunctionReturn(TokenList& tokenlist, const Settings& } } + const Token *ftok = tok->astOperand1(); + while (ftok && Token::simpleMatch(ftok, "::")) + ftok = ftok->astOperand2() ? ftok->astOperand2() : ftok->astOperand1(); + const Library::Function *func = ftok ? settings.library.getFunction(ftok) : nullptr; + if (func && func->isPossibleNull) { + ValueFlow::Value value(0); + value.setPossible(); + value.errorPath.emplace_back(tok, "Assuming function returns NULL"); + setTokenValue(tok, std::move(value), settings); + } + if (settings.checkUnknownFunctionReturn.find(tok->strAt(-1)) == settings.checkUnknownFunctionReturn.end()) continue; std::vector unknownValues = settings.library.unknownReturnValues(tok->astOperand1()); From 2d320c87459faec694da3763af74a093fb5e982f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 27 Aug 2026 15:37:50 +0200 Subject: [PATCH 3/5] update std configuration --- cfg/std.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/std.cfg b/cfg/std.cfg index 6c434b8715c..5b5788e6009 100644 --- a/cfg/std.cfg +++ b/cfg/std.cfg @@ -2534,7 +2534,7 @@ - + false From dcd7aff1a435e46553b20834a19cf0e85f941935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 28 Aug 2026 15:16:18 +0200 Subject: [PATCH 4/5] add library test --- test/testlibrary.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/testlibrary.cpp b/test/testlibrary.cpp index 28264a02a53..a7285f3ee68 100644 --- a/test/testlibrary.cpp +++ b/test/testlibrary.cpp @@ -57,6 +57,7 @@ class TestLibrary : public TestFixture { TEST_CASE(function_method); TEST_CASE(function_baseClassMethod); // calling method in base class TEST_CASE(function_warn); + TEST_CASE(function_possible_null); TEST_CASE(memory); TEST_CASE(memory2); // define extra "free" allocation functions TEST_CASE(memory3); @@ -643,6 +644,24 @@ class TestLibrary : public TestFixture { } } + void function_possible_null() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + " \n" + ""; + + Library library; + ASSERT(LibraryHelper::loadxmldata(library, xmldata, sizeof(xmldata))); + + const char code[] = "a();\n"; + const SimpleTokenList tokenList(code); + + const Library::Function *a = library.getFunction(tokenList.front()); + ASSERT(a->isPossibleNull); + } + void memory() const { constexpr char xmldata[] = "\n" "\n" From ce1628dc519632b40b1d255016edbb9fd3266535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 31 Aug 2026 10:54:15 +0200 Subject: [PATCH 5/5] update cppcheck-cfg.rng --- cfg/cppcheck-cfg.rng | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cfg/cppcheck-cfg.rng b/cfg/cppcheck-cfg.rng index a54c967aaac..ab243105c2b 100644 --- a/cfg/cppcheck-cfg.rng +++ b/cfg/cppcheck-cfg.rng @@ -166,6 +166,9 @@ + + +