I'd like to match on all the ways a particular argument to a function can be null. Right now I'm using
hasArgument(
3,
anyOf(
cxxNullPtrLiteralExpr()
,integerLiteral() // Technically this would alert on a constant pointer; but that's madness
)
)
However this doesn't match the following code:
void* nullObj = nullptr;
function(nullptr, false, false, nullObj);
Is it possible/easy to track this and match it? Right now I have a very simpler matcher but I guess this type of analysis requires considerably more logic?
High-level answer
You can't just "match" an expression whose value is NULL. AST matching can only inspect the syntax of the argument, so if the argument is not a literal, you don't know if it might be NULL.
Instead, you need to use a flow-sensitive checker that queries the Clang SA constraint engine. The constraint engine tracks values as they flow through the program.
The core of such a checker looks like this:
bool NullArgChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
ProgramStateRef state = C.getState();
auto SVal = C.getSVal(CE->getArg(0)).getAs<DefinedOrUnknownSVal>();
if (SVal) {
ConditionTruthVal Nullness = state->isNull(*SVal);
if (Nullness.isConstrainedTrue()) {
Given a call expression CE, we get its first argument, then query the CheckerContext for the symbolic value SVal associated with the first argument. We then ask if that value is known to be NULL.
Complete example
Here is a complete example checker that reports a warning every time it sees a value known to be NULL being passed as the first argument of any function.
NullArgChecker.cpp:
// NullArgChecker.cpp
// https://stackoverflow.com/questions/57665383/how-can-i-match-a-pointer-to-a-null-object
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class NullArgChecker : public Checker< eval::Call > {
mutable std::unique_ptr<BuiltinBug> BT_nullarg;
public:
NullArgChecker() {}
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
};
} // end anonymous namespace
bool NullArgChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
ProgramStateRef state = C.getState();
auto SVal = C.getSVal(CE->getArg(0)).getAs<DefinedOrUnknownSVal>();
if (SVal) {
// This is the core of this example checker: we query the constraint
// engine to see if the symbolic value associated with the first
// argument is known to be NULL along the current path.
ConditionTruthVal Nullness = state->isNull(*SVal);
if (Nullness.isConstrainedTrue()) {
// Create a warning for this condition.
ExplodedNode *N = C.generateErrorNode();
if (N) {
if (!BT_nullarg) {
BT_nullarg.reset(new BuiltinBug(
this, "Null Argument", "The first argument is NULL."));
}
C.emitReport(llvm::make_unique<BugReport>(
*BT_nullarg, BT_nullarg->getDescription(), N));
}
}
}
return false;
}
void ento::registerNullArgChecker(CheckerManager &mgr) {
mgr.registerChecker<NullArgChecker>();
}
bool ento::shouldRegisterNullArgChecker(const LangOptions &LO) {
return true;
}
Changes to other files to hook this in:
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
## -148,6 +148,10 ## def NonnullGlobalConstantsChecker: Checker<"NonnilStringCon
stants">,
let ParentPackage = CoreAlpha in {
+def NullArgChecker : Checker<"NullArg">,
+ HelpText<"Check for passing a NULL argument">,
+ Documentation<NotDocumented>;
+
def BoolAssignmentChecker : Checker<"BoolAssignment">,
HelpText<"Warn about assigning non-{0,1} values to Boolean variables">,
Documentation<HasAlphaDocumentation>;
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
## -62,6 +62,7 ## add_clang_library(clangStaticAnalyzerCheckers
NonNullParamChecker.cpp
NonnullGlobalConstantsChecker.cpp
NullabilityChecker.cpp
+ NullArgChecker.cpp
NumberObjectConversionChecker.cpp
ObjCAtSyncChecker.cpp
ObjCAutoreleaseWriteChecker.cpp
Example input to test it on:
// nullargpp.cpp
// Testing NullArg checker with C++.
#include <stddef.h> // NULL
void somefunc(int*);
void nullarg1()
{
somefunc(NULL); // reported
somefunc(0); // reported
somefunc(nullptr); // reported
}
void nullarg2()
{
int *p = 0;
somefunc(p); // reported
}
void nullarg3(int *p)
{
if (p) {
somefunc(p); // not reported
}
else {
somefunc(p); // reported
}
}
void not_nullarg(int *p)
{
somefunc(p); // not reported
}
Example run:
$ g++ -std=c++11 -E -o nullargpp.ii nullargpp.cpp
$ ~/bld/llvm-project/build/bin/clang -cc1 -analyze -analyzer-checker=alpha.core.NullArg nullargpp.ii
nullargpp.cpp:10:3: warning: The first argument is NULL
somefunc(
^~~~~~~~~
nullargpp.cpp:11:3: warning: The first argument is NULL
somefunc(0);
^~~~~~~~~~~
nullargpp.cpp:12:3: warning: The first argument is NULL
somefunc(nullptr);
^~~~~~~~~~~~~~~~~
nullargpp.cpp:18:3: warning: The first argument is NULL
somefunc(p);
^~~~~~~~~~~
nullargpp.cpp:27:5: warning: The first argument is NULL
somefunc(p);
^~~~~~~~~~~
5 warnings generated.
For maximum specificity, the above changes were made to llvm-project commit 05efe0fdc4 (March 2019), running on Linux, but should work with any Clang v9.
Related
I am using std::list's predicate to update the list based on predicate. But calling in the OnInitDialog() throws compilation error. My code is as follows:
The below is .h:
class CDlgWindow : public CDialog
{
private:
bool single_digit (const int &value);
int _days;
}
The below is .cpp:
CDlgWindow::CDlgWindow(CWnd* pParent, CString strInfo, int days) //ctor
{
_days = days;
//_strInfo = strInfo
}
bool CDlgWindow::single_digit(const int& value)
{
return (value >= _days);
}
BOOL CDlgWindow::OnInitDialog()
{
CDialog::OnInitDialog();
CenterWindow();
.
.
.
int numArr[] = {10,20,30,40};
int size = sizeof(numArr)/sizeof(numArr[0]);
std::list<int> numList (numArr, numArr+size);
numList.remove_if(single_digit); //Error C3867 here!
.
.
}
Complete error message:
Error C3867 function call missing argument list, use '&CDlgWindow::single_digit' to create a pointer to member.
I am trying to understand the functors concept. As I checked in C++11, we have lambdas for easier implementation. Please guide me to understand more on this issue. Thanks!
std::list's remove_if member needs a unary predicate (p) that operates on values (v). The expression p(v) must be valid. Which it isn't if p is a non-static class member (see repro).
There are two options:
Make the predicate (single_digit) a static class member:
class CDlgWindow : public CDialog
{
private:
static bool single_digit (const int &value);
// ...
}
Make the predicate a free function:
bool single_digit(int const& value) {
static int days_ = ...;
return (value >= days_);
}
If you go with option 1 you will have to make _days static as well, since a static member function cannot access non-static instance data. If _days is a compile-time constant, make sure to mark it const as well. That'll open up some compiler optimizations.
This is all hoping that things haven't significantly changed between C++98 and C++11. It's hard to find a C++98 compiler to verify this.
I have followed the ideas I saw on the internet:
UpdateColumnIndexVector(m_vColumnIndexesToExclude, WriteColumnIndexesToExclude);
That calls:
void CCreateReportDlg::UpdateColumnIndexVector(ColumnIndexVector &rvData, std::function<void()> WriteToRegistry)
{
bool bModified = true;
// Code simplified
if (bModified)
{
WriteToRegistry();
}
}
The function being passed in:
void CCreateReportDlg::WriteColumnIndexesToExclude()
{
ByteVector vData(m_vColumnIndexesToExclude.begin(), m_vColumnIndexesToExclude.end()); // int to BYTE
const CString strSection = theApp.GetActiveScheduleSection(_T("Options"));
theApp.WriteProfileVector(strSection, _T("AssignStatesEx"), vData);
}
When I compile:
error C3867: CCreateReportDlg::WriteColumnIndexesToExclude: non-standard syntax; use & to create a pointer to member
Yet, if I change this line to:
UpdateColumnIndexVector(m_vColumnIndexesToExclude, &WriteColumnIndexesToExclude);
That does not compile either:
error C2276: &: illegal operation on bound member function expression
error C2660: CCreateReportDlg::UpdateColumnIndexVector: function does not take 1 arguments
WriteColumnIndexesToExclude is a private member of the dialog class.
What am I doing wrong?
PS. My project is MFC C++ Dialog.
I'm porting C++ app from Solaris to Linux and I'm stuck with the following error. The code is:
template <class MapSuperClass> class FWPointerMap : public MapSuperClass
{
public:
FWPointerMap()
{
_wipe = false;
}
FWPointerMap(const MapSuperClass* mMap)
{
MapSuperClass::const_iterator it = mMap->begin(); // line 50
while(it != mMap->end())
{
insert(MapSuperClass::value_type((*it).first, (*it).second));
it++;
}
_wipe = false;
}
And I get the following error:
../../framework/fwcore/hdr/FWPointerMap: In constructor FWPointerMap<MapSuperClass>::FWPointerMap(const MapSuperClass*):
../../framework/fwcore/hdr/FWPointerMap:50: error: expected ; before it
../../framework/fwcore/hdr/FWPointerMap:52: error: it was not declared in this scope
I think you just need to add 'typename' to tell the compiler that MapSuperClass::const_iterator is a type:
typename MapSuperClass::const_iterator it = mMap->begin(); // line 50
Because MaySuperClass is a class template parameter, the assumption is that the const_iterator member is a field. Using typename informs the compiler that it is in fact a type.
More information: http://en.wikipedia.org/wiki/Typename#A_method_for_indicating_that_a_dependent_name_is_a_type
I am working on a parallel avl tree and have ran into a problem. Here is the function that causes this problem:
template<typename T, int Threads>
bool PTreeSet<T, Threads>::tryInsert(Node* parent, Node* node) {
if (parent->key > node->key) {
return parent->left.compare_exchange_strong(nullptr, node); // <-- Error
} else if (parent->key < node->key) {
return parent->right.compare_exchange_strong(nullptr, node); // <-- Error
} else {
return false;
}
return true;
}
parent->lefthas type atomic<Node*>and I want to set that pointer to node if the current value is null. The compiler complains with error
error: no matching member function for call to 'compare_exchange_strong'
return parent->left.compare_exchange_strong(nullptr, node);
Why is this not valid code?
The first parameter of atomic<T>::compare_exhange_strong is a T&. It requires an lvalue. That's one half of the "exchange": the current value of the atomic is loaded into the object referred to by the first parameter.
You need something like this:
Node* tmp = nullptr;
parent->left.compare_exchange_strong(tmp, node);
As a side effect, if parent->left is not in fact NULL, you get its current value in tmp (which you can ignore if you don't need it, of course).
In an embedded python scenario we are using PyArg_ParseTupleAndKeywords to receive data from Python (version >=3 .x) and use it in a C++ application.
At the moment we have a similar setup:
PyObject* whatever(PyObject *self, PyObject *args, PyObject *keywds) {
....
static char* kwlist[] = { "foo", "bar", NULL };
...
if(!PyArg_ParseTupleAndKeywords(args, keywds, ..., kwlist, ...))
{
...bail out
however, if we pass more parameters than the two expected (e.g. issuing a python call like whatever(foo="a", bar="b", baz="c")) the whole thing crashes (not really, it returns an error, but that's beyond the scope here).
We would like to avoid such scenarios; it would be great if we could parse only the parameters in the kwlist and ignore everthing else. What's the best way to do it?
One solution we were thinking about was to convert kwlist into a dict, then manipulate it with PyDict_Merge or the like.
In the end we solved it like below:
(I'm answering my own question since nobody answered, and I think it might be valuable to somebody else in the future).
PyObject* whatever(PyObject *self, PyObject *args, PyObject *incoming_keywds)
{
static char* kwlist[] = { "foo", "bar", NULL };
PyObject* keywds = PyDict_New();
/**
* The following routine returns a subset of the incoming dictionary 'incoming_keywds'
* containing only the keys allowed in the list 'kwlist'
*/
for ( int i = 0 ; kwlist[i] != NULL ; i++ )
{
char* key = kwlist[i];
PyObject *single_key = Py_BuildValue("s", key);
if ( PyDict_Contains(incoming_keywds, single_key) )
{
// not checking for NULL as GetItem return value since
// we already checked above if the dict contains key 'single_key'
if ( PyDict_SetItem(keywds, single_key, PyDict_GetItem(incoming_keywds, single_key)) < 0 )
{
/* error */
}
}
Py_DECREF(single_key);
}
/** end */