[Analyzer] C++ Constructors Do Not Work

Hi All,

It seems that the clang static analyzer does not correctly handle C++ constructors. For example, I have the following code:

struct S {
S(int x, int y, int z) {
a = x;
b = y;
c = z;
}
int a;
int b;
int c;
};

void objectCreate() {
S *newS = new S(12, 0, 15);
if (newS->b)
newS->c++;
else
newS->a++;
delete newS;
}

Since newS->b initializes to 0, the expression “newS->a++” should never execute. However, the analyzer in fact generates two new states and evaluates both branches. If I replace the newS->b condition with the integer literal 0, then only the first branch is evaluated, as I would expect. I dug into this further, and found that newS’s constructor is called AFTER objectCreate() has been evaluated. In other words, newS’s constructor is called after “delete newS”. This is clearly the wrong behavior.

This is not the first time I have observed C++ constructors being handled incorrectly. I also posted several weeks ago with the same observation regarding implicit copy constructors.

Any help would be so very appreciated, as this has been driving me crazy!

~Scott Constable

Scott,

I suspect that the analyzer does not step into the constructor during the analysis but instead treats it as an opaque function. The temporary variable destructor support is lacking, so we choose not to “inline” or step into the constructors in some cases as well.

When this program is analyzed, objectCreate() is analyzed and the constructor of S is also analyzed as a top-level function. You can pass -analyzer-display-progress option to the analyzer to see the order in which the top-level functions are being analyzed. (http://clang-analyzer.llvm.org/checker_dev_manual.html)

Anna.

Anna,

Although I think I understand the issue, I’m still frustrated by the inconsistency it creates. For instance, I have observed that implicitly defined copy constructors are not inlined, whereas explicitly defined copy constructors are. Until just recently, I thought that explicitly defined constructors were always inlined, but apparently this is not the case when evaluating a CXXNewExpr. These inconsistencies seem to make it impossible to develop a uniform way to handle constructors in my analysis.

By the way, I’m using clang SA for data-flow analysis. So if I have a class defined as

class MyClass {
public:
MyClass(int x, int y, int z) {
a = x;
b = y;
c = z;
}
private:
int a;
int b;
int c;
};

then a call


MyClass C(i1, i2, i3);

where argument i2 is tainted should produce an object C where C.b is tainted. As long as the constructor is inlined, this works just as expected. If it’s not, I have no idea how to properly model this behavior. Is there another way to tackle this that I may not have considered?

~Scott

The analyzer does not give any guarantees about which functions will be inlined. Some of this is due to the lack of full implementation for some language features (such as destructors support) but there are other reasons for this as well, such as performance considerations. The main issue that will never completely go away is that you cannot assume that definitions of all methods/functions you call are available. They might be defined in another translation unit or in a library that the user does not have access to.

If you want to get more familiar with code that makes those decisions look at ExprEngine::defaultEvalCall which performs call evaluation. If first tries to get the function definition by calling getRuntimeDefinition(); the definition might not always be available. Afterwards, we consult a long list of policies to decide if a function whose definition we can access should be inlined. Take a look at ExprEngine::shouldInlineCall and mayInlineDecl.

The checkers/analysis built on top of the clang static analyzer have to be written to support cases where the implementation details of some functions are not available. These are usually treated in a conservative way to avoid false positives. A constructor is no different in that respect. How do your analysis treat method calls which cannot be inlined?

I hope this answers the question. Let me know if you need more pointers!
Anna.