Author: DonĂ¡t Nagy Date: 2026-03-20T13:15:24+01:00 New Revision: a6a34333a113c4e648d115b010317ae89646e7c4
URL: https://github.com/llvm/llvm-project/commit/a6a34333a113c4e648d115b010317ae89646e7c4 DIFF: https://github.com/llvm/llvm-project/commit/a6a34333a113c4e648d115b010317ae89646e7c4.diff LOG: [analyzer] Don't rule out symbolic pointer pointing to stack (#187080) Ensure that the analyzer doesn't rule out the equality (or guarantee disequality) of a pointer to the stack and a symbolic pointer in unknown space. Previously the analyzer incorrectly assumed that stack pointers cannot be equal to symbolic pointers in unknown space. It is true that functions cannot validly return pointers to their own stack frame, but they can easily return a pointer to some other stack frame (e.g. a function can return a pointer recieved as an argument). The old behavior was introduced intentionally in 2012 by commit 3563fde6a02c2a75d0b4ba629d80c5511056a688, but it causes incorrect analysis, e.g. it prevents the correct handling of some testcases from the Juliet suite because it rules out the "fgets succeeds" branch. Reported-by: Daniel Krupp <[email protected]> Added: clang/test/Analysis/allow-equality-of-stack-and-symbolic-ptr.c Modified: clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp clang/test/Analysis/ptr-arith.c Removed: ################################################################################ diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 6108931f737d4..53ceabc621c19 100644 --- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -952,12 +952,8 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, const MemSpaceRegion *RightMS = RightBase->getMemorySpace(state); const MemSpaceRegion *UnknownMS = MemMgr.getUnknownRegion(); - // If the two regions are from diff erent known memory spaces they cannot be - // equal. Also, assume that no symbolic region (whose memory space is - // unknown) is on the stack. - if (LeftMS != RightMS && - ((LeftMS != UnknownMS && RightMS != UnknownMS) || - (isa<StackSpaceRegion>(LeftMS) || isa<StackSpaceRegion>(RightMS)))) { + // Two regions from diff erent known memory spaces cannot be equal. + if (LeftMS != RightMS && LeftMS != UnknownMS && RightMS != UnknownMS) { switch (op) { default: return UnknownVal(); diff --git a/clang/test/Analysis/allow-equality-of-stack-and-symbolic-ptr.c b/clang/test/Analysis/allow-equality-of-stack-and-symbolic-ptr.c new file mode 100644 index 0000000000000..964c860e460b2 --- /dev/null +++ b/clang/test/Analysis/allow-equality-of-stack-and-symbolic-ptr.c @@ -0,0 +1,61 @@ +// RUN: %clang_analyze_cc1 %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=unix.StdCLibraryFunctions \ +// RUN: -analyzer-checker=debug.ExprInspection \ +// RUN: -verify + +#include "Inputs/std-c-library-functions-POSIX.h" +#define NULL ((void*)0) + +void clang_analyzer_eval(int); +void clang_analyzer_warnIfReached(); + + +// An opaque function that returns a symbolic pointer in unknown space. +int *opaque_function(int *p); + +void test_simple(void) { + // Validate that the analyzer doesn't rule out the equality (or disequality) + // of a pointer to the stack ('&x') and a symbolic pointer in unknown space. + // Previously the analyzer incorrectly assumed that stack pointers cannot be + // equal to symbolic pointers, which is obviously nonsense. It is true that + // functions cannot validly return pointers to their own stack frame, but + // they can easily return a pointer to some other stack frame (e.g. in this + // example 'opaque_function' could return its argument). + int x = 0; + if (&x == opaque_function(&x)) { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + } else { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + } +} + +void test_local_not_leaked(void) { + // In this situation a very smart analyzer could theoretically deduce that + // the address of the local 'x' cannot leak from this function, so the + // call to 'opaque_function' cannot return it. + int x = 0; + if (&x == opaque_function(NULL)) { + // This branch is unreachable (without non-standard-compliant tricks); + // however, we cannot blame the analyzer for not deducing this. + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + } else { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + } +} + +void test_fgets_can_succeed(FILE *infile) { + // The modeling of 'fgets' in StdCLibraryFunctions splits two branches: one + // where the return value is assumed to be equal to the first argument, and + // one where the return value is assumed to be null. However, if the target + // buffer was on the stack, then 'evalBinOp' rejected the possibility that + // the return value (a symbolic pointer) can be equal to the first argument + // (a pointer to the stack), so the analyzer was unable to enter the + // "success" branch. + char buffer[100]; + if (fgets(buffer, 100, infile) != NULL) { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + } else { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + } +} diff --git a/clang/test/Analysis/ptr-arith.c b/clang/test/Analysis/ptr-arith.c index 51a13b8cfb723..2855e1761e673 100644 --- a/clang/test/Analysis/ptr-arith.c +++ b/clang/test/Analysis/ptr-arith.c @@ -146,7 +146,7 @@ void mixed_region_types(void) { void symbolic_region(int *p) { int a; - clang_analyzer_eval(&a != p); // expected-warning{{TRUE}} + clang_analyzer_eval(&a != p); // expected-warning{{UNKNOWN}} clang_analyzer_eval(&a > p); // expected-warning{{UNKNOWN}} clang_analyzer_eval(&a >= p); // expected-warning{{UNKNOWN}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
