https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103964
--- Comment #3 from Richard Biener <rguenth at gcc dot gnu.org> --- Yes, the oracle assumes that for MEM[(struct ovs_list *)pos_32 + 64B] pos_32 needs to still point to some valid object (even if it's not of type ovs_list) and a pointer to 'start' cannot be constructed from this without invoking UB (and the pointer offsetting rules are not part of TBAA). The MEMs appear in forwprop: <bb 12> : _15 = &member_59->elem; _16 = &pos_32->elem; - _48 = _16->prev; - _15->prev = _48; - _15->next = _16; + _48 = MEM[(struct ovs_list *)pos_32 + 64B].prev; + MEM[(struct ovs_list *)member_59 + 64B].prev = _48; + MEM[(struct ovs_list *)member_59 + 64B].next = _16; but there it's already pointer arithmetic. In .original we have pos = 0B;, pos = (struct member *) ((long unsigned int) start.next + 18446744073709551552);; goto <D.3776>; <D.3775>:; if (member->order > pos->order) { goto <D.3773>; } pos = (struct member *) ((long unsigned int) pos->elem.next + 18446744073709551552); <D.3776>:; if (&pos->elem != &start) goto <D.3775>; else goto <D.3773>; <D.3773>:; ovs_list_insert (&pos->elem, &member->elem); where I think passing &pos->elem and &member->elem to ovs_list_insert is already wrong since 'pos' doesn't point to a valid object if the ultimate written destination is 'start'. Doing the 'pos' initialization with uintptr_t isn't enough - you need to do this all the way up to the &pos->elem computation as you say: // TESTED: This works: //ovs_list_insert((void *)((uintptr_t)pos + __builtin_offsetof(struct member,elem)), &member->elem); so yes, it's UB. And UB that's not sanctioned with -fno-strict-aliasing.