Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F176369223
D60829.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
D60829.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Mon, Oct 19, 7:27 AM (17 h, 41 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41028836
Default Alt Text
D60829.diff (5 KB)
Attached To
Mode
D60829: mbuf: add bounds-checked mtod() accessors under bounds-safety
Attached
Detach File
Event Timeline
Log In to Comment