https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103964

--- Comment #10 from Ilya Maximets <i.maximets at ovn dot org> ---
(In reply to Jakub Jelinek from comment #9)
> (In reply to Ilya Maximets from comment #7)
> > (In reply to Jakub Jelinek from comment #6)
> > > What is the reason why OVS (and kernel) doesn't use 2 variables, one for 
> > > the
> > > iterator that is a pointer to the prev/next structure only and one 
> > > assigned
> > > e.g. in the condition from the iterator that is used only when it isn't 
> > > the
> > > start?
> > > At least if targetting C99 and above (or C++) one can declare one of those
> > > iterators in the for loop init expression...
> > 
> > The problem is that we need 2 variables and one of them need to be 
> > accessible
> > outside of the loop.  And I don't think it is possible to declare one
> > variable
> > and only initialize another one.
> 
> If you only need to use one variable outside of the loop and not the other
> one,
> that should be doable.  If you use one in some spots and another in other
> spots but never both, you could have different macros for those 2 versions.
> 
> E.g. the one where the list iterator is used inside of the loop only and the
> var pointing to the containing objects could look like:
>         for (struct ovs_list *iter = (pos = NULL, &start); iter != (&start)
>              && (((pos) = ((typeof(pos))(void*)((uintptr_t)((iter->next) -
>                                            __builtin_offsetof(struct member,
>                                                               elem))))), 1);
>              iter = iter->next, pos = NULL)
> which would get you after the loop pos = NULL if break; wasn't done and
> pos non-NULL otherwise.

Hmm.  I missed the possibility of a comma trick in the initialization part.
I think, we can try to re-write our macros this way.  Thanks!

> But in your testcase you actually need to use the other var after the loop,
> so it would need to be done the other way around, but then the user variable
> would need to be struct ovs_list * and the macro would need to be told in a
> different way what type the var declared in the loop should have.

Doesn't sound very intuitive.  I guess, it's easier to just add a NULL pointer
check after the loop, i.e. ovs_list_insert(pos ? &pos->elem : &start, ...).
Assuming our unit tests will catch all NULL-pointer dereferences (they will
likely not, but still), that could be a semi-automated way to find all the
problematic code parts.
Not very friendly for dependent projects though.

> 
> > One thing that is not clear to me is if the following code has an UB or not:
> > 
> >     struct member* pos;
> >     struct ovs_list start;
> > 
> >     pos = (struct member *)(void*)((uintptr_t)(&start) - 64);
> >     ovs_list_insert((void *)((uintptr_t)pos + 64), &member->elem);
> 
> It still creates a pointer out of something that doesn't point to such an
> object or points to some unrelated one, doesn't necessarily have sufficient
> alignment etc., so I think it is UB too, but perhaps the compiler at least
> now will handle it the way you expect.  A lot of programs including e.g.
> POSIX rely on at least
> (void *) -1 and similar pointer constants to be usable in equality
> comparisons (e.g. MAP_FAILED macro).

We also have this kind of stuff:

  static const struct ovs_list OVS_LIST_POISON =
      { (struct ovs_list *) (UINTPTR_MAX / 0xf * 0xc),
        (struct ovs_list *) (UINTPTR_MAX / 0xf * 0xc) };

Reply via email to