llvm-project/clang/test/Analysis/uninit-vals.cpp
Balázs Benics 5fbf4117e3
[analyzer] Fix crash when copying uninitialized data in function named "swap" (#178923)
So the RegionStore has some assumptions, namely that the
core.unitialized.Assign checker is enabled and detects copying Undefined
(read of uninitialized data) before the Store is instructed to model
this.

As it turns out, there is a little hack in the
UndefinedAssignmentChecker:
```c++
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
                                           const Stmt *StoreE, bool AtDeclInit,
                                           CheckerContext &C) const {
  if (!val.isUndef())
    return;

  // Do not report assignments of uninitialized values inside swap functions.
  // This should allow to swap partially uninitialized structs
  if (const FunctionDecl *EnclosingFunctionDecl =
      dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
    if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
      return;
  // ...
```

This meant that no Sink node was inserted by the checker, thus the Store
would just go and try to fulfill the bind operation.

However, the Store also assumed that it's not going to see Undefined
vals, so that case wasn't handled, but simply cast the value to a
nonloc::CompoundVal.

The checker should have created the Sink node regardless if it wants to
emit a report or not.
In addition to this, I'm also hardedning the Store to also be able to
handle UndefinedVals a bit better.

The crash bisects to #118096, but that's only surfaced this issue.

Fixes #178797
2026-02-02 18:19:56 +00:00

54 lines
1.4 KiB
C++

// RUN: %clang_analyze_cc1 -analyzer-checker=core.builtin -verify -DCHECK_FOR_CRASH %s
// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify -analyzer-output=text %s
#ifdef CHECK_FOR_CRASH
// expected-no-diagnostics
#endif
namespace PerformTrivialCopyForUndefs {
struct A {
int x;
};
struct B {
A a;
};
struct C {
B b;
};
void foo() {
C c1;
C *c2;
#ifdef CHECK_FOR_CRASH
// If the value of variable is not defined and checkers that check undefined
// values are not enabled, performTrivialCopy should be able to handle the
// case with undefined values, too.
c1.b.a = c2->b.a;
#else
c1.b.a = c2->b.a; // expected-warning{{1st function call argument is an uninitialized value}}
// expected-note@-1{{1st function call argument is an uninitialized value}}
#endif
}
}
namespace gh_178797 {
struct SpecialBuffer {
SpecialBuffer() : src(defaultBuffer), dst(defaultBuffer) {}
int* src;
int* dst;
int defaultBuffer[2];
};
// Not really a swap, but we need an assignment assigning UndefinedVal
// within a "swap" function to trigger this behavior.
void swap(int& lhs, int& rhs) {
lhs = rhs; // no-crash
// Not reporting copying uninitialized data because that is explicitly suppressed in the checker.
}
void entry_point() {
SpecialBuffer special;
swap(*special.dst, *++special.src);
}
} // namespace gh_178797