Check for functions calls in sizeof calculations#1111
Conversation
amai2012
left a comment
There was a problem hiding this comment.
Maybe I want to keep numElements return codes of GetType()? What's wrong with it? I don't see an obvious error...
Even if it would indicate an error one should introduce another message probably.
|
Thanks! Could you tweak the error message so it says something like "found function call within sizeof()". A separate error id would help also. Before this is accepted I want that we test this on real code and see how much noise it generates (I will test it with the daca2 script). |
What do you mean?
Because |
Sure, is there a dev document that explains how to add new ids?
I did a simple grep(with
What is the daca2 script? |
No sadly not. But I think your solution is ok. Spontaneously I thought we would still have 1 check function but it could call both
Maybe unexpanded macros could be a problem. It's normal that macro definitions are missing. you might want to ensure that it's really a function (the Token::function() is non-null, or _settings->library has function configuration). EDIT: I see, you use Token::function(), then unexpanded macros are not a problem. There is no way now to detect if it is in a template. But we have
It's like your grep but a more powerful. The script runs latest Cppcheck on all debian source code (thousands of projects, 100+ GB of source code). The results are shown here: http://cppcheck.sourceforge.net/devinfo/daca2-report/daca2.html If you want to look at some particular warnings and have a error id you want to look at you can use this cgi script (this takes several seconds to load): http://cppcheck.sourceforge.net/cgi-bin/daca2-search.cgi?id=sizeofCalculation |
|
I assume you will fix the test failure soonish: |
|
I believe the code looks pretty ok. if the test is fixed I will apply this and then the daca2 script can test it. |
Would that change trigger a warning on #include <cstdlib>
short foo() {
return 1;
}
int main() {
const int NUM=42;
short* buffer = (short*)malloc(NUM*sizeof foo() );
for (int i=0; i<NUM; ++i)
{
buffer[i] = foo();
}
return 0;
}? That would be a false positive IMHO. Even the small example from the PR int foo() { return 1; }; int a,sizeof(sizeof(foo()))is a message I don't want to see at all. |
You have of course simplified your code. But it looks weird to me; why not use
Yes as far as I see in the code, the intention seems to be to only report the "Calling 'sizeof' on 'sizeof'." warning and not warn about the function call. |
Yes it will, but grepping over qtbase, llvm, cocos2d, rethinkdb, redis, and php-src, I have only seen two cases for this. So its not very common. |
Actually that's not really the case. The code bails on I also found a problem with code like this: template<class T>
struct A
{
static B f(const B &);
static A f(const A &);
static A &g();
static T &h();
enum {
X = sizeof(f(g() >> h())) == sizeof(A),
Y = sizeof(f(g() << h())) == sizeof(A),
Z = X & Y
};
};It seems like cppcheck cant resolve the function |
Hmm it is not conclusive how
It is a bit weird implementation. it is preferable to look at the ast tree instead of walking the token list. I would suggest that you only look at the astOperand2() for the "(" token. |
|
Unfortunately, our |
How do you walk the ast? I didn't see any ast matchers.
But I don't know if this would catch the |
Is this meant for PR #1114? |
We don't have it. I would like to have it someday.
It would. It would not catch |
danmar
left a comment
There was a problem hiding this comment.
some more stylistic nits
| "{\n" | ||
| " int bar() { return 1; };\n" | ||
| "}\n" | ||
| "Foo f;int a,sizeof(f.bar())"); |
There was a problem hiding this comment.
this test code is weird. it is not valid syntax. how about replacing int a,sizeof(f.bar()) with int a=sizeof(f.bar());.
|
|
||
| check("#define foo() int\n" | ||
| "int a,sizeof(foo())"); | ||
| ASSERT_EQUALS("", errout.str()); |
There was a problem hiding this comment.
this is not technically correct. The testcases are not preprocessed. so I believe that during testing the checker would see # define foo ( ) int .... But when you actually run it on this code, it will not see the define at all. Then the checker will just see sizeof ( int ).
Imho you can just remove the first line and assert that the checker will not warn when a sizeof(foo()) is seen and it does not know what foo is.
|
|
||
| check("#define M(x) sizeof(x)\n" | ||
| "int foo() { return 1; }; int a,M(foo())"); | ||
| ASSERT_EQUALS("", errout.str()); |
There was a problem hiding this comment.
this is not properly tested because we don't run the preprocessor in the tests. you can write a test code and indicate that certain tokens are expanded macro(s) using the special character $.
int a = $sizeof $( foo() $) ;
that would mean that the Token::isExpandedMacro() would be true for the outer "sizeof (" and ")" tokens. And it would be false for foo().
| void CheckSizeof::sizeofFunctionError(const Token *tok, bool inconclusive) | ||
| { | ||
| reportError(tok, Severity::warning, | ||
| "sizeofFunction", "Found function call inside sizeof().", CWE682, inconclusive); |
There was a problem hiding this comment.
I think the id sizeofFunctionCall would be a bit better
| } | ||
|
|
||
| for (const Token *argument = tok->next()->astOperand2(); argument; argument = argument->astOperand2()) { | ||
| const Token *checkToken = argument->previous(); |
There was a problem hiding this comment.
why do you have a loop, is there a test case where it is not enough to only look at the first token?
There was a problem hiding this comment.
Yes, when doing sizeof(x.foo()), but let me double check.
There was a problem hiding this comment.
You can rewrite that as ’sizeof((x.foo)())’. Then you can intuitively see that the operands for ’.’ are x and foo.
There was a problem hiding this comment.
You are right, I was doing something else wrong. I updated to just check the first token.
| } | ||
| } | ||
|
|
||
| void CheckSizeof::sizeofFunctionError(const Token *tok, bool inconclusive) |
There was a problem hiding this comment.
The inconclusive argument can be removed, it's always false. Unless you plan to start using it someday.
I think that quite reasonable generic code could be written that way.
Yes, that intention I would support. |
A common case is to call an overloaded function in order to build a type trait. However, I avoid this case by making sure the function is not overloaded. |
A generic code example (invented, not real code): I doubt that ValueType determines the foo1 return type properly now. But the checker should not warn for such code. Well I think that when daca2 is running, we will see lots of real test cases. |
|
I merged this. Now daca2 will be running this checker. Results from this checker will be available here: http://cppcheck.sourceforge.net/cgi-bin/daca2-search.cgi?id=sizeofFunctionCall The first results could come in a few hours. |
How long does this take? I checked and there still aren't any results. |
|
I would guess that tomorrow evening it has run through all the code. We can already see that this is not noisy. It is not a problem for me if it doesn't catch many bugs. |
|
Still no results. Weird. One explanation is that in the daca2 script, no include paths are set and therefore the function pointer will often be NULL. Maybe we could start trying to set the include paths. |
|
I have gotten results from some codebases without using include. Maybe those codebases are not part of daca2. I am not sure why there isn't any results. |
A common mistake that I have found in our codebase is calling a function to get an integer or enum that represents the type such as:
This extends cppcheck for sizeof calculation to check for function calls as well.