param_set_charp() stores charp parameters in allocated memory after slab is
available, and releases the previous value when the parameter is updated.

The previous value is released before the replacement allocation succeeds.
If kmalloc_parameter() fails, the setter returns -ENOMEM with the parameter
left as NULL.

Failing zswap's compressor update before zswap is initialized can later
trigger:

  BUG: kernel NULL pointer dereference, address: 0000000000000000
  RIP: 0010:strcmp+0x10/0x30
  Call Trace:
    zswap_setup+0x3b1/0x490
    zswap_enabled_param_set+0x5b/0xa0
    param_attr_store+0x93/0xe0
    module_attr_store+0x1c/0x30
    kernfs_fop_write_iter+0x116/0x1f0

Allocate and copy the replacement first, then replace the parameter value
only after allocation succeeds.

Fixes: e180a6b7759a ("param: fix charp parameters set via sysfs")
Cc: [email protected]
Signed-off-by: Jiacheng Yu <[email protected]>
---

v1 -> v2:
- Use memcpy() instead of strscpy() as the length is already known.

 kernel/params.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/kernel/params.c b/kernel/params.c
index a668863a4bb6..939ef84a46ff 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -261,6 +261,7 @@ EXPORT_SYMBOL_GPL(param_set_uint_minmax);
 
 int param_set_charp(const char *val, const struct kernel_param *kp)
 {
+       char *tmp;
        size_t len, maxlen = 1024;
 
        len = strnlen(val, maxlen + 1);
@@ -269,19 +270,20 @@ int param_set_charp(const char *val, const struct 
kernel_param *kp)
                return -ENOSPC;
        }
 
-       maybe_kfree_parameter(*(char **)kp->arg);
-
        /*
         * This is a hack. We can't kmalloc() in early boot, and we
         * don't need to; this mangled commandline is preserved.
         */
        if (slab_is_available()) {
-               *(char **)kp->arg = kmalloc_parameter(len + 1);
-               if (!*(char **)kp->arg)
+               tmp = kmalloc_parameter(len + 1);
+               if (!tmp)
                        return -ENOMEM;
-               strcpy(*(char **)kp->arg, val);
+               memcpy(tmp, val, len + 1);
        } else
-               *(const char **)kp->arg = val;
+               tmp = (char *)val;
+
+       maybe_kfree_parameter(*(char **)kp->arg);
+       *(char **)kp->arg = tmp;
 
        return 0;
 }
-- 
2.34.1


Reply via email to