 
	
| [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 |