fixeria has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/libosmocore/+/43150?usp=email )


Change subject: linuxlist: fix false-positive UBSan misaligned-access reports
......................................................................

linuxlist: fix false-positive UBSan misaligned-access reports

llist_for_each_entry() and its variants terminate by comparing
&pos->member against head.  Once a full traversal reaches the end of a
non-empty list (or the list is empty to begin with), pos becomes a pure
container_of()-computed sentinel address that was never a real object
of typeof(*pos), only ever the plain 'struct llist_head *' passed in as
head.  Forming 'pos->member' on that address requires pos to satisfy
the alignment of typeof(*pos), which the sentinel does not necessarily
provide (e.g. embedded struct osmo_timer_list/gprs_nsvc on 32-bit ARM
with a 64-bit time_t need 8-byte alignment, while head itself is only
pointer-aligned) - tripping -fsanitize=alignment even though no real
object is ever misaligned or dereferenced.

Add __llist_member(), computing the same address via 'char *'
pointer arithmetic and an explicit cast to 'struct llist_head *', which
carries no alignment requirement of its own.  Use it for the loop
termination check, the "next" pointer computation, and the prefetch()
calls (dereferencing the returned struct llist_head * still only
requires pointer alignment, so prefetch() keeps working exactly
as before without reintroducing the false positive).

Change-Id: I0424e76e76d8aa9402bd1a5aefe789de16e72fae
Related: OS#7036, OS#6858
---
M include/osmocom/core/linuxlist.h
1 file changed, 33 insertions(+), 19 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/50/43150/1

diff --git a/include/osmocom/core/linuxlist.h b/include/osmocom/core/linuxlist.h
index 8c8b1bc..e5e5075 100644
--- a/include/osmocom/core/linuxlist.h
+++ b/include/osmocom/core/linuxlist.h
@@ -218,6 +218,20 @@
 #define llist_entry(ptr, type, member) \
        container_of(ptr, type, member)

+/*! Compute the address of the llist_head member within *pos, without
+ *  forming a 'pos->member' expression.
+ *
+ *  This exists so llist_for_each_entry() and friends can test for the
+ *  end of the list (where 'pos' is a container_of()-computed address
+ *  that was never a real object of typeof(*pos), only ever a
+ *  'struct llist_head *') without tripping -fsanitize=alignment: a
+ *  'pos->member' access requires 'pos' itself to satisfy the alignment
+ *  of typeof(*pos), which the end-of-list address does not necessarily
+ *  do, while plain 'char *' pointer arithmetic has no such requirement.
+ */
+#define __llist_member(pos, member) \
+       ((struct llist_head *)((char *)(pos) + offsetof(typeof(*(pos)), 
member)))
+
 /*! Get the first element from a linked list.
  *  \param ptr    the list head to take the element from.
  *  \param type   the type of the struct this is embedded in.
@@ -308,10 +322,10 @@
  */
 #define llist_for_each_entry(pos, head, member)                                
\
        for (pos = llist_entry((head)->next, typeof(*pos), member),     \
-                    prefetch(pos->member.next);                        \
-            &pos->member != (head);                                    \
-            pos = llist_entry(pos->member.next, typeof(*pos), member), \
-                    prefetch(pos->member.next))
+                    prefetch(__llist_member(pos, member)->next);       \
+            __llist_member(pos, member) != (head);                     \
+            pos = llist_entry(__llist_member(pos, member)->next, typeof(*pos), 
member), \
+                    prefetch(__llist_member(pos, member)->next))

 /*! Iterate backwards over a linked list of a given type.
  *  \param pos    the 'type *' to use as a loop counter.
@@ -320,10 +334,10 @@
  */
 #define llist_for_each_entry_reverse(pos, head, member)                        
\
        for (pos = llist_entry((head)->prev, typeof(*pos), member),     \
-                    prefetch(pos->member.prev);                        \
-            &pos->member != (head);                                    \
-            pos = llist_entry(pos->member.prev, typeof(*pos), member), \
-                    prefetch(pos->member.prev))
+                    prefetch(__llist_member(pos, member)->prev);       \
+            __llist_member(pos, member) != (head);                     \
+            pos = llist_entry(__llist_member(pos, member)->prev, typeof(*pos), 
member), \
+                    prefetch(__llist_member(pos, member)->prev))

 /*! Iterate over a linked list of a given type,
  *  continuing after an existing point.
@@ -333,10 +347,10 @@
  */
 #define llist_for_each_entry_continue(pos, head, member)               \
        for (pos = llist_entry(pos->member.next, typeof(*pos), member), \
-                    prefetch(pos->member.next);                        \
-            &pos->member != (head);                                    \
-            pos = llist_entry(pos->member.next, typeof(*pos), member), \
-                    prefetch(pos->member.next))
+                    prefetch(__llist_member(pos, member)->next);       \
+            __llist_member(pos, member) != (head);                     \
+            pos = llist_entry(__llist_member(pos, member)->next, typeof(*pos), 
member), \
+                    prefetch(__llist_member(pos, member)->next))

 /*! Iterate over llist of given type, safe against removal of llist entry.
  *  \param pos    the 'type *' to use as a loop counter.
@@ -346,9 +360,9 @@
  */
 #define llist_for_each_entry_safe(pos, n, head, member)                        
\
        for (pos = llist_entry((head)->next, typeof(*pos), member),     \
-               n = llist_entry(pos->member.next, typeof(*pos), member);        
\
-            &pos->member != (head);                                    \
-            pos = n, n = llist_entry(n->member.next, typeof(*n), member))
+               n = llist_entry(__llist_member(pos, member)->next, 
typeof(*pos), member); \
+            __llist_member(pos, member) != (head);                     \
+            pos = n, n = llist_entry(__llist_member(n, member)->next, 
typeof(*n), member))

 /*! Iterate over an rcu-protected llist.
  *  \param pos  the llist_head to use as a loop counter.
@@ -378,11 +392,11 @@
  */
 #define llist_for_each_entry_rcu(pos, head, member)                    \
        for (pos = llist_entry((head)->next, typeof(*pos), member),     \
-                    prefetch(pos->member.next);                        \
-            &pos->member != (head);                                    \
-            pos = llist_entry(pos->member.next, typeof(*pos), member), \
+                    prefetch(__llist_member(pos, member)->next);       \
+            __llist_member(pos, member) != (head);                     \
+            pos = llist_entry(__llist_member(pos, member)->next, typeof(*pos), 
member), \
                     ({ smp_read_barrier_depends(); 0;}),               \
-                    prefetch(pos->member.next))
+                    prefetch(__llist_member(pos, member)->next))


 /*! Iterate over an rcu-protected llist, continuing after existing point.

--
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/43150?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: I0424e76e76d8aa9402bd1a5aefe789de16e72fae
Gerrit-Change-Number: 43150
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <[email protected]>

Reply via email to