diff options
-rw-r--r-- | lib/StaticAnalyzer/Core/MemRegion.cpp | 4 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/RegionStore.cpp | 7 | ||||
-rw-r--r-- | test/Analysis/fields.c | 87 |
3 files changed, 95 insertions, 3 deletions
diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index 12e43537aa..b3a1e65b19 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -195,6 +195,10 @@ DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const } DefinedOrUnknownSVal FieldRegion::getExtent(SValBuilder &svalBuilder) const { + // Force callers to deal with bitfields explicitly. + if (getDecl()->isBitField()) + return UnknownVal(); + DefinedOrUnknownSVal Extent = DeclRegion::getExtent(svalBuilder); // A zero-length array at the end of a struct often stands for dynamically- diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index b93c10d680..82db23dd6b 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -757,9 +757,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings, TopKey = BindingKey::Make(Top, BindingKey::Default); } - // This assumes the region being invalidated is char-aligned. This isn't - // true for bitfields, but since bitfields have no subregions they shouldn't - // be using this function anyway. + // Find the length (in bits) of the region being invalidated. uint64_t Length = UINT64_MAX; SVal Extent = Top->getExtent(SVB); if (Optional<nonloc::ConcreteInt> ExtentCI = @@ -768,6 +766,9 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings, assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned()); // Extents are in bytes but region offsets are in bits. Be careful! Length = ExtentInt.getLimitedValue() * SVB.getContext().getCharWidth(); + } else if (const FieldRegion *FR = dyn_cast<FieldRegion>(Top)) { + if (FR->getDecl()->isBitField()) + Length = FR->getDecl()->getBitWidthValue(SVB.getContext()); } for (ClusterBindings::iterator I = Cluster.begin(), E = Cluster.end(); diff --git a/test/Analysis/fields.c b/test/Analysis/fields.c index 12e8bbf367..863a21aaf6 100644 --- a/test/Analysis/fields.c +++ b/test/Analysis/fields.c @@ -29,6 +29,10 @@ void test() { (void)(p = getit()).x; } +#define true ((bool)1) +#define false ((bool)0) +typedef _Bool bool; + void testLazyCompoundVal() { Point p = {42, 0}; @@ -36,3 +40,86 @@ void testLazyCompoundVal() { clang_analyzer_eval((q = p).x == 42); // expected-warning{{TRUE}} clang_analyzer_eval(q.x == 42); // expected-warning{{TRUE}} } + + +struct Bits { + unsigned a : 1; + unsigned b : 2; + unsigned c : 1; + + bool x; + + struct InnerBits { + bool y; + + unsigned d : 16; + unsigned e : 6; + unsigned f : 2; + } inner; +}; + +void testBitfields() { + struct Bits bits; + + if (foo() && bits.b) // expected-warning {{garbage}} + return; + if (foo() && bits.inner.e) // expected-warning {{garbage}} + return; + + bits.c = 1; + clang_analyzer_eval(bits.c == 1); // expected-warning {{TRUE}} + + if (foo() && bits.b) // expected-warning {{garbage}} + return; + if (foo() && bits.x) // expected-warning {{garbage}} + return; + + bits.x = true; + clang_analyzer_eval(bits.x == true); // expected-warning{{TRUE}} + bits.b = 2; + clang_analyzer_eval(bits.x == true); // expected-warning{{TRUE}} + if (foo() && bits.c) // no-warning + return; + + bits.inner.e = 50; + if (foo() && bits.inner.e) // no-warning + return; + if (foo() && bits.inner.y) // expected-warning {{garbage}} + return; + if (foo() && bits.inner.f) // expected-warning {{garbage}} + return; + + extern struct InnerBits getInner(); + bits.inner = getInner(); + + if (foo() && bits.inner.e) // no-warning + return; + if (foo() && bits.inner.y) // no-warning + return; + if (foo() && bits.inner.f) // no-warning + return; + + bits.inner.f = 1; + + if (foo() && bits.inner.e) // no-warning + return; + if (foo() && bits.inner.y) // no-warning + return; + if (foo() && bits.inner.f) // no-warning + return; + + if (foo() && bits.a) // expected-warning {{garbage}} + return; +} + + +//----------------------------------------------------------------------------- +// Incorrect behavior +//----------------------------------------------------------------------------- + +void testTruncation() { + struct Bits bits; + bits.c = 0x11; // expected-warning{{implicit truncation}} + // FIXME: We don't model truncation of bitfields. + clang_analyzer_eval(bits.c == 1); // expected-warning {{FALSE}} +} |