Page MenuHomeFreeBSD

D60829.id190162.diff
No OneTemporary

D60829.id190162.diff

diff --git a/sys/kern/kern_bounds_test.c b/sys/kern/kern_bounds_test.c
--- a/sys/kern/kern_bounds_test.c
+++ b/sys/kern/kern_bounds_test.c
@@ -11,10 +11,13 @@
* add kern_bounds_test.c (not kern_bounds.c) to BOUNDS_SAFETY_FILES to enable
* it. Writing N to debug.bounds_test reads arr[N] of a 4-element array; N is
* clamped to [0, 4], and N == 4 (one past the end) traps.
+ * debug.bounds_mtod_test takes a case number and exercises mtod() and
+ * mtod_len().
*/
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/mbuf.h>
#include <sys/sysctl.h>
#if __has_ptrcheck
@@ -39,4 +42,86 @@
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
sysctl_debug_bounds_test, "I",
"Read index N (0..4) of a 4-element array under -fbounds-safety; N==4 traps");
+
+#define TESTCAP 256
+
+static int
+sysctl_debug_bounds_mtod_test(SYSCTL_HANDLER_ARGS)
+{
+ struct mbuf *m;
+ char *p;
+ u_int realsize;
+ int error, mode;
+ volatile char sink;
+
+ mode = -1;
+ error = sysctl_handle_int(oidp, &mode, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ if (mode < 0 || mode > 8)
+ return (EINVAL);
+
+ /*
+ * Claim a smaller cluster than we have, so out-of-bounds reads still
+ * land in the real allocation when a soft trap lets them run.
+ */
+ m = m_getcl(M_WAITOK, MT_DATA, 0);
+ m->m_len = 64;
+ realsize = m->m_ext.ext_size;
+ memset(mtod(m, char *), 0x5a, realsize);
+ m->m_ext.ext_size = TESTCAP;
+
+ switch (mode) {
+ case 0:
+ p = mtod(m, char *);
+ sink = p[16];
+ break;
+ case 1:
+ p = mtod(m, char *);
+ sink = p[TESTCAP - 1];
+ break;
+ case 2:
+ p = mtod(m, char *);
+ sink = p[TESTCAP];
+ break;
+ case 3:
+ p = mtod_len(m, char *);
+ sink = p[63];
+ break;
+ case 4:
+ p = mtod_len(m, char *);
+ sink = p[64];
+ break;
+ case 5:
+ m->m_len = TESTCAP + 16;
+ p = mtod_len(m, char *);
+ sink = p[TESTCAP];
+ break;
+ case 6:
+ m->m_data += TESTCAP - 8;
+ m->m_len = 64;
+ p = mtod_len(m, char *);
+ sink = p[8];
+ break;
+ case 7:
+ m->m_data += TESTCAP + 1;
+ p = mtod(m, char *);
+ sink = p[0];
+ break;
+ case 8:
+ m->m_flags |= M_EXTPG;
+ p = mtod_len(m, char *);
+ sink = p[0];
+ m->m_flags &= ~M_EXTPG;
+ break;
+ }
+ m->m_ext.ext_size = realsize;
+ printf("bounds-safety: mtod test mode %d -> %d\n", mode, (int)sink);
+ m_freem(m);
+ return (0);
+}
+SYSCTL_PROC(_debug, OID_AUTO, bounds_mtod_test,
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
+ sysctl_debug_bounds_mtod_test, "I",
+ "Exercise the checked mbuf accessors; modes 2 and 4-8 trap");
#endif /* __has_ptrcheck */
diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h
--- a/sys/sys/mbuf.h
+++ b/sys/sys/mbuf.h
@@ -104,9 +104,17 @@
*
* mtod(m, t) -- Convert mbuf pointer to data pointer of correct type.
* mtodo(m, o) -- Same as above but with offset 'o' into data.
+ * mtod_len(m, t) -- Same as mtod(), bounded by m_len with -fbounds-safety.
*/
+#if __has_ptrcheck
+#define mtod(m, t) ((t)m_mtod_current(m))
+#define mtod_len(m, t) ((t)m_mtod_len(m))
+#define mtodo(m, o) ((void *)(m_mtod_current(m) + (o)))
+#else
#define mtod(m, t) ((t)((m)->m_data))
+#define mtod_len(m, t) ((t)((m)->m_data))
#define mtodo(m, o) ((void *)(((m)->m_data) + (o)))
+#endif
/*
* Argument structure passed to UMA routines during mbuf and packet
@@ -344,11 +352,7 @@
SLIST_ENTRY(mbuf) m_slistpkt;
STAILQ_ENTRY(mbuf) m_stailqpkt;
};
- /*
- * __unsafe_indexable: the bound is inside the backing cluster, not
- * expressible as a sibling annotation. A checked mtod() accessor is TBD;
- * until then reads through mtod() are unchecked.
- */
+ /* __unsafe_indexable: the bound is M_START() and M_SIZE(). */
caddr_t __unsafe_indexable m_data; /* location of data */
int32_t m_len; /* amount of data in this mbuf */
uint32_t m_type:8, /* type of data in this mbuf */
@@ -1210,6 +1214,49 @@
((m)->m_flags & M_PKTHDR) ? MHLEN : \
MLEN)
+#if __has_ptrcheck
+/*
+ * Checked mtod(). m_mtod_current() bounds m_data by the mbuf's storage and
+ * m_mtod_len() by m_len. If m_data is outside the storage, or the mbuf is
+ * M_EXTPG, the result has zero size.
+ */
+static __inline __pure caddr_t __header_bidi_indexable
+m_mtod_current(const struct mbuf *m)
+{
+ caddr_t __unsafe_indexable start;
+ __uintptr_t base, cur;
+ size_t size;
+
+ start = __DECONST(caddr_t __unsafe_indexable, M_START(m));
+ if (__predict_false(start == NULL))
+ return (__unsafe_forge_bidi_indexable(caddr_t, m->m_data, 0));
+ size = M_SIZE(m);
+ base = (__uintptr_t)start;
+ cur = (__uintptr_t)m->m_data;
+ /* Compare as integers, since m_data may point outside the mbuf. */
+ if (__predict_false(size > __UINTPTR_MAX__ - base || cur < base ||
+ cur - base > size))
+ return (__unsafe_forge_bidi_indexable(caddr_t, m->m_data, 0));
+ return (__unsafe_forge_bidi_indexable(caddr_t, start, size) +
+ (cur - base));
+}
+
+static __inline __pure caddr_t __header_bidi_indexable
+m_mtod_len(const struct mbuf *m)
+{
+ caddr_t storage;
+ size_t len;
+ caddr_t __sized_by(len) data;
+
+ if (__predict_false((m->m_flags & M_EXTPG) != 0))
+ return (__unsafe_forge_bidi_indexable(caddr_t, m->m_data, 0));
+ storage = m_mtod_current(m);
+ len = m->m_len > 0 ? (size_t)m->m_len : 0;
+ data = storage; /* checked against the storage bound */
+ return (data);
+}
+#endif
+
/*
* Set the m_data pointer of a newly allocated mbuf to place an object of the
* specified size at the end of the mbuf, longword aligned.

File Metadata

Mime Type
text/plain
Expires
Mon, Oct 19, 5:24 AM (17 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41028836
Default Alt Text
D60829.id190162.diff (5 KB)

Event Timeline