⬢ DragonFlyBSD Kernel Audit
DF-5691 / fix.diff
← back to finding ↓ download raw
--- a/sys/libprop/prop_string.c
+++ b/sys/libprop/prop_string.c
@@ -185,6 +185,36 @@
  *	Create an immutable string that contains a refrence to the
  *	provided C string.
  */
+#if defined(_KERNEL) || defined(PROP_RIG_FORCE_NOCOPY_COPY)
+/*
+ * DF-5691: in a kernel build the "nocopy" borrowing semantics are a
+ * use-after-free / heap-overflow foot-gun: the prop_string keeps a raw
+ * pointer to caller-owned memory, never owns or copies it
+ * (_prop_string_free skips PS_F_NOCOPY buffers, prop_string.c:85-87), and
+ * ps_size is frozen at creation while prop_string_cstring()/
+ * externalization re-read the buffer to its *current* NUL
+ * (prop_string.c:302-304).  A plist that outlives (or outgrows) the
+ * caller's buffer therefore reads freed/stale kernel memory into data
+ * copied back to userspace (the dm-ioctl dict copyout externalize path),
+ * and the copy variant can overflow its ps_size+1 allocation.  No kernel
+ * code calls the set-side nocopy creator today (rg sys/); make the kernel
+ * build copy so a plist can never reference caller-owned transient memory.
+ * (The get-side borrow -- prop_dictionary_get_cstring_nocopy, 26 live
+ * dm_ioctl.c/tbridge.c callers -- is a caller-contract issue: the dm
+ * sites that replace name/uuid keys mid-function keep the borrows
+ * un-dereferenced today; those callers should retain or use the copy
+ * variant.)
+ *
+ * PROP_RIG_FORCE_NOCOPY_COPY is a verification-rig-only activation define
+ * (findings/poc/DF-5691); it is undefined in every real build.
+ */
+prop_string_t
+prop_string_create_cstring_nocopy(const char *str)
+{
+
+	return (prop_string_create_cstring(str));
+}
+#else
 prop_string_t
 prop_string_create_cstring_nocopy(const char *str)
 {
@@ -198,6 +228,7 @@
 	}
 	return (ps);
 }
+#endif
 
 /*
  * prop_string_copy --