diff --git a/clang/lib/StaticAnalyzer/Checkers/PointerAlignmentChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PointerAlignmentChecker.cpp index f45cd0b121ac3..59c06c61146a8 100644 --- a/clang/lib/StaticAnalyzer/Checkers/PointerAlignmentChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/PointerAlignmentChecker.cpp @@ -191,15 +191,22 @@ int getTrailingZerosCount(const MemRegion *R, ProgramStateRef State, unsigned NaturalAlign = ASTCtx.getTypeAlignInChars(PT).getQuantity(); if (const FieldRegion *FR = R->getAs()) { - // If this is the first field of a larger struct, we can use the alignment - // of the containing struct try to increase the assumed alignment. + // If this is the a field of a larger struct, we can use the alignment + // of the containing struct combined with the offset to try to increase + // the assumed alignment. const RegionOffset &Offset = FR->getAsOffset(); - if (!Offset.hasSymbolicOffset() && Offset.getOffset() == 0) { + if (!Offset.hasSymbolicOffset()) { if (const auto *Base = FR->getSuperRegion()->getAs()) { auto BaseTy = Base->getValueType(); unsigned BaseAlign = ASTCtx.getTypeAlignInChars(BaseTy).getQuantity(); - NaturalAlign = std::max(NaturalAlign, BaseAlign); + uint64_t FieldOffsetBits = Offset.getOffset(); + unsigned Offset = + ASTCtx.toCharUnitsFromBits(FieldOffsetBits).getQuantity(); + unsigned OffsetAlign = + (Offset == 0) ? BaseAlign : (1ULL << llvm::countr_zero(Offset)); + unsigned InferredAlign = std::min(BaseAlign, OffsetAlign); + NaturalAlign = std::max(NaturalAlign, InferredAlign); } } } diff --git a/clang/test/Analysis/pointer-alignment.c b/clang/test/Analysis/pointer-alignment.c index 5e6fbbb7d1f13..ea748505e951f 100644 --- a/clang/test/Analysis/pointer-alignment.c +++ b/clang/test/Analysis/pointer-alignment.c @@ -44,12 +44,12 @@ uintptr_t* bar(uintptr_t *p) { struct S { intptr_t u[40]; - int i[40]; // expected-note{{Original allocation}} + int i[40]; int i_aligned[40] __attribute__((aligned(16))); // expected-note{{Original allocation}} }; int struct_field(struct S *s) { uintptr_t* p1 = (uintptr_t*)&s->u[3]; // no warning - uintptr_t* p2 = (uintptr_t*)&s->i[8]; // expected-warning{{Pointer value aligned to a 4 byte boundary cast to type 'uintptr_t * __capability' with 16-byte capability alignment}} + uintptr_t* p2 = (uintptr_t*)&s->i[8]; // no warning uintptr_t* p3 = (uintptr_t*)&s->i_aligned[6]; // expected-warning{{Pointer value aligned to a 8 byte boundary cast to type 'uintptr_t * __capability' with 16-byte capability alignment}} uintptr_t* p4 = (uintptr_t*)&s->i_aligned[4]; // no warning return (p4 - p3) + (p2 - p1); @@ -207,6 +207,8 @@ void cast_and_assign(void) { struct __attribute__((aligned(8))) AlignedParentStruct { int a; int b; + int c; + int d; }; void write_to_first_member(struct AlignedParentStruct *chunk) { @@ -214,9 +216,14 @@ void write_to_first_member(struct AlignedParentStruct *chunk) { *(long long*)(&chunk->a) = 0; } +void write_to_second_member(struct AlignedParentStruct *chunk) { + // No warning because alignment of is inferred from the parent struct. + *(long long*)(&chunk->c) = 0; +} + // ---- long long * __sealed_capability sealed_cast(int * __sealed_capability in) { // No warnings for sealed capabilities, since they will be effectively dynamically // verified when unsealed. return (long long * __sealed_capability)in; -} \ No newline at end of file +}