On 28/07/2026 18:46, Petr Pavlu wrote:
> On 7/28/26 10:55 AM, Jiacheng Yu wrote:
>> 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]>
> 
> It makes sense to me for param_set_charp() to have commit-or-rollback
> semantics. The set callbacks of other standard parameters behave this
> way, with the exception of array parameters.

Thanks. That is the intent of this patch.

> 
>> ---
>>  kernel/params.c | 14 ++++++++------
>>  1 file changed, 8 insertions(+), 6 deletions(-)
>>
>> diff --git a/kernel/params.c b/kernel/params.c
>> index a668863a4bb6..e4f2b71dde1e 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);
>> +            strscpy(tmp, val, len + 1);
> 
> What's wrong with the plain strcpy() here?

Functionally, plain strcpy() is fine here.  The preceding
strnlen(val, maxlen + 1) either finds the NUL byte within the limit or
rejects the string, and the new allocation is exactly len + 1 bytes.

However, when this patch rewrites the line to use tmp,
checkpatch --strict reports the following warning:

    WARNING: Prefer strscpy over strcpy

Since the line is being rewritten anyway, I changed strcpy() to
strscpy() to follow that preference:
https://github.com/KSPP/linux/issues/88

> 
>>      } else
>> -            *(const char **)kp->arg = val;
>> +            tmp = (char *)val;
>> +
>> +    maybe_kfree_parameter(*(char **)kp->arg);
>> +    *(char **)kp->arg = tmp;
> 
> Sashiko reports [1] that there is a pre-existing use-after-free window
> between freeing the old parameter and updating kp->arg. However, this
> issue doesn't appear to be valid because any concurrent access to
> a writable charp parameter should be protected by kernel_param_lock().
> This is documented include/linux/moduleparam.h [2].

I agree.  Writable charp parameters are expected to be protected by
kernel_param_lock(), and this patch does not change that locking model.

> 
>>  
>>      return 0;
>>  }
> 
> [1] 
> https://lore.kernel.org/linux-modules/[email protected]/
> [2] 
> https://github.com/torvalds/linux/blob/v7.2-rc5/include/linux/moduleparam.h#L124
> 


Reply via email to