[cfe-dev] Clang Static Analyzer - tracking object value

Gábor Kozár kozargabor at gmail.com
Tue Apr 30 05:44:48 PDT 2013


Hi Jordan,

Thank you very much for your help! I have no idea how I missed that. That
was a totally derp question, sorry.

> *...a lot of C++ objects are value objects, which means they get copied
from place to place, and currently it's up to checker writers to determine
when that copy happens. Anna actually started sketching out a C++ iterator
checker and realized that this problem was nontrivial (though by no means
insurmountable).

*
I have thought that's what checkBind() is for - but I haven't really looked
into what is it that it exactly does. Although I imagine that wrapping an
object inside another object would cause complications - because in that
case you have to check how the copy ctor behaves in relation to the wrapped
object (i.e. is it copied or is a new one created, or is it passed by
reference/pointer?), and then track it accordingly. Hmm... yeah, I think
can see why this is a non-trivial matter. :) I do find it interesting
though - might be something I'll look into during the summer holiday.

> *Aside: if the region is more important than the symbol, why did we
choose to attach our stream information to the symbol in
SimpleStreamChecker?*

It makes sense: fopen() and the like are not ctors, so - as far as I know -
the analyzer can't really reason about their return value, as it doesn't do
any inter-procedural analysis. That means that it can't do better than just
a create a symbol for their return value. My scenario is different, because
in my case, transaction objects are created by their ctors, and so the
analyzer has more information.

> *This "usually" is another problem for you—if we're constructing an
anonymous temporary object like 'MyHandler(this)', the expression may
result in an rvalue, which means the value represents a collection of
struct field bindings instead of an actual region in memory.

*
I did a quick test on this rvalue case, and discovered that the analyzer
(when invoked with my checker) seems to hang indefinitely on this code:

class MyTransaction {};

class TransactionWrapper
{
public:
    TransactionWrapper(MyTransaction tr) : m_tr(tr) {}

private:
    MyTransaction m_tr;
};

int main(int argc, char* argv[])
{
    auto trw = TransactionWrapper(MyTransaction());
    return 0;
}

It reports the creation of the MyTransaction object (apparently represented
by CXXTempObjectRegion in this case), then it also shows the copy (why
isn't it getting moved?), and also shows that the original MyTransaction
objects dies (at the end of the statement I imagine) - but then it just
hangs. What could cause such behavior?

Thanks again!

Gabor



2013/4/30 Jordan Rose <jordan_rose at apple.com>

> Aack, no, this is not how it works. :-)
>
> From a checker-writer's perspective, conjureSymbolVal() always creates a *
> new* symbol. Now, that symbol isn't *really* unique, because it's cheaper
> if we reuse symbols along multiple paths, but you really can't use it to
> represent the same symbol that the analyzer core will create.
>
> Instead, what you want is a *post-*call check, where you can then use
> getSVal on the constructor expression to get the return value (which is *
> usually* the region that was just constructed*). Unfortunately...
>
> Next, my problem is with detecting when members of the transaction object
> is called. I was trying to get the SymbolRef of the object whose member is
> called:
>
> const CXXInstanceCall* instanceCall =
> llvm::dyn_cast<CXXInstanceCall>(&call);
> ...
> SymbolRef transactionObjectSymbol =
> instanceCall->getCXXThisVal().getAsSymbol();
>
> But I find that this SymbolRef is always NULL. Why is that?
>
>
> ...not every C++ object has an associated symbol. As we describe in "Building
> a Checker in 24 Hours <http://llvm.org/devmtg/2012-11/>", a symbol is
> only used to represent a *value* that the analyzer can't reason about.
> What you're interested in here is the *region,* which represents the
> memory backing the abstract C++ object. Unfortunately...
>
> ...a lot of C++ objects are value objects, which means they get copied
> from place to place, and currently it's up to checker writers to determine
> when that copy happens. Anna actually started sketching out a C++ iterator
> checker and realized that this problem was nontrivial (though by no means
> insurmountable).
>
> (Aside: if the region is more important than the symbol, why did we choose
> to attach our stream information to the symbol in SimpleStreamChecker?
> Well, the type of a POSIX stream is FILE*, but the pointer-ness of it is
> essentially an implementation detail. It's more important to associate the
> state with the return *value* of fopen(). Indeed, the same logic in
> SimpleStreamChecker could be used for the raw file descriptors used by
> open(), write(), and close().)
>
> That's sort of where we stand today. I don't have a good answer for you on
> how to deal with C++ value objects. Sorry!
>
> Jordan
>
> * This *"usually"* is another problem for you—if we're constructing an
> anonymous temporary object like 'MyHandler(this)', the expression may
> result in an rvalue, which means the value represents a collection of
> struct field bindings instead of an actual region in memory. I would not
> worry about this case so much right now, though.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20130430/c56513a2/attachment.html>


More information about the cfe-dev mailing list