From: Mathieu Desnoyers Date: Tue, 20 Apr 2021 20:07:54 +0000 (-0400) Subject: Fix: hlist iteration relies on undefined behavior X-Git-Tag: v0.13.0~12 X-Git-Url: https://git.liburcu.org/?p=urcu.git;a=commitdiff_plain;h=a0b5ef6802894441aae058f9a34628b565a35846 Fix: hlist iteration relies on undefined behavior Comparing an offset from an object with NULL is undefined behavior and the compiler may assume that this is never true. This is indeed what is observed with gcc-10 miscompiling cds_hlist_for_each_entry_rcu_2(). Fix this by introducing cds_hlist_entry_safe() rather than open-coding the NULL check comparisons, and move cds_hlist_for_each_entry_2() and cds_hlist_for_each_entry_safe_2() to this scheme as well. Fixes: #1308 Signed-off-by: Mathieu Desnoyers Change-Id: Ief3531cf04e54b6dae05eb28a6822adfa141fdeb --- diff --git a/include/urcu/hlist.h b/include/urcu/hlist.h index 3444811..5e3c27d 100644 --- a/include/urcu/hlist.h +++ b/include/urcu/hlist.h @@ -17,6 +17,7 @@ */ #include +#include struct cds_hlist_head { struct cds_hlist_node *next; @@ -40,8 +41,14 @@ void CDS_INIT_HLIST_HEAD(struct cds_hlist_head *ptr) { .next = NULL } /* Get typed element from list at a given position. */ -#define cds_hlist_entry(ptr, type, member) \ - ((type *) ((char *) (ptr) - (unsigned long) (&((type *) 0)->member))) +#define cds_hlist_entry(ptr, type, member) caa_container_of(ptr, type, member) + +/* Get typed element from list at a given position, keeping NULL pointers. */ +#define cds_hlist_entry_safe(ptr, type, member) \ + ({ \ + __typeof__(ptr) ____ret = ptr; \ + ____ret ? cds_hlist_entry(____ret, type, member) : NULL; \ + }) /* Add new element at the head of the list. */ static inline @@ -93,17 +100,13 @@ void cds_hlist_del(struct cds_hlist_node *elem) entry = cds_hlist_entry(pos, __typeof__(*entry), member)) #define cds_hlist_for_each_entry_2(entry, head, member) \ - for (entry = ((head)->next == NULL ? NULL \ - : cds_hlist_entry((head)->next, __typeof__(*entry), member)); \ + for (entry = cds_hlist_entry_safe((head)->next, __typeof__(*entry), member); \ entry != NULL; \ - entry = (entry->member.next == NULL ? NULL \ - : cds_hlist_entry(entry->member.next, __typeof__(*entry), member))) + entry = cds_hlist_entry_safe(entry->member.next, __typeof__(*entry), member)) #define cds_hlist_for_each_entry_safe_2(entry, e, head, member) \ - for (entry = ((head)->next == NULL ? NULL \ - : cds_hlist_entry((head)->next, __typeof__(*entry), member)); \ - (entry != NULL) && (e = (entry->member.next == NULL ? NULL \ - : cds_hlist_entry(entry->member.next, \ + for (entry = cds_hlist_entry_safe((head)->next, __typeof__(*entry), member); \ + (entry != NULL) && (e = (cds_hlist_entry_safe(entry->member.next, \ __typeof__(*entry), member)), 1); \ entry = e) diff --git a/include/urcu/rcuhlist.h b/include/urcu/rcuhlist.h index 69c4d31..ca1da06 100644 --- a/include/urcu/rcuhlist.h +++ b/include/urcu/rcuhlist.h @@ -72,10 +72,10 @@ void cds_hlist_del_rcu(struct cds_hlist_node *elem) entry = cds_hlist_entry(pos, __typeof__(*entry), member)) #define cds_hlist_for_each_entry_rcu_2(entry, head, member) \ - for (entry = cds_hlist_entry(rcu_dereference((head)->next), \ + for (entry = cds_hlist_entry_safe(rcu_dereference((head)->next), \ __typeof__(*entry), member); \ - &entry->member != NULL; \ - entry = cds_hlist_entry(rcu_dereference(entry->member.next), \ + entry != NULL; \ + entry = cds_hlist_entry_safe(rcu_dereference(entry->member.next), \ __typeof__(*entry), member)) #endif /* _URCU_RCUHLIST_H */