[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH 1/2] xen/list: avoid UB in list iterators
On 16.02.2025 11:23, Juergen Gross wrote: > The list_for_each_entry*() iterators are testing for having reached the > end of the list in a way which relies on undefined behavior: the > iterator (being a pointer to the struct of a list element) is advanced > and only then tested to have reached not the next element, but the list > head. This results in the list head being addressed via a list element > pointer, which is undefined, in case the list elements have a higher > alignment then the list head. Nit: s/then/than/ > --- a/xen/include/xen/list.h > +++ b/xen/include/xen/list.h > @@ -291,6 +291,17 @@ static inline void list_move_tail(struct list_head *list, > list_add_tail(list, head); > } > > +/** > + * list_is_first - tests whether @list is the first entry in list @head > + * @list: the entry to test > + * @head: the head of the list > + */ > +static inline int list_is_first(const struct list_head *list, bool? > + const struct list_head *head) > +{ > + return list->prev == head; > +} "list" is ambiguous, as it could also mean the start of the list. Maybe better "entry"? (I understand that'll be inconsistent with the subsequent list_is_last(), but what do you do.) > @@ -440,7 +451,19 @@ static inline void list_splice_init(struct list_head > *list, > */ > #define list_next_entry(pos, member) \ > list_entry((pos)->member.next, typeof(*(pos)), member) > - > + > +/** > + * list_next_entry_or_null - get the next element in list > + * @pos: the type * to cursor > + * @member: the name of the struct list_head within the struct. Nit: Stray 2nd blank before "within" > @@ -492,10 +527,10 @@ static inline void list_splice_init(struct list_head > *list, > * @head: the head for your list. > * @member: the name of the list_struct within the struct. > */ > -#define list_for_each_entry(pos, head, member) \ > - for ((pos) = list_entry((head)->next, typeof(*(pos)), member); \ > - &(pos)->member != (head); \ > - (pos) = list_entry((pos)->member.next, typeof(*(pos)), member)) > +#define list_for_each_entry(pos, head, member) \ > + for ( (pos) = list_first_entry_or_null(head, typeof(*(pos)), member); \ > + pos; \ I suspect Misra would demand pos to be parenthesized here (and in similar places elsewhere), too. > @@ -556,11 +590,11 @@ static inline void list_splice_init(struct list_head > *list, > * @head: the head for your list. > * @member: the name of the list_struct within the struct. > */ > -#define list_for_each_entry_safe(pos, n, head, member) \ > - for ((pos) = list_entry((head)->next, typeof(*(pos)), member), \ > - (n) = list_entry((pos)->member.next, typeof(*(pos)), member); \ > - &(pos)->member != (head); \ > - (pos) = (n), (n) = list_entry((n)->member.next, typeof(*(n)), > member)) > +#define list_for_each_entry_safe(pos, n, head, member) \ > + for ( (pos) = list_first_entry_or_null(head, typeof(*(pos)), member), \ > + (n) = (pos) ? list_next_entry_or_null(head, pos, member) : NULL; \ n can end up being NULL here even if pos isn't. Then ... > + pos; \ > + (pos) = (n), (n) = list_next_entry_or_null(head, n, member) ) ... you can't use list_next_entry_or_null() on it. > @@ -655,10 +689,10 @@ static inline void list_splice_init(struct list_head > *list, > * the _rcu list-mutation primitives such as list_add_rcu() > * as long as the traversal is guarded by rcu_read_lock(). > */ > -#define list_for_each_entry_rcu(pos, head, member) \ > - for ((pos) = list_entry((head)->next, typeof(*(pos)), member); \ > - &rcu_dereference(pos)->member != (head); \ > - (pos) = list_entry((pos)->member.next, typeof(*(pos)), member)) > +#define list_for_each_entry_rcu(pos, head, member) \ > + for ( (pos) = list_first_entry_or_null(head, typeof(*(pos)), member); \ > + rcu_dereference(pos); \ > + (pos) = list_next_entry_or_null(head, pos, member) ) Don't you need a list_next_entry_or_null_rcu() flavor here, using rcu_dereference() on the passed in pos for the (pos)->member.next deref? Question on the patch as a whole: Since I have a vague recollection that we may have a use or two of one of these iterator macros which actually make assumptions on the state of pos upon loop exit, did you audit all use sites? Jan
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |