vmalloc() and vzalloc() take a byte count, but ALLOC_WITH_MULTIPLY does
not suggest their overflow-checking array helpers for allocations such
as vmalloc(n * sizeof(*p)).

Extend the check to suggest vmalloc_array() and vcalloc(). Unlike the
k/kv object helpers, these functions take a count and a size, so keep
the sizeof expression when generating a fix. Use the original source
for both factors to preserve strings and comments masked in the parsed
statement, and leave the fix alone if another check has changed the line.

Also include the v prefix in ALLOC_ARRAY_ARGS to flag sizeof as the first
argument of vmalloc_array() and vcalloc().

Link: https://github.com/KSPP/linux/issues/342
Assisted-by: LLM
Signed-off-by: Iurii Trukhin <[email protected]>
---
Changes in v2:
- Preserve strings and comments in both factors by using their original
  source positions, instead of the sanitized statement.
- Preserve allocation casts and skip the fix if another check changed
  the line before this rule ran.
- Acknowledge coding assistance.

v1: 
https://lore.kernel.org/all/CAOx7e9K707oLMN94TC=fbbzyknwacsn-8mdvjsl+n+bbcmw...@mail.gmail.com/

Testing:
- perl -c and checkpatch --strict.
- Allocator regression cases, including factor order, nested expressions,
  strings, escapes, comments, extra factors and multiline statements.
- Compiled and ran 12 generated C expressions to check that the rewritten
  allocations preserve the original size calculation.
- Compared the existing k/kv diagnostics and fixes with the unmodified
  script; checked application to mainline, linux-next and mm-nonmm-unstable.

LLM assistance was used to extend the rule, review the change and develop
and run the regression tests.

 scripts/checkpatch.pl | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 8a7787d228a6..cf2dd0b7c372 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -7355,17 +7355,22 @@ sub process {
                }


-# check for (kv|k)[mz]alloc with multiplies that could be
kmalloc_objs/kvmalloc_objs/kzalloc_objs/kvzalloc_objs
+# check for (kv|k|v)[mz]alloc with multiplies that could be
+# kmalloc_objs/kvmalloc_objs/kzalloc_objs/kvzalloc_objs/vmalloc_array/vcalloc
                if ($perl_version_ok &&
                    defined $stat &&
-                   $stat =~
/^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/)
{
+                   $stat =~
/^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k|v)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*(?:,|\))/)
{
                        my $oldfunc = $3;
                        my $a1 = $4;
                        my $a2 = $10;
+                       my ($alloc_start, $a1_start, $a1_end, $a2_start, 
$a2_end) =
+                          ($-[3], $-[4], $+[4], $-[10], $+[10]);
                        my $newfunc = "kmalloc_objs";
                        $newfunc = "kvmalloc_objs" if ($oldfunc eq "kvmalloc");
                        $newfunc = "kvzalloc_objs" if ($oldfunc eq "kvzalloc");
                        $newfunc = "kzalloc_objs" if ($oldfunc eq "kzalloc");
+                       $newfunc = "vmalloc_array" if ($oldfunc eq "vmalloc");
+                       $newfunc = "vcalloc" if ($oldfunc eq "vzalloc");
                        my $r1 = $a1;
                        my $r2 = $a2;
                        if ($a1 =~ /^sizeof\s*\S/) {
@@ -7381,9 +7386,20 @@ sub process {
                                         "Prefer $newfunc over $oldfunc with 
multiply\n" . $herectx) &&
                                    $cnt == 1 &&
                                    $fix) {
-                                       my $sized = trim($r2);
-                                       $sized =~ 
s/^sizeof\s*\S\(?([^\)]*)\)?$/$1/;
-                                       $fixed[$fixlinenr] =~
s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1
. ' = ' . "$newfunc(" . $sized . ', ' . trim($r1)/e;
+                                       if ($oldfunc =~ /^v/) {
+                                               # Preserve strings and comments 
masked in $stat.
+                                               if ($fixed[$fixlinenr] eq 
$rawline) {
+                                                       my $raw1 = 
substr($rawline, $a1_start, $a1_end - $a1_start);
+                                                       my $raw2 = 
substr($rawline, $a2_start, $a2_end - $a2_start);
+                                                       ($raw1, $raw2) = 
($raw2, $raw1) if ($a1 =~ /^sizeof\s*\S/);
+                                                       
substr($fixed[$fixlinenr], $alloc_start, $a2_end - $alloc_start,
+                                                              "$newfunc(" . 
trim($raw1) . ', ' . trim($raw2));
+                                               }
+                                       } else {
+                                               my $sized = trim($r2);
+                                               $sized =~ 
s/^sizeof\s*\S\(?([^\)]*)\)?$/$1/;
+                                               $fixed[$fixlinenr] =~
s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1
. ' = ' . "$newfunc(" . $sized . ', ' . trim($r1)/e;
+                                       }
                                }
                        }
                }
@@ -7397,7 +7413,7 @@ sub process {
                }

 # check for alloc argument mismatch
-               if ($line =~
/\b((?:devm_)?((?:k|kv)?(calloc|malloc_array)(?:_node)?))\s*\(\s*sizeof\b/)
{
+               if ($line =~
/\b((?:devm_)?((?:k|kv|v)?(calloc|malloc_array)(?:_node)?))\s*\(\s*sizeof\b/)
{
                        WARN("ALLOC_ARRAY_ARGS",
                             "$1 uses number as first arg, sizeof is generally 
wrong\n" .
$herecurr);
                }

base-commit: 4056b3c9d3c92ea45955641dbafeb9b960e146ef
-- 
2.56.0



On Wed, 14 Oct 2026 20:20:06 -0400, Iurii Trukhin <[email protected]> wrote:
> vmalloc() and vzalloc() have 2-factor multiplication argument forms
> vmalloc_array() and vcalloc() in <linux/vmalloc.h> that protect against
> integer overflow during size calculation.
>
> Commit 070580b0b174 ("checkpatch: Suggest kmalloc_obj family for sizeof
> allocations") updated ALLOC_WITH_MULTIPLY for the (kv|k)[mz]alloc()
> family to suggest kmalloc_objs(type, count, gfp), while vmalloc_array()
> and vcalloc() retain the (count, size) signature.
>
> Extend ALLOC_WITH_MULTIPLY to warn when vmalloc() or vzalloc() is called
> with a 2-factor sizeof() multiplication and suggest vmalloc_array() or
> vcalloc() respectively, including --fix support. Also include the 'v'
> prefix in ALLOC_ARRAY_ARGS so swapped sizeof() first arguments to
> vmalloc_array() and vcalloc() are flagged.
>
> Link: https://github.com/KSPP/linux/issues/342
> Signed-off-by: Iurii Trukhin <[email protected]>
> ---
>  scripts/checkpatch.pl | 19 +++++++++++++------
>  1 file changed, 13 insertions(+), 6 deletions(-)
>
> diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
> index 8a7787d228a6..651a46f1fc40 100755
> --- a/scripts/checkpatch.pl
> +++ b/scripts/checkpatch.pl
> @@ -7355,10 +7355,11 @@ sub process {
>               }
>
>
> -# check for (kv|k)[mz]alloc with multiplies that could be 
> kmalloc_objs/kvmalloc_objs/kzalloc_objs/kvzalloc_objs
> +# check for (kv|k|v)[mz]alloc with multiplies that could be
> +# kmalloc_objs/kvmalloc_objs/kzalloc_objs/kvzalloc_objs/vmalloc_array/vcalloc
>               if ($perl_version_ok &&
>                   defined $stat &&
> -                 $stat =~ 
> /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/)
>  {
> +                 $stat =~ 
> /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k|v)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*(?:,|\))/)
>  {
>                       my $oldfunc = $3;
>                       my $a1 = $4;
>                       my $a2 = $10;
> @@ -7366,6 +7367,8 @@ sub process {
>                       $newfunc = "kvmalloc_objs" if ($oldfunc eq "kvmalloc");
>                       $newfunc = "kvzalloc_objs" if ($oldfunc eq "kvzalloc");
>                       $newfunc = "kzalloc_objs" if ($oldfunc eq "kzalloc");
> +                     $newfunc = "vmalloc_array" if ($oldfunc eq "vmalloc");
> +                     $newfunc = "vcalloc" if ($oldfunc eq "vzalloc");
>                       my $r1 = $a1;
>                       my $r2 = $a2;
>                       if ($a1 =~ /^sizeof\s*\S/) {
> @@ -7381,9 +7384,13 @@ sub process {
>                                        "Prefer $newfunc over $oldfunc with 
> multiply\n" . $herectx) &&
>                                   $cnt == 1 &&
>                                   $fix) {
> -                                     my $sized = trim($r2);
> -                                     $sized =~ 
> s/^sizeof\s*\S\(?([^\)]*)\)?$/$1/;
> -                                     $fixed[$fixlinenr] =~ 
> s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1
>  . ' = ' . "$newfunc(" . $sized . ', ' . trim($r1)/e;
> +                                     if ($oldfunc =~ /^v/) {
> +                                             $fixed[$fixlinenr] =~ 
> s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(v[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1
>  . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e;
> +                                     } else {
> +                                             my $sized = trim($r2);
> +                                             $sized =~ 
> s/^sizeof\s*\S\(?([^\)]*)\)?$/$1/;
> +                                             $fixed[$fixlinenr] =~ 
> s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1
>  . ' = ' . "$newfunc(" . $sized . ', ' . trim($r1)/e;
> +                                     }
>                               }
>                       }
>               }
> @@ -7397,7 +7404,7 @@ sub process {
>               }
>
>  # check for alloc argument mismatch
> -             if ($line =~ 
> /\b((?:devm_)?((?:k|kv)?(calloc|malloc_array)(?:_node)?))\s*\(\s*sizeof\b/) {
> +             if ($line =~ 
> /\b((?:devm_)?((?:k|kv|v)?(calloc|malloc_array)(?:_node)?))\s*\(\s*sizeof\b/) 
> {
>                       WARN("ALLOC_ARRAY_ARGS",
>                            "$1 uses number as first arg, sizeof is generally 
> wrong\n" . $herecurr);
>               }
> --
> 2.56.0

Reply via email to