[cfe-dev] Static Analyzer: pointer alias assignment
Gábor Kozár
kozargabor at gmail.com
Wed Jul 24 10:32:05 PDT 2013
I'm not working on analyzer features, just on new checkers for the
analyzer. Anyhow, the info is definitely interesting! I forwarded your
comments about this to my boss. Thank you!
I'm still stuck though with the original problem I wrote about, most
recently in answer to Ted's e-mail. (Sorry, I'm just worried it will get
buried under all the others e-mails.)
2013/7/24 Anna Zaks <ganna at apple.com>
>
> On Jul 24, 2013, at 10:08 AM, Gábor Kozár <kozargabor at gmail.com> wrote:
>
> Thanks for the info! I missed -analyze-function, because I grep-ed for
> "analyzer" (-analyzer-checker, -analyzer-checker-help, etc. - I assumed
> it's a general prefix).
>
> > *This example definitely works for me:
>
> *
> Something weird is definitely going on here (see my last e-mail). I'll try
> to investigate tomorrow.*
>
> > We assume that you are developing against TOT unless stated otherwise.
> Is there a reason why you don't?
>
> *
> What do you mean by "TOT"?
> If you're asking why I'm not using the latest SVN version, it's because my
> boss is concerned about stability. How stable is the current SVN build, in
> general? Is it reliable, in terms of existing features and functionality?
> Obviously I don't expect work-in-progress features to work, but it'd be
> nice if they were disabled by default until they are stable - is this
> currently done, or what is your policy in this?
>
>
> We definitely recommend developing new analyzer features on the latest SVN
> revisions, even though they might be less stable than a released version.
> We constantly test the compiler and the analyzer and revert any commits
> that cause regressions. The release compiler receives more testing, but we
> do not do any additional analyzer tests for the release. The experimental
> features usually do not get turned on until ready.
>
> Anna.
>
>
> 2013/7/24 Anna Zaks <ganna at apple.com>
>
>>
>> On Jul 24, 2013, at 7:12 AM, Gábor Kozár <kozargabor at gmail.com> wrote:
>>
>> Anna,
>>
>> > *Can this be designed as an extension of the Dereference checker?
>>
>> *
>> That was my original thought, but looking at the Dereference checker
>> source code, it writes that it's a built-in check in ExprEngine, which
>> discouraged me to try to extend it.
>>
>>
>> I am not sure which code comment you are referring to but Dereference
>> checker is a very good checker to learn from. I highly recommend to
>> understand how it works before extending it.
>>
>> I also have no idea how it works or what it does - I've been unable to
>> have it produce any warnings, except for this trivial case:
>>
>> Foo* fp = nullptr;
>> fp->baz();
>>
>> And now I tried with your code example, and it also produces a warning.
>> By the way, when analyzing a translation unit, which function(s) are
>> treated as entry points?
>>
>>
>> AnalysisConsumer.cpp contains code that decides on the order of the
>> functions being analyzed, which ones are analyzed as top level, etc.
>>
>> It appears to me that all of them are, but are there any restrictions, or
>> ways to control this? I do not see any promising args with clang++ -cc1
>> --help.
>>
>> You might find this one helpful:
>> clang -cc1 -help | grep "function"
>> -analyze-function <value>
>> Run analysis on specific function
>> Specify the function selection heuristic used
>> during inlining
>> Force the static analyzer to analyze functions
>> defined in header files
>> Analyze the definitions of blocks in addition
>> to functions
>> Maximum depth of recursive constexpr function
>> calls
>> Parse templated function definitions at the
>> end of the translation unit
>> -analyzer-display-progress
>> Emit verbose output about the analyzer's
>> progress
>>
>> Also, AnalyzerOptions.cpp is a good reference for analyzer options.
>>
>> Anyway, this code snippet still does not cause the Dereference checker to
>> raise a warning, even though it is trivial:
>>
>> Foo* fp = getFooPtr(); // based on runtime data, either returns nullptr,
>> or an allocated object
>> if(!fp)
>> {
>> fp->baz();
>> }
>>
>>
>> This example definitely works for me:
>>
>> zaks$ cat ~/tmp/ex.cpp
>> struct Foo {
>> void baz();
>> };
>> Foo* getFooPtr();
>> void f() {
>> Foo* fp = getFooPtr(); // based on runtime data, either returns
>> nullptr, or an allocated object
>> if(!fp) {
>> fp->baz();
>> }
>> }
>>
>> $ clang --analyze ~/tmp/ex.cpp
>> */Users/zaks/tmp/ex.cpp:8:7: **warning: **Called C++ object pointer is
>> null*
>> fp->baz();
>> * ^~~~~~~~~*
>> 1 warning generated.
>>
>> To the best of my knowledge, the if(!bar) line creates two code paths:
>> one on which the condition is true (i.e. fp == nullptr), and one on which
>> the condition is false (i.e. fp != nullptr). Obviously the first statement
>> on the former code path is the fp->baz() line, analyzing which the Static
>> Analyzer tells me that 'fp' is constrained to be null (using
>> ProgramState::isNull). Therefore, my checker can easily find this issue.
>>
>> Browsing through the source code of the DereferenceChecker, I can tell it
>> does not use ProgramState::isNull, but instead ProgramState::assume.
>> However, as far as I can tell, it does exactly nothing - I can see that
>> it's supposed to either raise a warning, or dispatch an
>> ImplicitNullDerefEvent, but neither happens on the code example above (I
>> tested the latter by running both checkers with -analyzer-checker, and in
>> my checker, I subscribe to check::Event<ImplicitNullDerefEvent>).
>>
>> So why is ProgramState::assume used instead of ProgramState::isNull?
>>
>>
>> If you drill down the implementation of isNull, you'll see that it's
>> similar to assume, possibly more efficient and specialized for NULL.
>>
>>
>> > *Also, the Dereference checker does work fine in presence of
>> aliases... Is there a reason why it's different in your setting?
>>
>> *
>> I'm using Clang 3.3, so unless significant changes have been made to the
>> Static Analyzer or the Dereference checker, things should work the same. I
>> have not modified the Clang source code, except for adding a bunch of
>> custom checkers.
>>
>>
>> We assume that you are developing against TOT unless stated otherwise. Is
>> there a reason why you don't?
>>
>> Gabor
>>
>>
>> 2013/7/23 Anna Zaks <ganna at apple.com>
>>
>>>
>>> On Jul 23, 2013, at 10:33 AM, Gábor Kozár <kozargabor at gmail.com> wrote:
>>>
>>> Hi Anna,
>>>
>>> I'm building a checker that detects inconsistent pointer usages, for
>>> example when a pointer is dereferenced, then along the same path is
>>> null-checked (without its value changing in between, obviously).
>>>
>>>
>>> Can this be designed as an extension of the Dereference checker?
>>>
>>> Also, the Dereference checker does work fine in presence of aliases...
>>> Is there a reason why it's different in your setting?
>>>
>>> int foo(int *p) {
>>> int *q = p;
>>> if (q)
>>> ;
>>> return *p;
>>> }
>>>
>>> zaks$ clang --analyze ~/tmp/ex.c -Xclang -analyzer-output=text
>>> */Users/zaks/tmp/ex.c:5:10: **warning: **Dereference of null pointer
>>> (loaded from variable 'p')*
>>> return *p;
>>> * ^~*
>>> */Users/zaks/tmp/ex.c:3:7: note: *Assuming 'q' is null
>>> if (q)
>>> * ^*
>>> */Users/zaks/tmp/ex.c:3:3: note: *Taking false branch
>>> if (q)
>>> * ^*
>>> */Users/zaks/tmp/ex.c:5:10: note: *Dereference of null pointer (loaded
>>> from variable 'p')
>>> return *p;
>>> * ^*
>>> 1 warning generated.
>>>
>>>
>>> Code example:
>>>
>>> Foo* f = getFoo();
>>> f->bar();
>>>
>>> if(f) // warn
>>> { ... }
>>>
>>> I want to be able to do this with aliases as well, for example:
>>>
>>> Foo* f = getFoo();
>>> f->bar();
>>>
>>> Foo* g = f;
>>> if(g) // warn
>>> { ... }
>>>
>>> What I need is to be able to get the SVals representing 'f' and 'g' when
>>> checkBind is called on the Foo* g = f; line. Currently, instead of 'f',
>>> Clang gives me the value that was bound to 'f'.
>>>
>>> Thanks for your help!
>>>
>>>
>>> 2013/7/23 Anna Zaks <ganna at apple.com>
>>>
>>>>
>>>> On Jul 23, 2013, at 9:21 AM, Gábor Kozár <kozargabor at gmail.com> wrote:
>>>>
>>>> During the analysis of a test code, the following two bindings happen
>>>> (checkBind), with their respective source lines:
>>>>
>>>> (Bind: location <= value)
>>>>
>>>> Bind: &fp <= &SymRegion{conj_$4{struct Foo *}}
>>>> Code: Foo* fp = getFooPtr();
>>>>
>>>> Bind: &ap <= &SymRegion{conj_$4{struct Foo *}}
>>>> Code: Foo* ap = fp;
>>>>
>>>> In the second line, I need to detect that 'ap' is in fact the alias of
>>>> 'fp'. Unfortunately, I cannot seem to find any way to get Clang SA to tell
>>>> me that "&SymRegion{conj_$4{struct Foo *}}" is stored in "fp", which seems
>>>> weird, because the source code is very clear.
>>>>
>>>>
>>>> As you observe the two binds you see that the same value is stored in
>>>> both.
>>>>
>>>> The analyzer does not perform alias analyzes as in it does not build
>>>> sets of aliases. As it models the execution in presence of aliases, we did
>>>> not find a need for the alias sets. Can you give a bit more background on
>>>> why you need this info? Maybe your goal can be achieved differently?
>>>>
>>>>
>>>> Some of the information I extracted, but is not really useful to me:
>>>> - original SVal: &SymRegion{conj_$4{struct Foo *}}
>>>> - getAsRegion(): SymRegion{conj_$4{struct Foo *}}
>>>> - state->getSVal(): &SymRegion{reg_$6<element{SymRegion{conj_$4{struct
>>>> Foo *}},0 S32b,struct Foo *}>} -- in fact, I have no idea what this is
>>>> - getAsSymbol(): conj_$4{struct Foo *}
>>>>
>>>> As a workaround, I can keep track of this information myself, but there
>>>> must be a built-in way to do this.
>>>> Any help would be appreciated. Many thanks!
>>>> _______________________________________________
>>>> cfe-dev mailing list
>>>> cfe-dev at cs.uiuc.edu
>>>> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
>>>>
>>>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20130724/9c50645e/attachment.html>
More information about the cfe-dev
mailing list