Skip to content

Commit 958f7fb

Browse files
committed
Merge tag 'kmalloc_obj-v7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull kmalloc_obj updates from Kees Cook: "Introduce the kmalloc_obj* family of APIs for switching to type-based kmalloc allocations, away from purely size-based allocations. Discussed on lkml, with you, and at Linux Plumbers. It's been in -next for the entire dev cycle. Before the merge window closes, I'd like to send the treewide change (generated from the Coccinelle script included here), which mechanically converts almost 20k callsites from kmalloc* to kmalloc_obj*: 8007 files changed, 19980 insertions(+), 20838 deletions(-) This change needed fixes for mismatched types (since now the return type from allocations is a pointer to the requested type, not "void *"), and I've been fixing these over the last 4 releases. These fixes have mostly been trivial mismatches with const qualifiers or accidentally identical sizes (e.g. same object size: "struct kvec" vs "struct iovec", or differing pointers to pointers), but I did catch one case of too-small allocation. Summary: - Introduce kmalloc_obj*() family of type-based allocator APIs - checkpatch: Suggest kmalloc_obj family for sizeof allocations - coccinelle: Add kmalloc_objs conversion script" * tag 'kmalloc_obj-v7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: coccinelle: Add kmalloc_objs conversion script slab: Introduce kmalloc_flex() and family compiler_types: Introduce __flex_counter() and family checkpatch: Suggest kmalloc_obj family for sizeof allocations slab: Introduce kmalloc_obj() and family
2 parents 85f24b0 + bdc5071 commit 958f7fb

6 files changed

Lines changed: 367 additions & 6 deletions

File tree

Documentation/process/deprecated.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,3 +372,34 @@ The helper must be used::
372372
DECLARE_FLEX_ARRAY(struct type2, two);
373373
};
374374
};
375+
376+
Open-coded kmalloc assignments for struct objects
377+
-------------------------------------------------
378+
Performing open-coded kmalloc()-family allocation assignments prevents
379+
the kernel (and compiler) from being able to examine the type of the
380+
variable being assigned, which limits any related introspection that
381+
may help with alignment, wrap-around, or additional hardening. The
382+
kmalloc_obj()-family of macros provide this introspection, which can be
383+
used for the common code patterns for single, array, and flexible object
384+
allocations. For example, these open coded assignments::
385+
386+
ptr = kmalloc(sizeof(*ptr), gfp);
387+
ptr = kzalloc(sizeof(*ptr), gfp);
388+
ptr = kmalloc_array(count, sizeof(*ptr), gfp);
389+
ptr = kcalloc(count, sizeof(*ptr), gfp);
390+
ptr = kmalloc(struct_size(ptr, flex_member, count), gfp);
391+
ptr = kmalloc(sizeof(struct foo, gfp);
392+
393+
become, respectively::
394+
395+
ptr = kmalloc_obj(*ptr, gfp);
396+
ptr = kzalloc_obj(*ptr, gfp);
397+
ptr = kmalloc_objs(*ptr, count, gfp);
398+
ptr = kzalloc_objs(*ptr, count, gfp);
399+
ptr = kmalloc_flex(*ptr, flex_member, count, gfp);
400+
__auto_type ptr = kmalloc_obj(struct foo, gfp);
401+
402+
If `ptr->flex_member` is annotated with __counted_by(), the allocation
403+
will automatically fail if `count` is larger than the maximum
404+
representable value that can be stored in the counter member associated
405+
with `flex_member`.

include/linux/compiler_types.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,37 @@ struct ftrace_likely_data {
551551
#define __annotated(var, attr) __builtin_has_attribute(var, attr)
552552
#endif
553553

554+
/*
555+
* Optional: only supported since gcc >= 15, clang >= 19
556+
*
557+
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fcounted_005fby_005fref
558+
* clang: https://clang.llvm.org/docs/LanguageExtensions.html#builtin-counted-by-ref
559+
*/
560+
#if __has_builtin(__builtin_counted_by_ref)
561+
/**
562+
* __flex_counter() - Get pointer to counter member for the given
563+
* flexible array, if it was annotated with __counted_by()
564+
* @FAM: Pointer to flexible array member of an addressable struct instance
565+
*
566+
* For example, with:
567+
*
568+
* struct foo {
569+
* int counter;
570+
* short array[] __counted_by(counter);
571+
* } *p;
572+
*
573+
* __flex_counter(p->array) will resolve to &p->counter.
574+
*
575+
* Note that Clang may not allow this to be assigned to a separate
576+
* variable; it must be used directly.
577+
*
578+
* If p->array is unannotated, this returns (void *)NULL.
579+
*/
580+
#define __flex_counter(FAM) __builtin_counted_by_ref(FAM)
581+
#else
582+
#define __flex_counter(FAM) ((void *)NULL)
583+
#endif
584+
554585
/*
555586
* Some versions of gcc do not mark 'asm goto' volatile:
556587
*

include/linux/overflow.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,4 +552,46 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend)
552552
(__member_size((name)->array) / sizeof(*(name)->array) + \
553553
__must_be_array((name)->array))
554554

555+
/**
556+
* typeof_flex_counter() - Return the type of the counter variable of a given
557+
* flexible array member annotated by __counted_by().
558+
* @FAM: Instance of flexible array member within a given struct.
559+
*
560+
* Returns: "size_t" if no annotation exists.
561+
*/
562+
#define typeof_flex_counter(FAM) \
563+
typeof(_Generic(__flex_counter(FAM), \
564+
void *: (size_t)0, \
565+
default: *__flex_counter(FAM)))
566+
567+
/**
568+
* overflows_flex_counter_type() - Check if the counter associated with the
569+
* given flexible array member can represent
570+
* a value.
571+
* @TYPE: Type of the struct that contains the @FAM.
572+
* @FAM: Member name of the FAM within @TYPE.
573+
* @COUNT: Value to check against the __counted_by annotated @FAM's counter.
574+
*
575+
* Returns: true if @COUNT can be represented in the @FAM's counter. When
576+
* @FAM is not annotated with __counted_by(), always returns true.
577+
*/
578+
#define overflows_flex_counter_type(TYPE, FAM, COUNT) \
579+
(!overflows_type(COUNT, typeof_flex_counter(((TYPE *)NULL)->FAM)))
580+
581+
/**
582+
* __set_flex_counter() - Set the counter associated with the given flexible
583+
* array member that has been annoated by __counted_by().
584+
* @FAM: Instance of flexible array member within a given struct.
585+
* @COUNT: Value to store to the __counted_by annotated @FAM_PTR's counter.
586+
*
587+
* This is a no-op if no annotation exists. Count needs to be checked with
588+
* overflows_flex_counter_type() before using this function.
589+
*/
590+
#define __set_flex_counter(FAM, COUNT) \
591+
({ \
592+
*_Generic(__flex_counter(FAM), \
593+
void *: &(size_t){ 0 }, \
594+
default: __flex_counter(FAM)) = (COUNT); \
595+
})
596+
555597
#endif /* __LINUX_OVERFLOW_H */

include/linux/slab.h

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#ifndef _LINUX_SLAB_H
1313
#define _LINUX_SLAB_H
1414

15+
#include <linux/bug.h>
1516
#include <linux/cache.h>
1617
#include <linux/gfp.h>
1718
#include <linux/overflow.h>
@@ -965,6 +966,111 @@ static __always_inline __alloc_size(1) void *kmalloc_noprof(size_t size, gfp_t f
965966
void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node);
966967
#define kmalloc_nolock(...) alloc_hooks(kmalloc_nolock_noprof(__VA_ARGS__))
967968

969+
/**
970+
* __alloc_objs - Allocate objects of a given type using
971+
* @KMALLOC: which size-based kmalloc wrapper to allocate with.
972+
* @GFP: GFP flags for the allocation.
973+
* @TYPE: type to allocate space for.
974+
* @COUNT: how many @TYPE objects to allocate.
975+
*
976+
* Returns: Newly allocated pointer to (first) @TYPE of @COUNT-many
977+
* allocated @TYPE objects, or NULL on failure.
978+
*/
979+
#define __alloc_objs(KMALLOC, GFP, TYPE, COUNT) \
980+
({ \
981+
const size_t __obj_size = size_mul(sizeof(TYPE), COUNT); \
982+
(TYPE *)KMALLOC(__obj_size, GFP); \
983+
})
984+
985+
/**
986+
* __alloc_flex - Allocate an object that has a trailing flexible array
987+
* @KMALLOC: kmalloc wrapper function to use for allocation.
988+
* @GFP: GFP flags for the allocation.
989+
* @TYPE: type of structure to allocate space for.
990+
* @FAM: The name of the flexible array member of @TYPE structure.
991+
* @COUNT: how many @FAM elements to allocate space for.
992+
*
993+
* Returns: Newly allocated pointer to @TYPE with @COUNT-many trailing
994+
* @FAM elements, or NULL on failure or if @COUNT cannot be represented
995+
* by the member of @TYPE that counts the @FAM elements (annotated via
996+
* __counted_by()).
997+
*/
998+
#define __alloc_flex(KMALLOC, GFP, TYPE, FAM, COUNT) \
999+
({ \
1000+
const size_t __count = (COUNT); \
1001+
const size_t __obj_size = struct_size_t(TYPE, FAM, __count); \
1002+
TYPE *__obj_ptr; \
1003+
if (WARN_ON_ONCE(overflows_flex_counter_type(TYPE, FAM, __count))) \
1004+
__obj_ptr = NULL; \
1005+
else \
1006+
__obj_ptr = KMALLOC(__obj_size, GFP); \
1007+
if (__obj_ptr) \
1008+
__set_flex_counter(__obj_ptr->FAM, __count); \
1009+
__obj_ptr; \
1010+
})
1011+
1012+
/**
1013+
* kmalloc_obj - Allocate a single instance of the given type
1014+
* @VAR_OR_TYPE: Variable or type to allocate.
1015+
* @GFP: GFP flags for the allocation.
1016+
*
1017+
* Returns: newly allocated pointer to a @VAR_OR_TYPE on success, or NULL
1018+
* on failure.
1019+
*/
1020+
#define kmalloc_obj(VAR_OR_TYPE, GFP) \
1021+
__alloc_objs(kmalloc, GFP, typeof(VAR_OR_TYPE), 1)
1022+
1023+
/**
1024+
* kmalloc_objs - Allocate an array of the given type
1025+
* @VAR_OR_TYPE: Variable or type to allocate an array of.
1026+
* @COUNT: How many elements in the array.
1027+
* @GFP: GFP flags for the allocation.
1028+
*
1029+
* Returns: newly allocated pointer to array of @VAR_OR_TYPE on success,
1030+
* or NULL on failure.
1031+
*/
1032+
#define kmalloc_objs(VAR_OR_TYPE, COUNT, GFP) \
1033+
__alloc_objs(kmalloc, GFP, typeof(VAR_OR_TYPE), COUNT)
1034+
1035+
/**
1036+
* kmalloc_flex - Allocate a single instance of the given flexible structure
1037+
* @VAR_OR_TYPE: Variable or type to allocate (with its flex array).
1038+
* @FAM: The name of the flexible array member of the structure.
1039+
* @COUNT: How many flexible array member elements are desired.
1040+
* @GFP: GFP flags for the allocation.
1041+
*
1042+
* Returns: newly allocated pointer to @VAR_OR_TYPE on success, NULL on
1043+
* failure. If @FAM has been annotated with __counted_by(), the allocation
1044+
* will immediately fail if @COUNT is larger than what the type of the
1045+
* struct's counter variable can represent.
1046+
*/
1047+
#define kmalloc_flex(VAR_OR_TYPE, FAM, COUNT, GFP) \
1048+
__alloc_flex(kmalloc, GFP, typeof(VAR_OR_TYPE), FAM, COUNT)
1049+
1050+
/* All kzalloc aliases for kmalloc_(obj|objs|flex). */
1051+
#define kzalloc_obj(P, GFP) \
1052+
__alloc_objs(kzalloc, GFP, typeof(P), 1)
1053+
#define kzalloc_objs(P, COUNT, GFP) \
1054+
__alloc_objs(kzalloc, GFP, typeof(P), COUNT)
1055+
#define kzalloc_flex(P, FAM, COUNT, GFP) \
1056+
__alloc_flex(kzalloc, GFP, typeof(P), FAM, COUNT)
1057+
1058+
/* All kvmalloc aliases for kmalloc_(obj|objs|flex). */
1059+
#define kvmalloc_obj(P, GFP) \
1060+
__alloc_objs(kvmalloc, GFP, typeof(P), 1)
1061+
#define kvmalloc_objs(P, COUNT, GFP) \
1062+
__alloc_objs(kvmalloc, GFP, typeof(P), COUNT)
1063+
#define kvmalloc_flex(P, FAM, COUNT, GFP) \
1064+
__alloc_flex(kvmalloc, GFP, typeof(P), FAM, COUNT)
1065+
1066+
/* All kvzalloc aliases for kmalloc_(obj|objs|flex). */
1067+
#define kvzalloc_obj(P, GFP) \
1068+
__alloc_objs(kvzalloc, GFP, typeof(P), 1)
1069+
#define kvzalloc_objs(P, COUNT, GFP) \
1070+
__alloc_objs(kvzalloc, GFP, typeof(P), COUNT)
1071+
#define kvzalloc_flex(P, FAM, COUNT, GFP) \
1072+
__alloc_flex(kvzalloc, GFP, typeof(P), FAM, COUNT)
1073+
9681074
#define kmem_buckets_alloc(_b, _size, _flags) \
9691075
alloc_hooks(__kmalloc_node_noprof(PASS_BUCKET_PARAMS(_size, _b), _flags, NUMA_NO_NODE))
9701076

scripts/checkpatch.pl

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7260,17 +7260,42 @@ sub process {
72607260
"Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr);
72617261
}
72627262

7263-
# check for (kv|k)[mz]alloc with multiplies that could be kmalloc_array/kvmalloc_array/kvcalloc/kcalloc
7263+
# check for (kv|k)[mz]alloc that could be kmalloc_obj/kvmalloc_obj/kzalloc_obj/kvzalloc_obj
7264+
if ($perl_version_ok &&
7265+
defined $stat &&
7266+
$stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*,/) {
7267+
my $oldfunc = $3;
7268+
my $a1 = $4;
7269+
my $newfunc = "kmalloc_obj";
7270+
$newfunc = "kvmalloc_obj" if ($oldfunc eq "kvmalloc");
7271+
$newfunc = "kvzalloc_obj" if ($oldfunc eq "kvzalloc");
7272+
$newfunc = "kzalloc_obj" if ($oldfunc eq "kzalloc");
7273+
7274+
if ($a1 =~ s/^sizeof\s*\S\(?([^\)]*)\)?$/$1/) {
7275+
my $cnt = statement_rawlines($stat);
7276+
my $herectx = get_stat_here($linenr, $cnt, $here);
7277+
7278+
if (WARN("ALLOC_WITH_SIZEOF",
7279+
"Prefer $newfunc over $oldfunc with sizeof\n" . $herectx) &&
7280+
$cnt == 1 &&
7281+
$fix) {
7282+
$fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*,/$1 = $newfunc($a1,/;
7283+
}
7284+
}
7285+
}
7286+
7287+
7288+
# check for (kv|k)[mz]alloc with multiplies that could be kmalloc_objs/kvmalloc_objs/kzalloc_objs/kvzalloc_objs
72647289
if ($perl_version_ok &&
72657290
defined $stat &&
72667291
$stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) {
72677292
my $oldfunc = $3;
72687293
my $a1 = $4;
72697294
my $a2 = $10;
7270-
my $newfunc = "kmalloc_array";
7271-
$newfunc = "kvmalloc_array" if ($oldfunc eq "kvmalloc");
7272-
$newfunc = "kvcalloc" if ($oldfunc eq "kvzalloc");
7273-
$newfunc = "kcalloc" if ($oldfunc eq "kzalloc");
7295+
my $newfunc = "kmalloc_objs";
7296+
$newfunc = "kvmalloc_objs" if ($oldfunc eq "kvmalloc");
7297+
$newfunc = "kvzalloc_objs" if ($oldfunc eq "kvzalloc");
7298+
$newfunc = "kzalloc_objs" if ($oldfunc eq "kzalloc");
72747299
my $r1 = $a1;
72757300
my $r2 = $a2;
72767301
if ($a1 =~ /^sizeof\s*\S/) {
@@ -7286,7 +7311,9 @@ sub process {
72867311
"Prefer $newfunc over $oldfunc with multiply\n" . $herectx) &&
72877312
$cnt == 1 &&
72887313
$fix) {
7289-
$fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e;
7314+
my $sized = trim($r2);
7315+
$sized =~ s/^sizeof\s*\S\(?([^\)]*)\)?$/$1/;
7316+
$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;
72907317
}
72917318
}
72927319
}

0 commit comments

Comments
 (0)