diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -47,6 +47,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerIntPair.h" @@ -466,6 +467,42 @@ llvm_unreachable("unknown callable kind"); } +static bool isTransparentUnion(QualType T) { + const RecordType *UT = T->getAsUnionType(); + return UT && UT->getDecl()->hasAttr(); +} + +// In some cases, symbolic cases should be transformed before we associate +// them with parameters. This function incapsulates such cases. +static SVal processArgument(SVal Value, const Expr *ArgumentExpr, + const ParmVarDecl *Parameter, SValBuilder &SVB) { + QualType ParamType = Parameter->getType(); + QualType ArgumentType = ArgumentExpr->getType(); + + // Transparent unions allow users to easily convert values of union field + // types into union-typed objects. + // + // Also, more importantly, they allow users to define functions with different + // different parameter types, substituting types matching transparent union + // field types with the union type itself. + // + // Here, we check specifically for latter cases and prevent binding + // field-typed values to union-typed regions. + if (isTransparentUnion(ParamType) && + // Let's check that we indeed trying to bind different types. + !isTransparentUnion(ArgumentType)) { + BasicValueFactory &BVF = SVB.getBasicValueFactory(); + + llvm::ImmutableList CompoundSVals = BVF.getEmptySValList(); + CompoundSVals = BVF.prependSVal(Value, CompoundSVals); + + // Wrap it with compound value. + return SVB.makeCompoundVal(ParamType, CompoundSVals); + } + + return Value; +} + static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx, CallEvent::BindingsTy &Bindings, SValBuilder &SVB, @@ -490,10 +527,12 @@ // determined in compile-time but not represented as arg-expressions, // which makes getArgSVal() fail and return UnknownVal. SVal ArgVal = Call.getArgSVal(Idx); + const Expr *ArgExpr = Call.getArgExpr(Idx); if (!ArgVal.isUnknown()) { Loc ParamLoc = SVB.makeLoc( MRMgr.getParamVarRegion(Call.getOriginExpr(), Idx, CalleeCtx)); - Bindings.push_back(std::make_pair(ParamLoc, ArgVal)); + Bindings.push_back( + std::make_pair(ParamLoc, processArgument(ArgVal, ArgExpr, *I, SVB))); } } diff --git a/clang/test/Analysis/transparent_union_bug.c b/clang/test/Analysis/transparent_union_bug.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/transparent_union_bug.c @@ -0,0 +1,42 @@ +// RUN: %clang_analyze_cc1 -analyze -triple x86_64-apple-darwin10 \ +// RUN: -analyzer-checker=core,debug.ExprInspection -verify %s + +void clang_analyzer_warnIfReached(); +void clang_analyzer_printState(); + +typedef struct { + int value; +} Struct; + +typedef union { + Struct *ptr; + long num; +} __attribute__((transparent_union)) Alias; + +void foo(Struct *x); +void foo(Alias y) { + if (y.ptr == 0) { + // no-crash + } + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} +void foobar(long z); +void foobar(Alias z) { + clang_analyzer_printState(); + if (z.num != 42) { + // no-crash + } + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void foobaz(Alias x) { + if (x.ptr == 0) { + // no-crash + } + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} +void bar(Struct arg) { + foo(&arg); + foobar(42); + foobaz(&arg); +}