[analyzer] Fix ValistChecker false-positive involving symbolic pointers

In the following example:

  int va_list_get_int(va_list *va) {
    return va_arg(*va, int); // FP
  }

The `*va` expression will be something like `Element{SymRegion{va}, 0, va_list}`.
We use `ElementRegions` for representing the result of the dereference.
In this case, the `IsSymbolic` was set to `false` in the
`getVAListAsRegion()`.

Hence, before checking if the memregion is a SymRegion, we should take
the base of that region.

Analogously to the previous example, one can craft other cases:

  struct MyVaList {
    va_list l;
  };
  int va_list_get_int(struct MyVaList va) {
    return va_arg(va.l, int); // FP
  }

But it would also work if the `va_list` would be in the base or derived
part of a class. `ObjCIvarRegions` are likely also susceptible.
I'm not explicitly demonstrating these cases.

PS: Check the `MemRegion::getBaseRegion()` definition.

Fixes 

Reviewed By: xazax.hun

Differential Revision: https://reviews.llvm.org/D124239
This commit is contained in:
Balazs Benics 2022-04-26 08:49:05 +02:00
parent 170a903144
commit be744da01f
2 changed files with 14 additions and 5 deletions
clang
lib/StaticAnalyzer/Checkers
test/Analysis

View File

@ -178,7 +178,7 @@ const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E,
if (isa<ParmVarDecl>(DeclReg->getDecl()))
Reg = C.getState()->getSVal(SV.castAs<Loc>()).getAsRegion();
}
IsSymbolic = Reg && Reg->getAs<SymbolicRegion>();
IsSymbolic = Reg && Reg->getBaseRegion()->getAs<SymbolicRegion>();
// Some VarRegion based VA lists reach here as ElementRegions.
const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg);
return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg;

View File

@ -16,11 +16,20 @@ void call_inlined_uses_arg(int fst, ...) {
void f6(va_list *fst, ...) {
va_start(*fst, fst);
// FIXME: There should be no warning for this.
(void)va_arg(*fst, int); // expected-warning{{va_arg() is called on an uninitialized va_list}}
// expected-note@-1{{va_arg() is called on an uninitialized va_list}}
(void)va_arg(*fst, int);
va_end(*fst);
}
}
int va_list_get_int(va_list *va) {
return va_arg(*va, int); // no-warning
}
struct MyVaList {
va_list l;
};
int va_list_get_int2(struct MyVaList *va) {
return va_arg(va->l, int); // no-warning
}
void call_vprintf_bad(int isstring, ...) {
va_list va;