This commit heavily refactors `out-of-bounds-constraint-check.c`:
1. The complex combinations of several `clang_analyzer_eval` calls were
replaced by `clang_analyzer_value`, which can directly query the range
of a symbol.
2. Testcases were renamed to a (hopefully) more consistent scheme.
3. The use of `size_t` as an argument type was replaced by `unsigned
long long`, which is usually a no-op, but seems to be a better choice if
I look for `64u` in the output of `clang_analyzer_value`.
4. The single "dynamic extent" case was generalized into a full set of
tests that use `malloc`.
5. Half of the testcases (the ones that don't use `malloc`) were changed
to use an `int[5]` array instead of a string literal. After this change
the tests in this file cover every functionality that was tested by the
testcases `test_assume_after_access{,2}` in the file `out-of-bounds.c`
so I was able to delete those two testcases (and therefore consolidate
the validation of these constraints within a single test file).
This is the second commit in a series that reorganizes the tests of
`security.ArrayBound` to system that's easier to understand and
maintain. (Note that this file wasn't significantly modified by the
recent commit 6e17ed9b04e5523cc910bf171c3122dcc64b86db which renamed
`alpha.security.ArrayBoundV2` to `security.ArrayBound`; but I still felt
that this cleanup may be useful.)
Multiple people reported flaky bot failures tied to
`clang/test/Analysis/live-stmts.cpp` I tried reproducing the flaky
behavior on my Linux x86_64 system, but the tests appears to be stable
in my context.
Only by looking at the failures reported, I could formulate a potential
diagnosis.
The output always looked almost the same, except that the Exprs dumped
per Basic block were shuffled compared to my expectation. This suggests
to me some ordering issue.
If you look at the backing storage of
`blocksEndToLiveness[B].liveExprs`,
it uses `llvm::ImmutableSet<const Expr *>`.
That container likely uses the pointer values as keys, thus the runtime
values of the addresses influence the iteration order.
To fix this, before dumping, I sort the expressions by their
"beginLocs". It should be efficient enough for a debug checker, where
there is no performance constraint.
This should hopefully fix the flaky behavior on systems where ASLR works
differently than (my) Linux system.
Hopefully fixes#126619
Hopefully fixes#126804
This commit adds the new analyzer option
`assume-at-least-one-iteration`, which is `false` by default, but can be
set to `true` to ensure that the analyzer always assumes at least one
iteration in loops.
In some situations this "loop is skipped" execution path is an important
corner case that may evade the notice of the developer and hide
significant bugs -- however, there are also many situations where it's
guaranteed that at least one iteration will happen (e.g. some data
structure is always nonempty), but the analyzer cannot realize this and
will produce false positives when it assumes that the loop is skipped.
This commit refactors some logic around the implementation of the new
feature, but the only functional change is introducing the new analyzer
option. If the new option is left in its default state (false), then the
analysis is functionally equivalent to an analysis done with a version
before this commit.
Allow operator T&() in a member function which returns a const member
variable.
In particular, this will allow UniqueRef::operator T&() and
Ref::operator T&() to be treated as a safe pointer origin when they're
called on a const member.
Before commit 6e17ed9b04e5523cc910bf171c3122dcc64b86db the test file
`no-outofbounds.c` tested the behavior of the old alpha checker
`alpha.security.ArrayBound` (V1); then that commit converted it into a
test for the checker `security.ArrayBound` which was previously called
`alpha.security.ArrayBoundV2`.
This commit removes this small separate test file and adds some tests to
the "native" test files of `security.ArrayBound` to ensure that the same
functionality is still tested.
This is intended as the first commit in a series that reorganizes the
tests of `security.ArrayBound` to system that's easier to understand and
maintain.
Reapplying changes from https://github.com/llvm/llvm-project/pull/125638
after buildbot failures.
Buildbot failures fixed in 029e7e98dc9956086adc6c1dfb0c655a273fbee6,
latest commit on this PR. It was a problem with a declared class member
with same name as its type. Sorry!
Fixes https://github.com/llvm/llvm-project/issues/123459.
Previously, when the StackAddrEscapeChecker checked return values, it
did not scan into the structure of the return SVal. Now it does, and we
can catch some more false negatives that were already mocked out in the
tests in addition to those mentioned in
https://github.com/llvm/llvm-project/issues/123459.
The warning message at the moment for these newly caught leaks is not
great. I think they would be better if they had a better trace of why
and how the region leaks. If y'all are happy with these changes, I would
try to improve these warnings and work on normalizing this SVal checking
on the `checkEndFunction` side of the checker also.
Two of the stack address leak test cases now have two warnings, one
warning from return expression checking and another from`
checkEndFunction` `iterBindings` checking. For these two cases, I prefer
the warnings from the return expression checking, but I couldn't figure
out a way to drop the `checkEndFunction` without breaking other
`checkEndFunction` warnings that we do want. Thoughts here?
The C++ standard prohibits this implicit destructor call, leading to
incorrect reports from clang-analyzer. This causes projects that use
std::option (including llvm) to fail the cplusplus.NewDelete test
incorrectly when run through the analyzer.
Fixes#119415
Before this commit, there were two alpha checkers that used different
algorithms/logic for detecting out of bounds memory access: the old
`alpha.security.ArrayBound` and the experimental, more complex
`alpha.security.ArrayBoundV2`.
After lots of quality improvement commits ArrayBoundV2 is now stable
enough to be moved out of the alpha stage. As indexing (and dereference)
are common operations, it still produces a significant amount of false
positives, but not much more than e.g. `core.NullDereference` or
`core.UndefinedBinaryOperatorResult`, so it should be acceptable as a
non-`core` checker.
At this point `alpha.security.ArrayBound` became obsolete (there is a
better tool for the same task), so I'm removing it from the codebase.
With this I can eliminate the ugly "V2" version mark almost everywhere
and rename `alpha.security.ArrayBoundV2` to `security.ArrayBound`.
(The version mark is preserved in the filename "ArrayBoundCheckerV2", to
ensure a clear git history. I'll rename it to "ArrayBoundChecker.cpp" in
a separate commit.)
This commit adapts the unit tests of `alpha.security.ArrayBound` to
testing the new `security.ArrayBound` (= old ArrayBoundV2). Currently
the names of the test files are very haphazard, I'll probably create a
separate followup commit that consolidates this.
From investigation of a few slow analysis cases, I discovered that
`RegionStoreManager::bind*` and `ExprEngine::removeDead` are often the
slowest actions. This change adds explicit scope to the time trace
generated by `-ftime-trace` to enable easy diagnostics of the cases when
these functions are the slowdown culprits.
--
CPP-6109
Specifically, add a scope for
- each work-list step,
- each entry point,
- each checker run within a step, and
- bug-suppression phase at the end of the analysis of an entry-point.
These scopes add no perceptible run-time overhead when time-tracing is
disabled. You can enable it and generate a time trace using the
`-ftime-trace=file.json` option.
See also the RFC:
https://discourse.llvm.org/t/analyzer-rfc-ftime-trace-time-scopes-for-steps-and-entry-points/84343
--
CPP-6065
When a callee is a method call (e.g. calling a lambda), we need to skip
the object pointer to match the parameter list with the call arguments.
This manifests as a bug that the checker erroneously generate a warning
for a lambda capture (L1) which is passed to a no-escape argument of
another lambda (L2).
This caused assertion failures:
clang/lib/Analysis/CFG.cpp:822:
void (anonymous namespace)::CFGBuilder::appendStmt(CFGBlock *, const Stmt *):
Assertion `!isa<Expr>(S) || cast<Expr>(S)->IgnoreParens() == S' failed.
See comment on the PR.
This reverts commit 44aa618ef67d302f5ab77cc591fb3434fe967a2e.
In `VisitObjCForCollectionStmt`, the function does `evalLocation` for
the current element at the original source state `Pred`. The evaluation
may result in a new state, say `PredNew`. I.e., there is a transition:
`Pred -> PredNew`, though it is a very rare case that `Pred` is NOT
identical to `PredNew`. (This explains why the bug exists for many years
but no one noticed until recently a crash observed downstream.) Later,
the original code does NOT use `PredNew` as the new source state in
`StmtNodeBuilder` for next transitions. In cases `Pred != PredNew`, the
program ill behaves.
(rdar://143280254)
If we see a variable declaration (aka. DeclStmt), and the VarRegion it
declared doesn't have Stack memspace, we assumed that it must be a local
static variable.
However, the declared variable may be an extern declaration of a global.
In this patch, let's admit that local extern declarations are a thing.
For the sake of completeness, I also added one more test for
thread_locals - which are implicitly considered statics btw. (the
`isStaticLocal()` correctly also considers thread locals as local
statics).
Fixes#124975
A DeclRefExpr could never refer to a Decomposition in valid C++ code,
but somehow the Analyzer creates these entities and then it tries to
print them.
There is no sensible answer here, so we print 'decomposition' followed
by the names of all of its bindings, separated by dashes.
During import of a function template at specific conditions an assertion
"TemplateOrSpecialization.isNull()" can be triggered. This can
happen when the new AST is already incompatible after import failures.
Problem is fixed by returning import failure at the assert condition.
In preparation of making `-Wreturn-type` default to an error (as there
is virtually no situation where you’d *want* to fall off the end of a
function that is supposed to return a value), this patch fixes tests
that have relied on this being only a warning, of which there seem
to be 3 kinds:
1. Tests which for no apparent reason have a function that triggers the
warning.
I suspect that a lot of these were on accident (or from before the
warning was introduced), since a lot of people will open issues w/ their
problematic code in the `main` function (which is the one case where you
don’t need to return from a non-void function, after all...), which
someone will then copy, possibly into a namespace, possibly renaming it,
the end result of that being that you end up w/ something that
definitely is not `main` anymore, but which still is declared as
returning `int`, and which still has no return statement (another reason
why I think this might apply to a lot of these is because usually the
actual return type of such problematic functions is quite literally
`int`).
A lot of these are really old tests that don’t use `-verify`, which is
why no-one noticed or had to care about the extra warning that was
already being emitted by them until now.
2. Tests which test either `-Wreturn-type`, `[[noreturn]]`, or what
codegen and sanitisers do whenever you do fall off the end of a
function.
3. Tests where I struggle to figure out what is even being tested
(usually because they’re Objective-C tests, and I don’t know
Objective-C), whether falling off the end of a function matters in the
first place, and tests where actually spelling out an expression to
return would be rather cumbersome (e.g. matrix types currently don’t
support list initialisation, so I can’t write e.g. `return {}`).
For tests that fall into categories 2 and 3, I just added
`-Wno-error=return-type` to the `RUN` lines and called it a day. This
was especially necessary for the former since `-Wreturn-type` is an
analysis-based warning, meaning that it is currently impossible to test
for more than one occurrence of it in the same compilation if it
defaults to an error since the analysis pass is skipped for subsequent
functions as soon as an error is emitted.
I’ve also added `-Werror=return-type` to a few tests that I had already
updated as this patch was previously already making the warning an error
by default, but we’ve decided to split that into two patches instead.
The checker currently reports beneath the null dereference dereferences
of undefined value and of label addresses. If we want to add more kinds
of invalid dereferences (or split the existing functionality) it is more
useful to make it separate checkers.
To make this possible the existing checker is split into a
DereferenceModeling part and a NullDereference checker that actually
only switches on the check of null dereference. This is similar
architecture as in MallocChecker and CStringChecker.
The change is almost NFC but a new (modeling) checker is added. If the
NullDereference checker is turned off the found invalid dereferences
will still stop the analysis without emitted warning (this is different
compared to the old behavior).
In my patch there, I left a test expectation stale. Tests with
`REQUIRES: Z3` never run because no bots check such configurations.
Here I'm adjusting the test expectations to meet reality.
If we have a refutation Z3 query timed out (UNDEF), allow a couple of
retries to improve stability of the query. By default allow 2 retries,
which will give us in maximum of 3 solve attempts per query.
Retries should help mitigating flaky Z3 queries.
See the details in the following RFC:
https://discourse.llvm.org/t/analyzer-rfc-retry-z3-crosscheck-queries-on-timeout/83711
Note that with each attempt, we spend more time per query.
Currently, we have a 15 seconds timeout per query - which are also in
effect for the retry attempts.
---
Why should this help?
In short, retrying queries should bring stability because if a query
runs long
it's more likely that it did so due to some runtime anomaly than it's on
the edge of succeeding. This is because most queries run quick, and the
queries that run long, usually run long by a fair amount.
Consequently, retries should improve the stability of the outcome of the
Z3 query.
In general, the retries shouldn't increase the overall analysis time
because it's really rare we hit the 0.1% of the cases when we would do
retries. But keep in mind that the retry attempts can add up if many
retries are allowed, or the individual query timeout is large.
CPP-5920
Generalize the SymbolIDs used for SymbolData to all SymExprs and use
these IDs for comparison SymbolRef keys in various containers, such as
ConstraintMap. These IDs are superior to raw pointer values because they
are more controllable and are not randomized across executions (unlike
[pointers](https://en.wikipedia.org/wiki/Address_space_layout_randomization)).
These IDs order is stable across runs because SymExprs are allocated in
the same order.
Stability of the constraint order is important for the stability of the
analyzer results. I evaluated this change on a set of 200+ open-source C
and C++ projects with the total number of ~78 000 symbolic-execution
issues passing Z3 refutation.
This patch reduced the run-to-run churn (flakiness) in SE issues from
80-90 to 30-40 (out of 78K) in our CSA deployment (in our setting flaky
issues are mostly due to Z3 refutation instability).
Note, most of the issue churn (flakiness) is caused by the mentioned Z3
refutation. With Z3 refutation disabled, issue churn goes down to ~10
issues out of 83K and this patch has no effect on appearing/disappearing
issues between runs. It however, seems to reduce the volatility of the
execution flow: before we had 40-80 issues with changed execution flow,
after - 10-30.
Importantly, this change is necessary for the next step in stabilizing
analysis results by caching Z3 query outcomes between analysis runs
(work in progress).
Across our admittedly noisy CI runs, I detected no significant effect on
memory footprint or analysis time.
This PR reapplies https://github.com/llvm/llvm-project/pull/121551 with
a fix to a g++ compiler error reported on some build bots
CPP-5919
Generalize the `SymbolID`s used for `SymbolData` to all `SymExpr`s and
use these IDs for comparison `SymbolRef` keys in various containers,
such as `ConstraintMap`. These IDs are superior to raw pointer values
because they are more controllable and are not randomized across
executions (unlike
[pointers](https://en.wikipedia.org/wiki/Address_space_layout_randomization)).
These IDs order is stable across runs because SymExprs are allocated in
the same order.
Stability of the constraint order is important for the stability of the
analyzer results. I evaluated this change on a set of 200+ open-source C
and C++ projects with the total number of ~78 000 symbolic-execution
issues passing Z3 refutation.
This patch reduced the run-to-run churn (flakiness) in SE issues from
80-90 to 30-40 (out of 78K) in our CSA deployment (in our setting flaky
issues are mostly due to Z3 refutation instability).
Note, most of the issue churn (flakiness) is caused by the mentioned Z3
refutation. With Z3 refutation disabled, issue churn goes down to ~10
issues out of 83K and this patch has no effect on appearing/disappearing
issues between runs. It however, seems to reduce the volatility of the
execution flow: before we had 40-80 issues with changed execution flow,
after - 10-30.
Importantly, this change is necessary for the next step in stabilizing
analysis results by caching Z3 query outcomes between analysis runs
(work in progress).
Across our admittedly noisy CI runs, I detected no significant effect on
memory footprint or analysis time.
CPP-5919
When a single #embed directive is used to initialize a char array, the
case is optimized via swap of EmbedExpr to underlying StringLiteral,
resulting in better performance in AST consumers.
While browsing through the code, I realized that
7122b70cfc8e23a069410215c363da76d842bda4
which changed type of EmbedExpr made the "fast path" unreachable. This
patch fixes this unfortunate situation.
This commit ensures that if the loop condition is opaque (the analyzer
cannot determine whether it's true or false) and there were at least two
iterations, then the analyzer doesn't make the unjustified assumption
that it can enter yet another iteration.
Note that the presence of a loop suggests that the developer thought
that two iterations can happen (otherwise an `if` would've been
sufficient), but it does not imply that the developer expected three or
four iterations -- and in fact there are many false positives where a
loop iterates over a two-element (or three-element) data structure, but
the analyzer cannot understand the loop condition and blindly assumes
that there may be three or more iterations. (In particular, analyzing
the FFMPEG project produces 100+ such false positives.)
Moreover, this provides some performance improvements in the sense that
the analyzer won't waste time on traversing the execution paths with 3
or 4 iterations in a loop (which are very similar to the paths with 2
iterations) and therefore will be able to traverse more branches
elsewhere on the `ExplodedGraph`.
This logic is disabled if the user enables the widen-loops analyzer
option (which is disabled by default), because the "simulate one final
iteration after the invalidation" execution path would be suppressed by
the "exit the loop if the loop condition is opaque and there were at
least two iterations" logic. If we want to support loop widening, we
would need to create a follow-up commit which ensures that it "plays
nicely" with this logic.
The current implementation of APInt extension in the code can trigger an
assertion failure when the `zext` function is called with a target width
smaller than the current bit width. For example:
```cpp
if (InitNum.getBitWidth() != BoundNum.getBitWidth()) {
InitNum = InitNum.zext(BoundNum.getBitWidth());
BoundNum = BoundNum.zext(InitNum.getBitWidth());
}
```
This logic does not guarantee that the `zext` target width is always
greater than or equal to the current bit width, leading to potential
crashes.
Expected Behavior:
- Ensure InitNum and BoundNum are extended to the maximum of their respective widths.
- Prevent assertion failures by enforcing correct `zext` usage.
Fixes#121201
In WebKit, we often capture this as Ref or RefPtr in addition to this
itself so that the object lives as long as a capturing lambda stays
alive.
Detect this pattern and treat it as safe. This PR also makes the check
for a lambda being passed as a function argument more robust by handling
CXXBindTemporaryExpr, CXXConstructExpr, and DeclRefExpr referring to the
lambda.
adoptRef in WebKit constructs Ref/RefPtr so treat it as such in
isCtorOfRefCounted.
Also removed the support for makeRef and makeRefPtr as they don't exist
any more.
adoptRef in WebKit constructs Ref/RefPtr so treat it as such in
isCtorOfRefCounted. Also removed the support for makeRef and makeRefPtr
as they don't exist any more.
Resolves#100762
Gist of the change:
1. All the symbol analysis, constraint manager and expression parsing
logic was already present, but the previous code didn't "visit" the
expressions within `assume()` by parsing those expressions, all of the
code "just works" by evaluating the SVals, and hence leaning on the same
logic that makes the code with `__builtin_assume` work
2. "Ignore" an expression from adding in CFG if it has side-effects (
similar to CGStmt.cpp (todo add link))
3. Add additional test case for ternary operator handling and modify
CFG.cpp's VisitGuardedExpr code for `continue`-ing if the `ProgramPoint`
is a `StmtPoint`
---------
Co-authored-by: Balazs Benics <benicsbalazs@gmail.com>
Discussion here:
https://discourse.llvm.org/t/analyzer-rfc-taming-z3-query-times/79520/15?u=szelethus
The original patch, #97298 introduced new timeouts backed by thorough
testing and measurements to keep the running time of Z3 within
reasonable limits. The measurements also showed that only certain
reports and certain TUs were responsible for the poor performance of Z3
refutation.
Unfortunately, it seems like that on machines with different
characteristics (slower machines) the current timeouts don't just axe
0.01% of reports, but many more as well. Considering that timeouts are
inherently nondeterministic as a cutoff point, this lead reports sets
being vastly different on the same projects with the same configuration.
The discussion link shows that all configurations introduced in the
patch with their default values lead to severa nondeterminism of the
analyzer. As we, and others use the analyzer as a gating tool for PRs,
we should revert to the original defaults.
We should respect that
* There are still parts of the analyzer that are either proven or
suspected to contain nondeterministic code (like pointer sets),
* A 15s timeout is more likely to hit the same reports every time on a
wider range of machines, but is still inherently nondeterministic, but
an infinite timeout leads to the tool hanging,
* If you measure the performance of the analyzer on your machines, you
can and should achieve some speedup with little or no observable
nondeterminism.
---------
Co-authored-by: Balazs Benics <benicsbalazs@gmail.com>
In WebKit, we often write Foo::ensureBar function which lazily
initializes m_bar and returns a raw pointer or a raw reference to m_bar.
Such a return value is safe to use for the duration of a member function
call in Foo so long as m_bar is const so that it never gets unset or
updated with a new value once it's initialized.
This PR adds support for recognizing these types of functions and
treating its return value as a safe origin of a function argument
(including "this") or a local variable.
It's safe to have a raw pointer or a raw reference to a singleton
object. Explicitly allow this in UncountedLocalVarsChecker and
UncheckedLocalVarsChecker.
This PR introduces a new checker
`[alpha.webkit.MemoryUnsafeCastChecker]` that warns all downcasts from a base type to a derived type.
rdar://137766829
Currently, we support `-wdeprecated-array-compare` for C++20 or above
and don't report any warning for older versions, this PR supports
`-Warray-compare` for older versions and for GCC compatibility.
Fixes#114770
This change removes the alpha.core.IdenticalExpr static analysis checker
since it's checks are present in the clang-tidy checks
misc-redundant-expression and bugprone-branch-clone. This check was
implemented as a static analysis check using AST matching, and since
alpha and duplicated in 2 clang-tidy checks may be removed.
Co-authored-by: Vince Bridgers <vince.a.bridgers@ericsson.com>
Like in the test case:
```c++
struct String {
String(const String &) {}
};
struct MatchComponent {
unsigned numbers[2];
String prerelease;
MatchComponent(MatchComponent const &) = default;
};
MatchComponent get();
void consume(MatchComponent const &);
MatchComponent parseMatchComponent() {
MatchComponent component = get();
component.numbers[0] = 10;
component.numbers[1] = 20;
return component; // We should have no stack addr escape warning here.
}
void top() {
consume(parseMatchComponent());
}
```
When calling `consume(parseMatchComponent())` the
`parseMatchComponent()` would return a copy of a temporary of
`component`. That copy would invoke the
`MatchComponent::MatchComponent(const MatchComponent &)` ctor.
That ctor would have a (reference typed) ParamVarRegion, holding the
location (lvalue) of the object we are about to copy (&component). So
far so good, but just before evaluating the binding operation for
initializing the `numbers` field of the temporary, we evaluate the
ArrayInitLoopExpr representing the by-value elementwise copy of the
array `component.numbers`. This is represented by a LazyCompoundVal,
because we (usually) don't just copy large arrays and bind many
individual direct bindings. Rather, we take a snapshot by using a LCV.
However, notice that the LCV representing this copy would look like
this:
lazyCompoundVal{ParamVarRegion{"reference param of cctor"}.numbers}
Notice that it refers to the numbers field of a reference. It would be
much better to desugar the reference to the actual object, thus it
should be: `lazyCompoundVal{component.numbers}`
Actually, when binding the result of the ArrayInitLoopExpr to the
`temp_object.numbers` in the compiler-generated member initializer of
the cctor, we should have two individual direct bindings because this is
a "small array":
```
binding &Element{temp_object.numbers, 0} <- loadFrom(&Element{component.numbers, 0})
binding &Element{temp_object.numbers, 1} <- loadFrom(&Element{component.numbers, 1})
```
Where `loadFrom(...)` would be:
```
loadFrom(&Element{component.numbers, 0}): 10 U32b
loadFrom(&Element{component.numbers, 1}): 20 U32b
```
So the store should look like this, after PostInitializer of
`temp_object.numbers`:
```
temp_object at offset 0: 10 U32b
temp_object at offset 32: 20 U32b
```
The lesson is that it's okay to have TypedValueRegions of references as
long as we don't form subregions of those. If we ever want to refer to a
subregion of a "reference" we actually meant to "desugar" the reference
and slice a subregion of the pointee of the reference instead.
Once this canonicalization is in place, we can also drop the special
handling of references in `ProcessInitializer`, because now reference
TypedValueRegions are eagerly desugared into their referee region when
forming a subregion of it.
There should be no practical differences, but there are of course bugs
that this patch may surface.