diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1626,11 +1626,56 @@ return Result; } +static SVal getSValAsConcreteInt(SValBuilder &SVB, SVal V, QualType baseT, + QualType elemT, + const RegionRawOffset &ORegionRawOffs) { + ASTContext &Ctx = SVB.getContext(); + + const llvm::APSInt *ConcreteValue = [&V](SVal Val) { + if (auto Int = V.getAs()) + return &Int->getValue(); + return &V.getAs()->getValue(); + }(V); + assert(ConcreteValue); + + const unsigned bitPosition = [&]() { + unsigned bitPos; + if (Ctx.getTargetInfo().isBigEndian()) + bitPos = (Ctx.getTypeSizeInChars(baseT).getQuantity() - + Ctx.getTypeSizeInChars(elemT).getQuantity()) - + ORegionRawOffs.getOffset().getQuantity(); + else + bitPos = ORegionRawOffs.getOffset().getQuantity(); + return bitPos * Ctx.getCharWidth(); + }(); + const unsigned numBits = + Ctx.getCharWidth() * Ctx.getTypeSizeInChars(elemT).getQuantity(); + if (bitPosition < ConcreteValue->getBitWidth() && + (numBits + bitPosition) <= ConcreteValue->getBitWidth()) { + llvm::APInt bits = ConcreteValue->extractBits(numBits, bitPosition); + return SVB.makeIntVal(bits, true); + } + return UnknownVal(); +} + SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const ElementRegion* R) { // Check if the region has a binding. - if (const Optional &V = B.getDirectBinding(R)) + if (const Optional &V = B.getDirectBinding(R)) { + if (V->isConstant()) { + const RegionRawOffset &O = R->getAsArrayOffset(); + if (const TypedValueRegion *baseR = + dyn_cast_or_null(O.getRegion())) { + QualType baseT = baseR->getValueType(); + if (baseT->isScalarType()) { + QualType elemT = R->getElementType(); + if (elemT->isScalarType()) + return getSValAsConcreteInt(svalBuilder, *V, baseT, elemT, O); + } + } + } return *V; + } const MemRegion* superR = R->getSuperRegion(); @@ -1721,6 +1766,10 @@ if (V->isUnknownOrUndef()) return *V; + + if (V->isConstant()) + return getSValAsConcreteInt(svalBuilder, *V, baseT, elemT, O); + // Other cases: give up. We are indexing into a larger object // that has some value, but we don't know how to handle that yet. return UnknownVal(); diff --git a/clang/test/Analysis/concrete-endian.cpp b/clang/test/Analysis/concrete-endian.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/concrete-endian.cpp @@ -0,0 +1,201 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -triple x86_64-unknown-linux-gnu -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -triple powerpc64-unknown-linux-gnu -verify %s + +void clang_analyzer_dump(int); +void clang_analyzer_eval(int); + +int testLocConcreteInts() { + static_assert(sizeof(char) == 1, "test assumes sizeof(char) is 1"); + static_assert(sizeof(short) == 2, "test assumes sizeof(short) is 2"); + static_assert(sizeof(int) == 4, "test assumes sizeof(int) is 4"); + static_assert(sizeof(long) == 8, "test assumes sizeof(long) is 8"); + static_assert(sizeof(long *) == 8, "test assumes sizeof(long *) is 8"); + long *p = (long *)0x11223344AA998877ULL; + unsigned char *ppb = (unsigned char *)&p; + unsigned short *pps = (unsigned short *)&p; + unsigned int *ppi = (unsigned int *)&p; + +#if defined(__LITTLE_ENDIAN__) + clang_analyzer_eval(ppb[0] == 0x77); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[1] == 0x88); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[2] == 0x99); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[3] == 0xaa); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[4] == 0x44); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[5] == 0x33); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[6] == 0x22); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[7] == 0x11); // expected-warning{{TRUE}} +#elif defined(__BIG_ENDIAN__) + clang_analyzer_eval(ppb[7] == 0x77); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[6] == 0x88); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[5] == 0x99); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[4] == 0xaa); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[3] == 0x44); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[2] == 0x33); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[1] == 0x22); // expected-warning{{TRUE}} + clang_analyzer_eval(ppb[0] == 0x11); // expected-warning{{TRUE}} +#else +#error "Don't recognize the endianess of this target!" +#endif + // Array out of bounds should yield UNKNOWN + clang_analyzer_eval(ppb[8]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(ppb[-1]); // expected-warning{{UNKNOWN}} + +#if defined(__LITTLE_ENDIAN__) + clang_analyzer_eval(pps[0] == 0x8877); // expected-warning{{TRUE}} + clang_analyzer_eval(pps[1] == 0xaa99); // expected-warning{{TRUE}} + clang_analyzer_eval(pps[2] == 0x3344); // expected-warning{{TRUE}} + clang_analyzer_eval(pps[3] == 0x1122); // expected-warning{{TRUE}} +#elif defined(__BIG_ENDIAN__) + clang_analyzer_eval(pps[3] == 0x8877); // expected-warning{{TRUE}} + clang_analyzer_eval(pps[2] == 0xaa99); // expected-warning{{TRUE}} + clang_analyzer_eval(pps[1] == 0x3344); // expected-warning{{TRUE}} + clang_analyzer_eval(pps[0] == 0x1122); // expected-warning{{TRUE}} +#else +#error "Don't recognize the endianess of this target!" +#endif + // Array out of bounds should yield UNKNOWN + clang_analyzer_eval(pps[4]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(pps[-1]); // expected-warning{{UNKNOWN}} + +#if defined(__LITTLE_ENDIAN__) + clang_analyzer_eval(ppi[0] == 0xaa998877); // expected-warning{{TRUE}} + clang_analyzer_eval(ppi[1] == 0x11223344); // expected-warning{{TRUE}} +#elif defined(__BIG_ENDIAN__) + clang_analyzer_eval(ppi[0] == 0x11223344); // expected-warning{{TRUE}} + clang_analyzer_eval(ppi[1] == 0xaa998877); // expected-warning{{TRUE}} +#else +#error "Don't recognize the endianess of this target!" +#endif + // Array out of bounds should yield UNKNOWN + clang_analyzer_eval(ppi[2]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(ppi[-1]); // expected-warning{{UNKNOWN}} + + return 0; +} + +int testNonlocConcreteInts() { + static_assert(sizeof(char) == 1, "test assumes sizeof(char) is 1"); + static_assert(sizeof(short) == 2, "test assumes sizeof(short) is 2"); + static_assert(sizeof(int) == 4, "test assumes sizeof(int) is 4"); + static_assert(sizeof(long) == 8, "test assumes sizeof(long) is 8"); + static_assert(sizeof(long *) == 8, "test assumes sizeof(long *) is 8"); + + unsigned short sh = 0x1122; + unsigned char *pbsh = (unsigned char *)&sh; + + unsigned int i = 0x11223344; + unsigned char *pbi = (unsigned char *)&i; + unsigned short *psi = (unsigned short *)&i; + + unsigned long ll = 0x11223344AA998877ULL; + unsigned char *pbll = (unsigned char *)≪ + unsigned short *psll = (unsigned short *)≪ + unsigned int *pill = (unsigned int *)≪ + + // Uses built macro to determine endianess. + // Use "clang -dM -E -x c /dev/null" to dump macros. + // This test uses __LITTLE_ENDIAN__ and __BIG_ENDIAN__ + + // First, the short endianess test section +#if defined(__LITTLE_ENDIAN__) + clang_analyzer_eval(pbsh[0] == 0x22); // expected-warning{{TRUE}} + clang_analyzer_eval(pbsh[1] == 0x11); // expected-warning{{TRUE}} +#elif defined(__BIG_ENDIAN__) + clang_analyzer_eval(pbsh[0] == 0x11); // expected-warning{{TRUE}} + clang_analyzer_eval(pbsh[1] == 0x22); // expected-warning{{TRUE}} +#else +#error "Don't recognize the endianess of this target!" +#endif + // Array out of bounds should yield UNKNOWN + clang_analyzer_eval(pbsh[2]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(pbsh[-1]); // expected-warning{{UNKNOWN}} + + // The "int" endianess test section +#if defined(__LITTLE_ENDIAN__) + clang_analyzer_eval(pbi[0] == 0x44); // expected-warning{{TRUE}} + clang_analyzer_eval(pbi[1] == 0x33); // expected-warning{{TRUE}} + clang_analyzer_eval(pbi[2] == 0x22); // expected-warning{{TRUE}} + clang_analyzer_eval(pbi[3] == 0x11); // expected-warning{{TRUE}} +#elif defined(__BIG_ENDIAN__) + clang_analyzer_eval(pbi[3] == 0x44); // expected-warning{{TRUE}} + clang_analyzer_eval(pbi[2] == 0x33); // expected-warning{{TRUE}} + clang_analyzer_eval(pbi[1] == 0x22); // expected-warning{{TRUE}} + clang_analyzer_eval(pbi[0] == 0x11); // expected-warning{{TRUE}} +#else +#error "Don't recognize the endianess of this target!" +#endif + // Array out of bounds should yield UNKNOWN + clang_analyzer_eval(pbi[4]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(pbi[-1]); // expected-warning{{UNKNOWN}} + +#if defined(__LITTLE_ENDIAN__) + clang_analyzer_eval(psi[0] == 0x3344); // expected-warning{{TRUE}} + clang_analyzer_eval(psi[1] == 0x1122); // expected-warning{{TRUE}} +#elif defined(__BIG_ENDIAN__) + clang_analyzer_eval(psi[1] == 0x3344); // expected-warning{{TRUE}} + clang_analyzer_eval(psi[0] == 0x1122); // expected-warning{{TRUE}} +#else +#error "Don't recognize the endianess of this target!" +#endif + // Array out of bounds should yield UNKNOWN + clang_analyzer_eval(psi[2]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(psi[-1]); // expected-warning{{UNKNOWN}} + + // The "long" endianess test section +#if defined(__LITTLE_ENDIAN__) + clang_analyzer_eval(pbll[0] == 0x77); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[1] == 0x88); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[2] == 0x99); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[3] == 0xaa); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[4] == 0x44); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[5] == 0x33); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[6] == 0x22); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[7] == 0x11); // expected-warning{{TRUE}} +#elif defined(__BIG_ENDIAN__) + clang_analyzer_eval(pbll[7] == 0x77); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[6] == 0x88); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[5] == 0x99); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[4] == 0xaa); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[3] == 0x44); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[2] == 0x33); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[1] == 0x22); // expected-warning{{TRUE}} + clang_analyzer_eval(pbll[0] == 0x11); // expected-warning{{TRUE}} +#else +#error "Don't recognize the endianess of this target!" +#endif + // Array out of bounds should yield UNKNOWN + clang_analyzer_eval(pbll[8]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(pbll[-1]); // expected-warning{{UNKNOWN}} + +#if defined(__LITTLE_ENDIAN__) + clang_analyzer_eval(psll[0] == 0x8877); // expected-warning{{TRUE}} + clang_analyzer_eval(psll[1] == 0xaa99); // expected-warning{{TRUE}} + clang_analyzer_eval(psll[2] == 0x3344); // expected-warning{{TRUE}} + clang_analyzer_eval(psll[3] == 0x1122); // expected-warning{{TRUE}} +#elif defined(__BIG_ENDIAN__) + clang_analyzer_eval(psll[3] == 0x8877); // expected-warning{{TRUE}} + clang_analyzer_eval(psll[2] == 0xaa99); // expected-warning{{TRUE}} + clang_analyzer_eval(psll[1] == 0x3344); // expected-warning{{TRUE}} + clang_analyzer_eval(psll[0] == 0x1122); // expected-warning{{TRUE}} +#else +#error "Don't recognize the endianess of this target!" +#endif + // Array out of bounds should yield UNKNOWN + clang_analyzer_eval(psll[4]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(psll[-1]); // expected-warning{{UNKNOWN}} + +#if defined(__LITTLE_ENDIAN__) + clang_analyzer_eval(pill[0] == 0xaa998877); // expected-warning{{TRUE}} + clang_analyzer_eval(pill[1] == 0x11223344); // expected-warning{{TRUE}} +#elif defined(__BIG_ENDIAN__) + clang_analyzer_eval(pill[0] == 0x11223344); // expected-warning{{TRUE}} + clang_analyzer_eval(pill[1] == 0xaa998877); // expected-warning{{TRUE}} +#else +#error "Don't recognize the endianess of this target!" +#endif + // Array out of bounds should yield UNKNOWN + clang_analyzer_eval(pill[2]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(pill[-1]); // expected-warning{{UNKNOWN}} + + return 0; +}