Bug 195235
Summary: | llist.h llist_for_each_entry/llist_for_each_entry_safe have undefined behavior | ||
---|---|---|---|
Product: | Other | Reporter: | Erich Keane (erich.keane) |
Component: | Other | Assignee: | other_other |
Status: | NEW --- | ||
Severity: | high | CC: | erich.keane, sos22-lk, ying.huang |
Priority: | P1 | ||
Hardware: | All | ||
OS: | Linux | ||
Kernel Version: | 3.13->Trunk | Subsystem: | |
Regression: | No | Bisected commit-id: |
Description
Erich Keane
2017-04-03 19:22:17 UTC
Is that right? I thought the marker for end of an llist was that member.next is NULL, which isn't quite the same condition as llist_entry(member.next) being null? Maybe something like this: #define llist_for_each_entry(pos, node, member) \ for ((pos) = (node) ? llist_entry((node), typeof(*(pos)), member) : NULL; \ (pos) != NULL; \ (pos) = (pos)->member.next ? llist_entry((pos)->member.next, typeof(*(pos)), member) : NULL) Although adding a load of extra branches just to avoid unfortunate compiler optimisations is kind of sad. Or maybe: #define llist_for_each_entry(pos, node, member) \ for ((pos) = llist_entry((node), typeof(*(pos)), member); \ (intptr_t)(pos) + offsetof(typeof(*pos), member) != 0; \ (pos) = llist_entry((pos)->member.next, typeof(*(pos)), member)) If thats the case, that isn't what the compiler is doing, whether or not there is an optimizer. Assuming ZERO optimizers, &pos->member != NULL will only be false in the event that "member" is the first member, AND POS is NULL, right? Otherwise why would "(intptr_t)(pos) + offsetof(typeof(*pos), member)" be zero? Well, no. In the absence of optimisation, &pos->member is null if member is 10 bytes into the pos type and pos is itself -10. -10 is not null, so &pos->member can be null even when pos isn't. llist_entry(NULL, type, member) returns -offsetof(type, member), so llist_entry(X, type, member) + offsetof(type, member) (with appropriate casts) should be zero precisely when X is null, so my suggestion preserves the existing behavior in the absence of optimization. I would guess (emphasize guess) that it'd be enough to stop the compiler breaking things for you with optimizations on. A couple of things to note, though: 1) I've not made any attempt to check any of this by actually happens, so it's possible that I've just misunderstood something. 2) The kernel bugzilla doesn't get as much attention as you might expect. If you actually want this fixed, you'd probably be better off posting on the mailing list, or maybe going directly to rev(com, intel., at, huang, ying.), since he wrote that macro. 3) When you make a report, try to include a minimal demonstration of something going wrong. At a minimum, you should probably include an example of the macro being used in a way which you think leads to incorrect behavior, and the compiler you were testing with. Wow... thats ALSO likely UB (pointer wrapping, since the underlying type of a pointer isn't a signed or unsigned type!). Fortunately, Ying is apparently from my company as well, so I'll follow up via email. Thanks! -Erich There is some discussion on LKML about this, please refer to it for Linux kernel developers' opinions on this issue. https://lkml.org/lkml/2017/1/15/194 |