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

