Skip to content
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

[analyzer] Fix StdLibraryFunctionsChecker crash on surprising sink node #66109

Merged

Conversation

NagyDonat
Copy link
Contributor

Recent changes in StdLibraryFunctionsChecker introduced a situation where the checker sequentially performed two state transitions to add two separate note tags.

In the unlikely case when the updated state (the variable NewState) was posteriorly overconstrained, the engine marked the node after the first state transition as a sink to stop the "natural" graph exploration after that point.

However, in this particular case the checker tried to directly add a second node, and this triggered an assertion in the addPredecessor() method of ExplodedNode.

This commit introduces an explicit isSink() check to avoid this crash. To avoid similar bugs in the future, perhaps it would be possible to tweak addTransition() and ensure that it returns nullptr when it would return a sink node (to unify the two possible error conditions).

This crash was observed in an analysis of the curl project (in a very long and complex function), and there I validated that this is the root cause, but I don't have a self-contained testcase that can trigger the creation of a PosteriorlyOverconstrained node in this situation.

@NagyDonat NagyDonat requested a review from a team as a code owner September 12, 2023 16:41
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer labels Sep 12, 2023
@llvmbot
Copy link
Member

llvmbot commented Sep 12, 2023

@llvm/pr-subscribers-clang

Changes

Recent changes in StdLibraryFunctionsChecker introduced a situation where the checker sequentially performed two state transitions to add two separate note tags.

In the unlikely case when the updated state (the variable NewState) was posteriorly overconstrained, the engine marked the node after the first state transition as a sink to stop the "natural" graph exploration after that point.

However, in this particular case the checker tried to directly add a second node, and this triggered an assertion in the addPredecessor() method of ExplodedNode.

This commit introduces an explicit isSink() check to avoid this crash. To avoid similar bugs in the future, perhaps it would be possible to tweak addTransition() and ensure that it returns nullptr when it would return a sink node (to unify the two possible error conditions).

This crash was observed in an analysis of the curl project (in a very long and complex function), and there I validated that this is the root cause, but I don't have a self-contained testcase that can trigger the creation of a PosteriorlyOverconstrained node in this situation.

Full diff: https://github.com/llvm/llvm-project/pull/66109.diff

1 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp (+8-3)
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index f5f6e3a91237686..d3a41dd9aff2891 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -1387,8 +1387,8 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
     if (!NewState)
       continue;
 
-    // It is possible that NewState == State is true.
-    // It can occur if another checker has applied the state before us.
+    // Here it's possible that NewState == State, e.g. when other checkers
+    // already applied the same constraints (or stricter ones).
     // Still add these note tags, the other checker should add only its
     // specialized note tags. These general note tags are handled always by
     // StdLibraryFunctionsChecker.
@@ -1427,8 +1427,13 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
             });
         Pred = C.addTransition(NewState, Pred, Tag);
       }
-      if (!Pred)
+      if (!Pred || Pred->isSink()) {
+        // Pred may be:
+        //  - a nullpointer, when the generated node is not new;
+        //  - a sink, when NewState is posteriorly overconstrained.
+        // In these situations we cannot add the errno note tag.
         continue;
+      }
     }
 
     // If we can get a note tag for the errno change, add this additionally to

@llvmbot
Copy link
Member

llvmbot commented Sep 12, 2023

@llvm/pr-subscribers-clang-static-analyzer-1

Changes

Recent changes in StdLibraryFunctionsChecker introduced a situation where the checker sequentially performed two state transitions to add two separate note tags.

In the unlikely case when the updated state (the variable NewState) was posteriorly overconstrained, the engine marked the node after the first state transition as a sink to stop the "natural" graph exploration after that point.

However, in this particular case the checker tried to directly add a second node, and this triggered an assertion in the addPredecessor() method of ExplodedNode.

This commit introduces an explicit isSink() check to avoid this crash. To avoid similar bugs in the future, perhaps it would be possible to tweak addTransition() and ensure that it returns nullptr when it would return a sink node (to unify the two possible error conditions).

This crash was observed in an analysis of the curl project (in a very long and complex function), and there I validated that this is the root cause, but I don't have a self-contained testcase that can trigger the creation of a PosteriorlyOverconstrained node in this situation.

Full diff: https://github.com/llvm/llvm-project/pull/66109.diff

1 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp (+8-3)
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index f5f6e3a91237686..d3a41dd9aff2891 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -1387,8 +1387,8 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
     if (!NewState)
       continue;
 
-    // It is possible that NewState == State is true.
-    // It can occur if another checker has applied the state before us.
+    // Here it's possible that NewState == State, e.g. when other checkers
+    // already applied the same constraints (or stricter ones).
     // Still add these note tags, the other checker should add only its
     // specialized note tags. These general note tags are handled always by
     // StdLibraryFunctionsChecker.
@@ -1427,8 +1427,13 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
             });
         Pred = C.addTransition(NewState, Pred, Tag);
       }
-      if (!Pred)
+      if (!Pred || Pred->isSink()) {
+        // Pred may be:
+        //  - a nullpointer, when the generated node is not new;
+        //  - a sink, when NewState is posteriorly overconstrained.
+        // In these situations we cannot add the errno note tag.
         continue;
+      }
     }
 
     // If we can get a note tag for the errno change, add this additionally to

Recent changes in StdLibraryFunctionsChecker introduced a situation
where the checker sequentially performed two state transitions to add
two separate note tags.

In the unlikely case when the updated state (the variable `NewState`)
was posteriorly overconstrained, the engine marked the node after the
first state transition as a sink to stop the "natural" graph exploration
after that point.

However, in this particular case the checker tried to directly add a
second node, and this triggered an assertion in the `addPredecessor()`
method of `ExplodedNode`.

This commit introduces an explicit `isSink()` check to avoid this crash.
To avoid similar bugs in the future, perhaps it would be possible to
tweak `addTransition()` and ensure that it returns `nullptr` when it
would return a sink node (to unify the two possible error conditions).

This crash was observed in an analysis of the curl project (in a very
long and complex function), and there I validated that this is the root
cause, but I don't have a self-contained testcase that can trigger the
creation of a PosteriorlyOverconstrained node in this situation.
@NagyDonat NagyDonat force-pushed the StdCLibraryFunctions_fix_overconstrained_crash branch from 48deb9b to b5c4590 Compare September 13, 2023 09:32
@NagyDonat NagyDonat requested a review from steakhal September 13, 2023 12:45
Copy link
Contributor

@steakhal steakhal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it deserves a fragile test case.
I think the fix looks good.
I also agree with your other statements on the addTransition subject.

@NagyDonat NagyDonat merged commit 0b2778d into llvm:main Sep 14, 2023
kstoimenov pushed a commit to kstoimenov/llvm-project that referenced this pull request Sep 14, 2023
…de (llvm#66109)

Recent changes in StdLibraryFunctionsChecker introduced a situation
where the checker sequentially performed two state transitions to add
two separate note tags.

In the unlikely case when the updated state (the variable `NewState`)
was posteriorly overconstrained, the engine marked the node after the
first state transition as a sink to stop the "natural" graph exploration
after that point.

However, in this particular case the checker tried to directly add a
second node, and this triggered an assertion in the `addPredecessor()`
method of `ExplodedNode`.

This commit introduces an explicit `isSink()` check to avoid this crash.
To avoid similar bugs in the future, perhaps it would be possible to
tweak `addTransition()` and ensure that it returns `nullptr` when it
would return a sink node (to unify the two possible error conditions).

This crash was observed in an analysis of the curl project (in a very
long and complex function), and there I validated that this is the root
cause, but I don't have a self-contained testcase that can trigger the
creation of a PosteriorlyOverconstrained node in this situation.
ZijunZhaoCCK pushed a commit to ZijunZhaoCCK/llvm-project that referenced this pull request Sep 19, 2023
…de (llvm#66109)

Recent changes in StdLibraryFunctionsChecker introduced a situation
where the checker sequentially performed two state transitions to add
two separate note tags.

In the unlikely case when the updated state (the variable `NewState`)
was posteriorly overconstrained, the engine marked the node after the
first state transition as a sink to stop the "natural" graph exploration
after that point.

However, in this particular case the checker tried to directly add a
second node, and this triggered an assertion in the `addPredecessor()`
method of `ExplodedNode`.

This commit introduces an explicit `isSink()` check to avoid this crash.
To avoid similar bugs in the future, perhaps it would be possible to
tweak `addTransition()` and ensure that it returns `nullptr` when it
would return a sink node (to unify the two possible error conditions).

This crash was observed in an analysis of the curl project (in a very
long and complex function), and there I validated that this is the root
cause, but I don't have a self-contained testcase that can trigger the
creation of a PosteriorlyOverconstrained node in this situation.
Guzhu-AMD pushed a commit to GPUOpen-Drivers/llvm-project that referenced this pull request Sep 21, 2023
Local branch amd-gfx 737eb03 Merged main:602e47c5f9fd into amd-gfx:f337c8eadcf3
Remote branch main 0b2778d [analyzer] Fix StdLibraryFunctionsChecker crash on surprising sink node (llvm#66109)
@NagyDonat NagyDonat deleted the StdCLibraryFunctions_fix_overconstrained_crash branch October 12, 2023 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants