]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
netconsole: avoid clobbering userdatum value on truncated write
authorBreno Leitao <leitao@debian.org>
Mon, 27 Apr 2026 14:30:36 +0000 (07:30 -0700)
committerJakub Kicinski <kuba@kernel.org>
Wed, 29 Apr 2026 01:28:11 +0000 (18:28 -0700)
userdatum_value_store() bounds count by MAX_EXTRADATA_VALUE_LEN (200)
and then copies straight into udm->value, which is itself 200 bytes:

if (count > MAX_EXTRADATA_VALUE_LEN)
return -EMSGSIZE;
...
ret = strscpy(udm->value, buf, sizeof(udm->value));
if (ret < 0)
goto out_unlock;

If userspace writes exactly MAX_EXTRADATA_VALUE_LEN bytes with no NUL
within them, strscpy() copies 199 bytes plus a NUL into udm->value and
returns -E2BIG. The function jumps to out_unlock and reports the error
to userspace, but udm->value has already been overwritten with the
truncated string and update_userdata() is skipped, so the corruption
is not yet visible on the wire.

The next successful write to any userdatum entry under the same target
calls update_userdata(), which packs udm->value into the active
netconsole payload. From that point on, every netconsole message
carries the silently truncated value, and userspace has no indication
that a previous, error-returning write left state behind.

Tighten the entry check from "count > MAX_EXTRADATA_VALUE_LEN" to
"count >= MAX_EXTRADATA_VALUE_LEN". With count strictly less than
sizeof(udm->value), strscpy() can no longer return -E2BIG here, so
the corrupting truncation path is removed entirely.

Fixes: 8a6d5fec6c7f ("net: netconsole: add a userdata config_group member to netconsole_target")
Signed-off-by: Breno Leitao <leitao@debian.org>
Link: https://patch.msgid.link/20260427-netconsole_ai_fixes-v2-2-59965f29d9cc@debian.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/netconsole.c

index 76d7fbf9e1883fa24d8d7ba0480468c09b27103b..595e09bd1ccfcfb955cca5307c243e74bd65aeaf 100644 (file)
@@ -1076,15 +1076,13 @@ static ssize_t userdatum_value_store(struct config_item *item, const char *buf,
        struct userdata *ud;
        ssize_t ret;
 
-       if (count > MAX_EXTRADATA_VALUE_LEN)
+       if (count >= MAX_EXTRADATA_VALUE_LEN)
                return -EMSGSIZE;
 
        mutex_lock(&netconsole_subsys.su_mutex);
        dynamic_netconsole_mutex_lock();
-
-       ret = strscpy(udm->value, buf, sizeof(udm->value));
-       if (ret < 0)
-               goto out_unlock;
+       /* count is bounded above, so strscpy() cannot truncate here */
+       strscpy(udm->value, buf, sizeof(udm->value));
        trim_newline(udm->value, sizeof(udm->value));
 
        ud = to_userdata(item->ci_parent);