--- 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 --