From: Alejandro Colomar <alx@kernel.org>
To: LKML <linux-kernel@vger.kernel.org>, Kees Cook <kees@kernel.org>
Cc: corbet@lwn.net, serge@hallyn.com, Martin Uecker <uecker@tugraz.at>
Subject: Re: kalloc_objs() may not be as safe as it seems
Date: Mon, 16 Mar 2026 19:35:12 +0100 [thread overview]
Message-ID: <abhNL1wbFajcHZXC@devuan> (raw)
In-Reply-To: <abhGS0n_RsUG97Ni@devuan>
[-- Attachment #1: Type: text/plain, Size: 3885 bytes --]
On 2026-03-16T19:33:37+0100, Alejandro Colomar wrote:
> Hi Kees,
>
> I just learnt about kalloc_objs() et al. from
> <https://lwn.net/Articles/1062856/>.
>
> ptr = kmalloc_obj(*ptr);
> ptr = kmalloc_objs(*ptr, n);
>
> This resembles a lot the macro we have in shadow-utils, malloc_T(),
> which would be used as (to resemble the above):
>
> ptr = malloc_T(1, typeof(*ptr)); // But we'd really pass the type
> ptr = malloc_T(n, typeof(*ptr)); // But we'd really pass the type
>
> But I've noticed some design mistakes that make it not as safe as it
> seems.
>
> Default arguments
> ~~~~~~~~~~~~~~~~~
>
> I tend to think it's simpler to have a single API that works for both
> 1 element and multiple elements. The special-casing of 1 element seems
> unnecessary, and having a literal 1 works just fine.
>
> I think the combination of having the macros be variadic (for gfp) with
> having two very similar APIs that differ in number of arguments, and
> all those arguments being integer types, is prone to errors. Consider
> the case where one would accidentally write
>
> ptr = kmalloc_obj(*ptr, n); // Bogus
> instead of
> ptr = kmalloc_objs(*ptr, n);
>
> The compiler wouldn't realize at all. That's a strong argument in
> favour of having default arguments be required to be explicit, with an
> empty argument:
>
> ptr = kmalloc_obj(*ptr,);
> ptr = kmalloc_objs(*ptr, n,);
>
> I know you (and Linus too, FWIW) have previously claimed that it looks
> weird to the eye. But I'm pretty sure you could get used to it. That's
> certainly going to be safer.
>
> With mandatory empty arguments, the compiler would easily distinguish
> mistakes like the one above.
>
> Type safety
> ~~~~~~~~~~~
>
> Apart from the issues with the above, the ability to pass a variable
> instead of a type name is also a bad choice. In shadow-utils, we
> require a type name, and a variable is rejected. We implement that with
> the typeas() macro:
>
> #define typeas(T) typeof((T){0})
>
> This macro works exactly like typeof(), but it requires that the input
> is also a type. Passing a variable is a syntax error. We implement
> malloc_T() with it:
>
> // malloc_T - malloc type-safe
> #define malloc_T_(n, T) \
> ({ \
> (typeas(T) *){reallocarray(n, sizeof(T))}; \
I meant the following:
(typeas(T) *){reallocarray(NULL, n, sizeof(T))};
I had the issue while pasting and adapting for simplicity. The original
code is correct, of course.
> })
>
> which is used as (taking some arbitrary examples from shadow-utils):
>
> lp = xmalloc_T(1, struct link_name);
> targs = xmalloc_T(n_args + 3, char *);
>
> Some reasons for passing a type name instead of a variable are:
>
> - It allows grepping for all allocations of a given type.
> - It adds readability. It's similar to declaring variables with some
> explicit type, vs. using 'auto' (__auto_type) everywhere.
>
> But there's also a safety aspect. Consider we want to allocate an array
> of 42 ints. And consider the programmer accidentally swaps arguments.
>
> int *p = malloc_T(int, 42); // syntax error
> int *p = malloc_T(42, int);
> vs
> int *p = kmalloc_objs(*p, 42);
> int *p = kmalloc_objs(42, *p); // Bogus
>
> The latter is dereferencing an uninitialized pointer. If for some
> reason the pointer had a value before this call, you'd be allocating as
> many elements as *p says, which would be bogus, and since typeof(42) is
> the same as typeof(*p), the return type would be valid, so this would
> still compile.
>
>
> Have a lovely night!
> Alex
>
> --
> <https://www.alejandro-colomar.es>
--
<https://www.alejandro-colomar.es>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
next prev parent reply other threads:[~2026-03-16 18:35 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-16 18:33 kalloc_objs() may not be as safe as it seems Alejandro Colomar
2026-03-16 18:35 ` Alejandro Colomar [this message]
2026-03-17 21:14 ` Kees Cook
2026-03-17 21:40 ` Alejandro Colomar
2026-03-18 16:33 ` Alejandro Colomar
2026-03-19 20:12 ` Kees Cook
2026-03-19 21:18 ` Alejandro Colomar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=abhNL1wbFajcHZXC@devuan \
--to=alx@kernel.org \
--cc=corbet@lwn.net \
--cc=kees@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=serge@hallyn.com \
--cc=uecker@tugraz.at \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox