diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -449,6 +449,11 @@ ProgramStateRef stateTrue, stateFalse; + // Assume different address spaces cannot overlap. + if (First.Expression->getType()->getPointeeType().getAddressSpace() != + Second.Expression->getType()->getPointeeType().getAddressSpace()) + return state; + // Get the buffer values and make sure they're known locations. const LocationContext *LCtx = C.getLocationContext(); SVal firstVal = state->getSVal(First.Expression, LCtx); diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp --- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -715,11 +715,57 @@ llvm_unreachable("Fields not found in parent record's definition"); } +static uint64_t getLocBitwidth(ProgramStateRef State, Loc loc) { + uint64_t LocBitwidth = 0; + ASTContext &Ctx = State->getStateManager().getContext(); + + switch (loc.getSubKind()) { + default: + llvm_unreachable("Unhandled loc subkind in getLocBitwidth"); + break; + case loc::MemRegionValKind: { + const MemRegion *R = loc.castAs().getRegion(); + if (const auto *SR = dyn_cast(R)) { + QualType Ty = SR->getSymbol()->getType(); + if (!Ty.isNull()) + LocBitwidth = Ctx.getTypeSize(Ty); + } + } break; + case loc::ConcreteIntKind: { + SVal V = State->getSVal(loc); + QualType Ty = V.getType(Ctx); + if (!Ty.isNull()) + LocBitwidth = Ctx.getTypeSize(Ty); + } break; + case loc::GotoLabelKind: + break; + } + return LocBitwidth; +} + +static bool compareLocBitWidth(ProgramStateRef State, Loc RhsLoc, Loc LhsLoc) { + uint64_t RhsBitwidth = getLocBitwidth(State, RhsLoc); + uint64_t LhsBitwidth = getLocBitwidth(State, LhsLoc); + if (RhsBitwidth && LhsBitwidth) { + assert(RhsBitwidth == LhsBitwidth && + "RhsLoc bitwidth must be same as LhsLoc bitwidth!"); + return RhsBitwidth == LhsBitwidth; + } + return false; +} + // FIXME: all this logic will change if/when we have MemRegion::getLocation(). SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, Loc rhs, QualType resultTy) { + + // Assert that bitwidth of lhs and rhs are the same. + // This can happen if two different address spaces are used, + // and the bitwidths of the address spaces are different. + // See LIT case clang/test/Analysis/cstring-checker-addressspace.c + compareLocBitWidth(state, rhs, lhs); + // Only comparisons and subtractions are valid operations on two pointers. // See [C99 6.5.5 through 6.5.14] or [C++0x 5.6 through 5.15]. // However, if a pointer is casted to an integer, evalBinOpNN may end up diff --git a/clang/test/Analysis/cstring-checker-addressspace.c b/clang/test/Analysis/cstring-checker-addressspace.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/cstring-checker-addressspace.c @@ -0,0 +1,27 @@ +// RUN: %clang_analyze_cc1 -triple amdgcn-unknown-unknown \ +// RUN: -analyze -analyzer-checker=core,alpha.unix.cstring \ +// RUN: -analyze -analyzer-checker=debug.ExprInspection \ +// RUN: -analyzer-config crosscheck-with-z3=true -verify %s \ +// RUN: -Wno-incompatible-library-redeclaration +// REQUIRES: z3 + +void clang_analyzer_warnIfReached(); + +// From https://llvm.org/docs/AMDGPUUsage.html#address-spaces, +// select address space 3 (local), since the pointer size is +// different than Generic. +#define DEVICE __attribute__((address_space(3))) +_Static_assert(sizeof(int *) == 8, ""); +_Static_assert(sizeof(DEVICE int *) == 4, ""); + +DEVICE void *memcpy(DEVICE void *, const void *, unsigned long); + +// Copy from host to device memory. +DEVICE void *memcpy(DEVICE void *dst, const void *src, unsigned long len); + +void top(DEVICE int *dst, int *src, int len) { + memcpy(dst, src, len); + + // Create a bugreport for triggering Z3 refutation. + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} +}