-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[NFC][Static Analyzer] Rename and discuss about NotNullConstraint & NotNullBufferConstraint
#131374
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -367,13 +367,13 @@ class StdLibraryFunctionsChecker | |
| }; | ||
|
|
||
| /// Check null or non-null-ness of an argument that is of pointer type. | ||
| class NotNullConstraint : public ValueConstraint { | ||
| class NullnessConstraint : public ValueConstraint { | ||
| using ValueConstraint::ValueConstraint; | ||
| // This variable has a role when we negate the constraint. | ||
| bool CannotBeNull = true; | ||
|
|
||
| public: | ||
| NotNullConstraint(ArgNo ArgN, bool CannotBeNull = true) | ||
| NullnessConstraint(ArgNo ArgN, bool CannotBeNull = true) | ||
| : ValueConstraint(ArgN), CannotBeNull(CannotBeNull) {} | ||
|
|
||
| ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, | ||
|
|
@@ -389,9 +389,9 @@ class StdLibraryFunctionsChecker | |
| llvm::raw_ostream &Out) const override; | ||
|
|
||
| ValueConstraintPtr negate() const override { | ||
| NotNullConstraint Tmp(*this); | ||
| NullnessConstraint Tmp(*this); | ||
| Tmp.CannotBeNull = !this->CannotBeNull; | ||
| return std::make_shared<NotNullConstraint>(Tmp); | ||
| return std::make_shared<NullnessConstraint>(Tmp); | ||
| } | ||
|
|
||
| protected: | ||
|
|
@@ -407,17 +407,17 @@ class StdLibraryFunctionsChecker | |
| /// The argument is meant to be a buffer that has a size constraint, and it | ||
| /// is allowed to have a NULL value if the size is 0. The size can depend on | ||
| /// 1 or 2 additional arguments, if one of these is 0 the buffer is allowed to | ||
| /// be NULL. This is useful for functions like `fread` which have this special | ||
| /// property. | ||
| class NotNullBufferConstraint : public ValueConstraint { | ||
| /// be NULL. Otherwise, the buffer pointer must be non-null. This is useful | ||
| /// for functions like `fread` which have this special property. | ||
| class BufferNullnessConstraint : public ValueConstraint { | ||
| using ValueConstraint::ValueConstraint; | ||
| ArgNo SizeArg1N; | ||
| std::optional<ArgNo> SizeArg2N; | ||
| // This variable has a role when we negate the constraint. | ||
| bool CannotBeNull = true; | ||
|
|
||
| public: | ||
| NotNullBufferConstraint(ArgNo ArgN, ArgNo SizeArg1N, | ||
| BufferNullnessConstraint(ArgNo ArgN, ArgNo SizeArg1N, | ||
| std::optional<ArgNo> SizeArg2N, | ||
| bool CannotBeNull = true) | ||
| : ValueConstraint(ArgN), SizeArg1N(SizeArg1N), SizeArg2N(SizeArg2N), | ||
|
|
@@ -436,9 +436,9 @@ class StdLibraryFunctionsChecker | |
| llvm::raw_ostream &Out) const override; | ||
|
|
||
| ValueConstraintPtr negate() const override { | ||
| NotNullBufferConstraint Tmp(*this); | ||
| BufferNullnessConstraint Tmp(*this); | ||
| Tmp.CannotBeNull = !this->CannotBeNull; | ||
| return std::make_shared<NotNullBufferConstraint>(Tmp); | ||
| return std::make_shared<BufferNullnessConstraint>(Tmp); | ||
| } | ||
|
|
||
| protected: | ||
|
|
@@ -1151,7 +1151,7 @@ ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply( | |
| return State; | ||
| } | ||
|
|
||
| ProgramStateRef StdLibraryFunctionsChecker::NotNullConstraint::apply( | ||
| ProgramStateRef StdLibraryFunctionsChecker::NullnessConstraint::apply( | ||
| ProgramStateRef State, const CallEvent &Call, const Summary &Summary, | ||
| CheckerContext &C) const { | ||
| SVal V = getArgSVal(Call, getArgNo()); | ||
|
|
@@ -1165,7 +1165,7 @@ ProgramStateRef StdLibraryFunctionsChecker::NotNullConstraint::apply( | |
| return State->assume(L, CannotBeNull); | ||
| } | ||
|
|
||
| void StdLibraryFunctionsChecker::NotNullConstraint::describe( | ||
| void StdLibraryFunctionsChecker::NullnessConstraint::describe( | ||
| DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, | ||
| const Summary &Summary, llvm::raw_ostream &Out) const { | ||
| assert(CannotBeNull && | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The flag My understanding is that
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you check the comments of
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently the
Purpose of the assertion seems to be only to make a simplification (not implement
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @balazske Thanks for pointing out the places I should look at. I've learned more about them. Regarding the However, the assertions and the implementations of This suggests that I believe the document of But in practice, this function is called when reporting pre-condition violations, i.e., the constraint is not satisfied by the argument. Additionally, In conclusion, I suggest we make no changes except for the name of
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, I see. It's called on the negation of the violated constraint! Then the only remaining question I have is that do you think
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically it is not an error to use the describe functions on any constraint, the assertion indicates only a not implemented (and not used) case. We can remove the assertion and print out a text like "(describe not implemented yet)" as description. |
||
|
|
@@ -1176,15 +1176,15 @@ void StdLibraryFunctionsChecker::NotNullConstraint::describe( | |
| Out << "is not NULL"; | ||
| } | ||
|
|
||
| bool StdLibraryFunctionsChecker::NotNullConstraint::describeArgumentValue( | ||
| bool StdLibraryFunctionsChecker::NullnessConstraint::describeArgumentValue( | ||
| const CallEvent &Call, ProgramStateRef State, const Summary &Summary, | ||
| llvm::raw_ostream &Out) const { | ||
| assert(!CannotBeNull && "This function is used when the value is NULL"); | ||
| Out << "is NULL"; | ||
| return true; | ||
| } | ||
|
|
||
| ProgramStateRef StdLibraryFunctionsChecker::NotNullBufferConstraint::apply( | ||
| ProgramStateRef StdLibraryFunctionsChecker::BufferNullnessConstraint::apply( | ||
| ProgramStateRef State, const CallEvent &Call, const Summary &Summary, | ||
| CheckerContext &C) const { | ||
| SVal V = getArgSVal(Call, getArgNo()); | ||
|
|
@@ -1213,7 +1213,7 @@ ProgramStateRef StdLibraryFunctionsChecker::NotNullBufferConstraint::apply( | |
| return State->assume(L, CannotBeNull); | ||
| } | ||
|
|
||
| void StdLibraryFunctionsChecker::NotNullBufferConstraint::describe( | ||
| void StdLibraryFunctionsChecker::BufferNullnessConstraint::describe( | ||
| DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, | ||
| const Summary &Summary, llvm::raw_ostream &Out) const { | ||
| assert(CannotBeNull && | ||
|
|
@@ -1224,7 +1224,7 @@ void StdLibraryFunctionsChecker::NotNullBufferConstraint::describe( | |
| Out << "is not NULL"; | ||
| } | ||
|
|
||
| bool StdLibraryFunctionsChecker::NotNullBufferConstraint::describeArgumentValue( | ||
| bool StdLibraryFunctionsChecker::BufferNullnessConstraint::describeArgumentValue( | ||
| const CallEvent &Call, ProgramStateRef State, const Summary &Summary, | ||
| llvm::raw_ostream &Out) const { | ||
| assert(!CannotBeNull && "This function is used when the value is NULL"); | ||
|
|
@@ -1792,14 +1792,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( | |
| }; | ||
| auto LessThanOrEq = BO_LE; | ||
| auto NotNull = [&](ArgNo ArgN) { | ||
| return std::make_shared<NotNullConstraint>(ArgN); | ||
| return std::make_shared<NullnessConstraint>(ArgN); | ||
| }; | ||
| auto IsNull = [&](ArgNo ArgN) { | ||
| return std::make_shared<NotNullConstraint>(ArgN, false); | ||
| return std::make_shared<NullnessConstraint>(ArgN, false); | ||
| }; | ||
| auto NotNullBuffer = [&](ArgNo ArgN, ArgNo SizeArg1N, | ||
| std::optional<ArgNo> SizeArg2N = std::nullopt) { | ||
| return std::make_shared<NotNullBufferConstraint>(ArgN, SizeArg1N, | ||
| return std::make_shared<BufferNullnessConstraint>(ArgN, SizeArg1N, | ||
| SizeArg2N); | ||
| }; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The negation of the constraint does not fully match my expectation: this constraint is saying
size == 0 || buf != 0. So the negation is suppose to besize != 0 && buf == 0. The implementation looks more likesize == 0 || buf == 0to me.In general, how is the negation of a constraint used? I imagine that sometimes the engine needs to split the path by assuming 'C' and '!C' for a constraint 'C'? If the negation can be used as an assumption for a path, we should be serious about its correctness. Additionally,
describeanddescribeArgumentValueneed to report differently depending on whether the constraint is negated or not.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The negation of a constraint
Cis only used to determine if the constraint holds at a state. It checks that applyingCresults in a feasible state and applying!Cresults in a infeasible one.The current negation implementation of
NotNullBufferConstraintis kind of "weaker" than what it suppose to be. (Formally, the "weaker than" relation doesn't exist between them.) In other words, applying!Cmay not result in an infeasible state when it is suppose to happen. Consequently, the checker can miss the chance of determining a constraint holds at a state. But I suspect this is some rare corner case.My conclusion is to make no change but some comments about this. @balazske
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The purpose is to ignore the constraint of the buffer if
size == 0is true. If size is 0 the buffer can be a null or non-null pointer, so the constraint has no effect.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I somehow thought the checker passes a pre-condition
Conly whenState->assume(C) && !State->assume(!C))but the reality is it reports a violation only whenState->assume(!C) && !State->assume(C).Yes, you are right. This is fine.