fixeria has uploaded this change for review.
llc: fix misaligned MetaInfo access in msgb headroom
struct MetaInfo (two struct timespec) requires 8-byte alignment on
some ABIs (e.g. 32-bit ARM with a 64-bit time_t), but msgb->head is
only guaranteed byte alignment: struct msgb's own size before the
flexible _data[] member is not a multiple of 8, so storing MetaInfo
directly at msg->head can misalign it, triggering UBSan
-fsanitize=alignment reports wherever recv_time/expire_time
are subsequently dereferenced.
Reserve extra headroom for worst-case alignment padding and
store/retrieve MetaInfo via the next 8-byte-aligned address within
that headroom instead of assuming msg->head itself is suitably
aligned.
Change-Id: I39abce68c5678704eea2ad1e330419f645aa4cfd
Related: OS#6858
---
M src/llc.c
1 file changed, 34 insertions(+), 8 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-pcu refs/changes/43/43143/1
diff --git a/src/llc.c b/src/llc.c
index 478d23d..8107c0c 100644
--- a/src/llc.c
+++ b/src/llc.c
@@ -17,6 +17,7 @@
#include <stdio.h>
+#include <stdint.h>
#include <osmocom/core/msgb.h>
@@ -25,6 +26,31 @@
#include "pcu_utils.h"
#include "llc.h"
+/* struct msgb's flexible '_data' array is only guaranteed to start at an
+ * address suitable for 'unsigned char', not for arbitrary types: on some
+ * ABIs (e.g. 32-bit ARM with a 64-bit time_t) 'struct MetaInfo' requires
+ * stricter (8-byte) alignment than that, courtesy of the embedded
+ * 'struct timespec' members, so msgb->head itself cannot be assumed to be
+ * a valid storage location for it. Reserve extra headroom for worst-case
+ * padding and store/retrieve MetaInfo at the next aligned address instead
+ * of at msgb->head directly. */
+#define META_INFO_ALIGN _Alignof(struct MetaInfo)
+#define META_INFO_HEADROOM (sizeof(struct MetaInfo) + META_INFO_ALIGN - 1)
+
+/* Allocate a msgb with enough headroom to hold a MetaInfo (plus worst-case
+ * alignment padding) followed by 'len' bytes of LLC PDU data. */
+static inline struct msgb *llc_msgb_meta_info_alloc(uint16_t len, const char *name)
+{
+ return msgb_alloc_headroom(META_INFO_HEADROOM + len, META_INFO_HEADROOM, name);
+}
+
+static inline struct MetaInfo *llc_msgb_meta_info_get(const struct msgb *msg)
+{
+ uintptr_t p = (uintptr_t)msg->head;
+ p = (p + (META_INFO_ALIGN - 1)) & ~(uintptr_t)(META_INFO_ALIGN - 1);
+ return (struct MetaInfo *)p;
+}
+
void llc_init(struct gprs_llc *llc)
{
llc_reset(llc);
@@ -148,12 +174,12 @@
struct gprs_llc_hdr *llc_hdr;
enum gprs_llc_queue_prio prio;
- llc_msg = msgb_alloc_headroom(sizeof(*meta_storage) + len, sizeof(*meta_storage), "llc_pdu_queue");
+ llc_msg = llc_msgb_meta_info_alloc(len, "llc_pdu_queue");
if (!llc_msg)
return -ENOMEM;
/* Fist set up MetaInfo in the msgb headroom: */
- meta_storage = (struct MetaInfo *)llc_msg->head;
+ meta_storage = llc_msgb_meta_info_get(llc_msg);
osmo_clock_gettime(CLOCK_MONOTONIC, &meta_storage->recv_time);
meta_storage->expire_time = *expire_time;
@@ -215,8 +241,8 @@
msg = msg1;
msg1 = NULL;
} else {
- const struct MetaInfo *mi1 = (struct MetaInfo *)msg1->head;
- const struct MetaInfo *mi2 = (struct MetaInfo *)msg2->head;
+ const struct MetaInfo *mi1 = llc_msgb_meta_info_get(msg1);
+ const struct MetaInfo *mi2 = llc_msgb_meta_info_get(msg2);
if (timespeccmp(&mi2->recv_time, &mi1->recv_time, >)) {
msg = msg1;
@@ -250,10 +276,10 @@
struct MetaInfo *meta_storage;
unsigned int len = llc_frame_length(llc);
- llc_msg = msgb_alloc_headroom(sizeof(*meta_storage) + len, sizeof(*meta_storage), "llc_pdu_queue");
+ llc_msg = llc_msgb_meta_info_alloc(len, "llc_pdu_queue");
OSMO_ASSERT(llc_msg);
- meta_storage = (struct MetaInfo *)llc_msg->head;
+ meta_storage = llc_msgb_meta_info_get(llc_msg);
memcpy(meta_storage, &llc->meta_info, sizeof(struct MetaInfo));
memcpy(msgb_put(llc_msg, len), llc->frame, len);
@@ -283,7 +309,7 @@
if (!msg)
return NULL;
- meta_storage = (struct MetaInfo *)msg->head;
+ meta_storage = llc_msgb_meta_info_get(msg);
q->queue_size -= 1;
q->queue_octets -= msgb_length(msg);
@@ -316,7 +342,7 @@
timespecadd(&tv_now, &hyst_delta, &tv_now2);
while ((msg = llc_queue_pick_msg(q, &prio))) {
- info = (const struct MetaInfo *)msg->head;
+ info = llc_msgb_meta_info_get(msg);
const struct timespec *tv_disc = &info->expire_time;
const struct timespec *tv_recv = &info->recv_time;
To view, visit change 43143. To unsubscribe, or for help writing mail filters, visit settings.