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 @@
+
+
+
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
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());
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"
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"