Skip to content
Draft
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
3 changes: 3 additions & 0 deletions cfg/cppcheck-cfg.rng
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@
</data>
</attribute>
</optional>
<optional>
<attribute name="possible-null"><ref name="DATA-BOOL"/></attribute>
</optional>
<optional>
<attribute name="container">
<data type="positiveInteger"/>
Expand Down
2 changes: 1 addition & 1 deletion cfg/std.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2534,7 +2534,7 @@
<!-- char * getenv(const char *name); -->
<function name="getenv,std::getenv">
<use-retval/>
<returnValue type="char *"/>
<returnValue type="char *" possible-null="true"/>
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1" direction="in">
Expand Down
2 changes: 2 additions & 0 deletions lib/library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>(container);
// cppcheck-suppress shadowFunction - TODO: fix this
Expand Down
1 change: 1 addition & 0 deletions lib/library.h
Original file line number Diff line number Diff line change
Expand Up @@ -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{};
Expand Down
11 changes: 11 additions & 0 deletions lib/valueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<MathLib::bigint> unknownValues = settings.library.unknownReturnValues(tok->astOperand1());
Expand Down
19 changes: 19 additions & 0 deletions test/testlibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -643,6 +644,24 @@ class TestLibrary : public TestFixture {
}
}

void function_possible_null() const {
constexpr char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"a\">\n"
" <returnValue type=\"void *\" possible-null=\"true\"/>\n"
" </function>\n"
"</def>";

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[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
Expand Down
31 changes: 31 additions & 0 deletions test/testnullpointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
Loading