From nobody Sat Jul 25 06:09:14 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5CD6D2EEE73; Fri, 17 Jul 2026 04:46:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263612; cv=none; b=n0rY2mb1rK+3dLkaelw5LjQVe3BwHPXetzll/q4iMCPkvn9LRs77c0tmqR8sxITxgbXhV+SlvGa1EkZOIIX/6jeyZoU63jGmF7nQTAnqnpwmLtYBK9tfliCEk+xgRHcopyT6INNR3v3XZNsS0g84mTkLHMME2LttqewlCvytqwc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263612; c=relaxed/simple; bh=LL9mxB2YokLqPzPhU7M3HSqO4I8f0exEUzsTZLmPTrA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ekeI3IJ0P4s6nswdsi5ZzghYxT7RnyaPZWES9vyyDTwgDoKNq5mqGOtp28J+Dq0rzVMCcgrgBhN2R4pP+k4iKI2Lbd7F6hi+N5Vjjl9yvCVg9jtIm1yHSr1IVdZQjlyyBiM09lC0+sVjtntwahk0rCiq97ySnPNjf3VAoEbtf4Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=TUmdjypH; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="TUmdjypH" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C1E691F00A3A; Fri, 17 Jul 2026 04:46:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784263611; bh=xatRQMV9MWwD2SSf3DV+2s0Vh+uk8WULL8ox0uG/owo=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=TUmdjypHTjKcSm89Eb6RXEF7U9UBXGIyGumRknvNqsB1VzN9BPrlZ0NrYWORhbVFO pf2iFyx7ymtqL8li82nSH0tT38ovgHaeExHMyCk74NoargES6A7vwbLsBCO2g1Sfcv e8rY74keiUMfULeJT+La13ano3SZBSbcnOOmaO710qkDr37Veo9TfN5oq1B3Tl+/Vw Z1+nqbZAht017t3LMXKnlzT5BagNf9qIMiMIiMMtOCMuQ8RhA2c95Nc/VkZvcTnji9 bfXE3caF7Gb8JTV0LCKx4q29QITv+TLa0w05MkdooUzmDtX0h4+QIOpdBCn3eEZ4fN c0nTmWvGLPlwA== From: Eric Biggers To: stable@vger.kernel.org Cc: linux-hardening@vger.kernel.org, linux-fscrypt@vger.kernel.org, linux-kernel@vger.kernel.org, Kees Cook , Vlastimil Babka , Eric Biggers Subject: [PATCH 6.12 1/7] slab: Introduce kmalloc_obj() and family Date: Thu, 16 Jul 2026 21:42:57 -0700 Message-ID: <20260717044303.425265-2-ebiggers@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260717044303.425265-1-ebiggers@kernel.org> References: <20260717044303.425265-1-ebiggers@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Kees Cook commit 2932ba8d9c99875b98c951d9d3fd6d651d35df3a upstream. Introduce type-aware kmalloc-family helpers to replace the common idioms for single object and arrays of objects allocation: ptr =3D kmalloc(sizeof(*ptr), gfp); ptr =3D kmalloc(sizeof(struct some_obj_name), gfp); ptr =3D kzalloc(sizeof(*ptr), gfp); ptr =3D kmalloc_array(count, sizeof(*ptr), gfp); ptr =3D kcalloc(count, sizeof(*ptr), gfp); These become, respectively: ptr =3D kmalloc_obj(*ptr, gfp); ptr =3D kmalloc_obj(*ptr, gfp); ptr =3D kzalloc_obj(*ptr, gfp); ptr =3D kmalloc_objs(*ptr, count, gfp); ptr =3D kzalloc_objs(*ptr, count, gfp); Beyond the other benefits outlined below, the primary ergonomic benefit is the elimination of needing "sizeof" nor the type name, and the enforcement of assignment types (they do not return "void *", but rather a pointer to the type of the first argument). The type name _can_ be used, though, in the case where an assignment is indirect (e.g. via "return"). This additionally allows[1] variables to be declared via __auto_type: __auto_type ptr =3D kmalloc_obj(struct foo, gfp); Internal introspection of the allocated type now becomes possible, allowing for future alignment-aware choices to be made by the allocator and future hardening work that can be type sensitive. For example, adding __alignof(*ptr) as an argument to the internal allocators so that appropriate/efficient alignment choices can be made, or being able to correctly choose per-allocation offset randomization within a bucket that does not break alignment requirements. Link: https://lore.kernel.org/all/CAHk-=3DwiCOTW5UftUrAnvJkr6769D29tF7Of79g= UjdQHS_TkF5A@mail.gmail.com/ [1] Acked-by: Vlastimil Babka Link: https://patch.msgid.link/20251203233036.3212363-1-kees@kernel.org Signed-off-by: Kees Cook Signed-off-by: Eric Biggers --- Documentation/process/deprecated.rst | 24 ++++++++++++ include/linux/slab.h | 58 ++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/Documentation/process/deprecated.rst b/Documentation/process/d= eprecated.rst index 1f7f3e6c9cda..91c628fa2d59 100644 --- a/Documentation/process/deprecated.rst +++ b/Documentation/process/deprecated.rst @@ -372,3 +372,27 @@ The helper must be used:: DECLARE_FLEX_ARRAY(struct type2, two); }; }; + +Open-coded kmalloc assignments for struct objects +------------------------------------------------- +Performing open-coded kmalloc()-family allocation assignments prevents +the kernel (and compiler) from being able to examine the type of the +variable being assigned, which limits any related introspection that +may help with alignment, wrap-around, or additional hardening. The +kmalloc_obj()-family of macros provide this introspection, which can be +used for the common code patterns for single, array, and flexible object +allocations. For example, these open coded assignments:: + + ptr =3D kmalloc(sizeof(*ptr), gfp); + ptr =3D kzalloc(sizeof(*ptr), gfp); + ptr =3D kmalloc_array(count, sizeof(*ptr), gfp); + ptr =3D kcalloc(count, sizeof(*ptr), gfp); + ptr =3D kmalloc(sizeof(struct foo, gfp); + +become, respectively:: + + ptr =3D kmalloc_obj(*ptr, gfp); + ptr =3D kzalloc_obj(*ptr, gfp); + ptr =3D kmalloc_objs(*ptr, count, gfp); + ptr =3D kzalloc_objs(*ptr, count, gfp); + __auto_type ptr =3D kmalloc_obj(struct foo, gfp); diff --git a/include/linux/slab.h b/include/linux/slab.h index b35e2db7eb0e..718160551190 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -12,6 +12,7 @@ #ifndef _LINUX_SLAB_H #define _LINUX_SLAB_H =20 +#include #include #include #include @@ -883,6 +884,63 @@ static __always_inline __alloc_size(1) void *kmalloc_n= oprof(size_t size, gfp_t f } #define kmalloc(...) alloc_hooks(kmalloc_noprof(__VA_ARGS__)) =20 +/** + * __alloc_objs - Allocate objects of a given type using + * @KMALLOC: which size-based kmalloc wrapper to allocate with. + * @GFP: GFP flags for the allocation. + * @TYPE: type to allocate space for. + * @COUNT: how many @TYPE objects to allocate. + * + * Returns: Newly allocated pointer to (first) @TYPE of @COUNT-many + * allocated @TYPE objects, or NULL on failure. + */ +#define __alloc_objs(KMALLOC, GFP, TYPE, COUNT) \ +({ \ + const size_t __obj_size =3D size_mul(sizeof(TYPE), COUNT); \ + (TYPE *)KMALLOC(__obj_size, GFP); \ +}) + +/** + * kmalloc_obj - Allocate a single instance of the given type + * @VAR_OR_TYPE: Variable or type to allocate. + * @GFP: GFP flags for the allocation. + * + * Returns: newly allocated pointer to a @VAR_OR_TYPE on success, or NULL + * on failure. + */ +#define kmalloc_obj(VAR_OR_TYPE, GFP) \ + __alloc_objs(kmalloc, GFP, typeof(VAR_OR_TYPE), 1) + +/** + * kmalloc_objs - Allocate an array of the given type + * @VAR_OR_TYPE: Variable or type to allocate an array of. + * @COUNT: How many elements in the array. + * @GFP: GFP flags for the allocation. + * + * Returns: newly allocated pointer to array of @VAR_OR_TYPE on success, + * or NULL on failure. + */ +#define kmalloc_objs(VAR_OR_TYPE, COUNT, GFP) \ + __alloc_objs(kmalloc, GFP, typeof(VAR_OR_TYPE), COUNT) + +/* All kzalloc aliases for kmalloc_(obj|objs|flex). */ +#define kzalloc_obj(P, GFP) \ + __alloc_objs(kzalloc, GFP, typeof(P), 1) +#define kzalloc_objs(P, COUNT, GFP) \ + __alloc_objs(kzalloc, GFP, typeof(P), COUNT) + +/* All kvmalloc aliases for kmalloc_(obj|objs|flex). */ +#define kvmalloc_obj(P, GFP) \ + __alloc_objs(kvmalloc, GFP, typeof(P), 1) +#define kvmalloc_objs(P, COUNT, GFP) \ + __alloc_objs(kvmalloc, GFP, typeof(P), COUNT) + +/* All kvzalloc aliases for kmalloc_(obj|objs|flex). */ +#define kvzalloc_obj(P, GFP) \ + __alloc_objs(kvzalloc, GFP, typeof(P), 1) +#define kvzalloc_objs(P, COUNT, GFP) \ + __alloc_objs(kvzalloc, GFP, typeof(P), COUNT) + #define kmem_buckets_alloc(_b, _size, _flags) \ alloc_hooks(__kmalloc_node_noprof(PASS_BUCKET_PARAMS(_size, _b), _flags, = NUMA_NO_NODE)) =20 --=20 2.55.0 From nobody Sat Jul 25 06:09:14 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 72B2B2F12B3; Fri, 17 Jul 2026 04:46:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263612; cv=none; b=YyFrq+Wr5H2k4BnJLtHyjzRNPRGhGHPf4T1tB7iojwFzZgoleT4fz1iGZBrXr+cRaR1Y2UIIRrZ+zk4sDYk4hEFVtOizw5Si1GdjvIfDMJvDZL70alVRPrrpQxrkA/zbkc+Mq1A1ZIC7FagqSG1LgDy8UKORz3rEAWvh8FPcQCM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263612; c=relaxed/simple; bh=yIcHfM3xjQiQ61CxfZmKJHpZuG42hvfBCB5Y6uCfWq4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=T7GCGd+hRDL7e1EzeeKwe2flxLslFv0r/wHCdsmnZJvPKQugErvkVIxg6mDXhX+mL6KAc6NgG4qVeSeCY824JMKRBJMm52IzZ1RMD1dzeKY/l7WsUM24MsZhM0WIu1f8C9HdtS8cPra7U6pfjlEiuxR7oYWHtEv68W1CDd+kDtk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=NMInv6v2; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="NMInv6v2" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 258131F00A3F; Fri, 17 Jul 2026 04:46:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784263611; bh=fNeB/Y/cqOjzXec3adTFGQCU+s51eXxOcM2XPEZSHgQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=NMInv6v2AGHF+xoWPgjhtTQ7MTgY9oRUlxZga8zhPVbF3qJvv4/WpFNJLv2ApZdvg xaoqEiy3WtR5LnCfxvSTPONnGtY1U3U14lBR58qN2RbvAcOP87erg1bMzvR+2bY0h3 cwSNIsKuccrrQ4uxXt4/QM3GMq3qqxoUUlN8HNnis5AfhAfnSBvRDeBaiZw7knCxhp 0HukxtRUVQF/QVQ2batBXVVJ8W8D8DsbW1EHxDhJjUv6vs6w8NjAN92ZqRpU2Hx40k 2/F2m4hFPA8XY9m171bkBA+Ke2TfbAUEF9eBra0dKAruBfDgAjcIpvcsznq3pO94rY MMKGel3mp8MFQ== From: Eric Biggers To: stable@vger.kernel.org Cc: linux-hardening@vger.kernel.org, linux-fscrypt@vger.kernel.org, linux-kernel@vger.kernel.org, Kees Cook , Vlastimil Babka , Eric Biggers Subject: [PATCH 6.12 2/7] slab: Introduce kmalloc_flex() and family Date: Thu, 16 Jul 2026 21:42:58 -0700 Message-ID: <20260717044303.425265-3-ebiggers@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260717044303.425265-1-ebiggers@kernel.org> References: <20260717044303.425265-1-ebiggers@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Kees Cook commit e4c8b46b924eb8de66c6f0accc9cdd0c2e8fa23b upstream. As done for kmalloc_obj*(), introduce a type-aware allocator for flexible arrays, which may also have "counted_by" annotations: ptr =3D kmalloc(struct_size(ptr, flex_member, count), gfp); becomes: ptr =3D kmalloc_flex(*ptr, flex_member, count, gfp); The internal use of __flex_counter() allows for automatically setting the counter member of a struct's flexible array member when it has been annotated with __counted_by(), avoiding any missed early size initializations while __counted_by() annotations are added to the kernel. Additionally, this also checks for "too large" allocations based on the type size of the counter variable. For example: if (count > type_max(ptr->flex_counter)) fail...; size =3D struct_size(ptr, flex_member, count); ptr =3D kmalloc(size, gfp); if (!ptr) fail...; ptr->flex_counter =3D count; becomes (n.b. unchanged from earlier example): ptr =3D kmalloc_flex(*ptr, flex_member, count, gfp); if (!ptr) fail...; ptr->flex_counter =3D count; Note that manual initialization of the flexible array counter is still required (at some point) after allocation as not all compiler versions support the __counted_by annotation yet. But doing it internally makes sure they cannot be missed when __counted_by _is_ available, meaning that the bounds checker will not trip due to the lack of "early enough" initializations that used to work before enabling the stricter bounds checking. For example: ptr =3D kmalloc_flex(*ptr, flex_member, count, gfp); fill(ptr->flex, count); ptr->flex_count =3D count; This works correctly before adding a __counted_by annotation (since nothing is checking ptr->flex accesses against ptr->flex_count). After adding the annotation, the bounds sanitizer would trip during fill() because ptr->flex_count wasn't set yet. But with kmalloc_flex() setting ptr->flex_count internally at allocation time, the existing code works without needing to move the ptr->flex_count assignment before the call to fill(). (This has been a stumbling block for __counted_by adoption.) Link: https://patch.msgid.link/20251203233036.3212363-4-kees@kernel.org Acked-by: Vlastimil Babka Signed-off-by: Kees Cook [Backport-notes: Removed the actual flex counter handling. That's a new feature, which isn't necessary for just adding the new allocation APIs to get backports to apply cleanly. Also, the allocation-time overflow check in the upstream commit was reverted upstream.] Signed-off-by: Eric Biggers --- Documentation/process/deprecated.rst | 7 +++++ include/linux/slab.h | 42 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/Documentation/process/deprecated.rst b/Documentation/process/d= eprecated.rst index 91c628fa2d59..fed56864d036 100644 --- a/Documentation/process/deprecated.rst +++ b/Documentation/process/deprecated.rst @@ -387,6 +387,7 @@ allocations. For example, these open coded assignments:: ptr =3D kzalloc(sizeof(*ptr), gfp); ptr =3D kmalloc_array(count, sizeof(*ptr), gfp); ptr =3D kcalloc(count, sizeof(*ptr), gfp); + ptr =3D kmalloc(struct_size(ptr, flex_member, count), gfp); ptr =3D kmalloc(sizeof(struct foo, gfp); =20 become, respectively:: @@ -395,4 +396,10 @@ become, respectively:: ptr =3D kzalloc_obj(*ptr, gfp); ptr =3D kmalloc_objs(*ptr, count, gfp); ptr =3D kzalloc_objs(*ptr, count, gfp); + ptr =3D kmalloc_flex(*ptr, flex_member, count, gfp); __auto_type ptr =3D kmalloc_obj(struct foo, gfp); + +If `ptr->flex_member` is annotated with __counted_by(), the allocation +will automatically fail if `count` is larger than the maximum +representable value that can be stored in the counter member associated +with `flex_member`. diff --git a/include/linux/slab.h b/include/linux/slab.h index 718160551190..d54d44b053dd 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -900,6 +900,27 @@ static __always_inline __alloc_size(1) void *kmalloc_n= oprof(size_t size, gfp_t f (TYPE *)KMALLOC(__obj_size, GFP); \ }) =20 +/** + * __alloc_flex - Allocate an object that has a trailing flexible array + * @KMALLOC: kmalloc wrapper function to use for allocation. + * @GFP: GFP flags for the allocation. + * @TYPE: type of structure to allocate space for. + * @FAM: The name of the flexible array member of @TYPE structure. + * @COUNT: how many @FAM elements to allocate space for. + * + * Returns: Newly allocated pointer to @TYPE with @COUNT-many trailing + * @FAM elements, or NULL on failure or if @COUNT cannot be represented + * by the member of @TYPE that counts the @FAM elements (annotated via + * __counted_by()). + */ +#define __alloc_flex(KMALLOC, GFP, TYPE, FAM, COUNT) \ +({ \ + const size_t __count =3D (COUNT); \ + const size_t __obj_size =3D struct_size_t(TYPE, FAM, __count); \ + TYPE *__obj_ptr =3D KMALLOC(__obj_size, GFP); \ + __obj_ptr; \ +}) + /** * kmalloc_obj - Allocate a single instance of the given type * @VAR_OR_TYPE: Variable or type to allocate. @@ -923,23 +944,44 @@ static __always_inline __alloc_size(1) void *kmalloc_= noprof(size_t size, gfp_t f #define kmalloc_objs(VAR_OR_TYPE, COUNT, GFP) \ __alloc_objs(kmalloc, GFP, typeof(VAR_OR_TYPE), COUNT) =20 +/** + * kmalloc_flex - Allocate a single instance of the given flexible structu= re + * @VAR_OR_TYPE: Variable or type to allocate (with its flex array). + * @FAM: The name of the flexible array member of the structure. + * @COUNT: How many flexible array member elements are desired. + * @GFP: GFP flags for the allocation. + * + * Returns: newly allocated pointer to @VAR_OR_TYPE on success, NULL on + * failure. If @FAM has been annotated with __counted_by(), the allocation + * will immediately fail if @COUNT is larger than what the type of the + * struct's counter variable can represent. + */ +#define kmalloc_flex(VAR_OR_TYPE, FAM, COUNT, GFP) \ + __alloc_flex(kmalloc, GFP, typeof(VAR_OR_TYPE), FAM, COUNT) + /* All kzalloc aliases for kmalloc_(obj|objs|flex). */ #define kzalloc_obj(P, GFP) \ __alloc_objs(kzalloc, GFP, typeof(P), 1) #define kzalloc_objs(P, COUNT, GFP) \ __alloc_objs(kzalloc, GFP, typeof(P), COUNT) +#define kzalloc_flex(P, FAM, COUNT, GFP) \ + __alloc_flex(kzalloc, GFP, typeof(P), FAM, COUNT) =20 /* All kvmalloc aliases for kmalloc_(obj|objs|flex). */ #define kvmalloc_obj(P, GFP) \ __alloc_objs(kvmalloc, GFP, typeof(P), 1) #define kvmalloc_objs(P, COUNT, GFP) \ __alloc_objs(kvmalloc, GFP, typeof(P), COUNT) +#define kvmalloc_flex(P, FAM, COUNT, GFP) \ + __alloc_flex(kvmalloc, GFP, typeof(P), FAM, COUNT) =20 /* All kvzalloc aliases for kmalloc_(obj|objs|flex). */ #define kvzalloc_obj(P, GFP) \ __alloc_objs(kvzalloc, GFP, typeof(P), 1) #define kvzalloc_objs(P, COUNT, GFP) \ __alloc_objs(kvzalloc, GFP, typeof(P), COUNT) +#define kvzalloc_flex(P, FAM, COUNT, GFP) \ + __alloc_flex(kvzalloc, GFP, typeof(P), FAM, COUNT) =20 #define kmem_buckets_alloc(_b, _size, _flags) \ alloc_hooks(__kmalloc_node_noprof(PASS_BUCKET_PARAMS(_size, _b), _flags, = NUMA_NO_NODE)) --=20 2.55.0 From nobody Sat Jul 25 06:09:14 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C32513033DE; Fri, 17 Jul 2026 04:46:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263613; cv=none; b=vA4iOHZktTVQzjtKAiIwerzu70I14COVuUq7ta2YldUW7/FMwOwlEP7IbGf52NlmRbCeg5Cc2uqDw2fTQmNWc1i7QgbY9IFTSEMVVvTFtuKyGw2iN7j3d7x5CBuE2kyy61zdNTsOc0kxm/qnkBS2xSpLfgwmvlHRUyVLdN6lyoU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263613; c=relaxed/simple; bh=JbD+pV0bxMio0bzJ2YrsvMqRt17grhfkpzgmVw/UZBw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jkw9kRv9K2I9VfNBCLcgmGwUKv8nFb49MHz5Yl1AHwOMNyBT7pQlLA0Ft0NM4o0Lxnn9Q0Xg9RnTckQ5ToRZCs3bewntDs0LJ+GrckGx7I6qv/d1OUp0I3ID9TbmpuDnqAjGbYpB/2ZwzfOC8EJ0gy/j+x1RyHIGmcDY9Ggy/jg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=g2piZX0y; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="g2piZX0y" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7D2721F00AC4; Fri, 17 Jul 2026 04:46:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784263611; bh=+3TkwhxL7AAa7pEOFpsU8nQCGpvkJ9pkYd16HJnht9c=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=g2piZX0yOK353vDxyfIInRuHJ3H6BJCTgunq2s1U/m6g2P8gpYaiC3enJHfB82wg+ A7MJSduc7/P8QEsc1DY+WDY/YagXBJgWTKYYzEEmT4P203z59cAmnkf3iir2pXjz+C BxCeRRZTArA381A6yJyIzW4UlbYa2H5+8cXfSDvE6qQMJv5EO163Qa2ZOiQPpwMN+e XhVGq6+pD9zWTdWsrIBIG7vJEVkcTjV3j17jOZGAOp95NahCQvWz+o99Mfp77vJ1+a 7WHsOZgsX96ZI83XKLYz8jDtGrdJrPZQn1Yd6KMpBdSY7iGJzr7w2JDC4ubgIMGW2G kI1oKql3cm4OQ== From: Eric Biggers To: stable@vger.kernel.org Cc: linux-hardening@vger.kernel.org, linux-fscrypt@vger.kernel.org, linux-kernel@vger.kernel.org, Linus Torvalds , Eric Biggers Subject: [PATCH 6.12 3/7] add default_gfp() helper macro and use it in the new *alloc_obj() helpers Date: Thu, 16 Jul 2026 21:42:59 -0700 Message-ID: <20260717044303.425265-4-ebiggers@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260717044303.425265-1-ebiggers@kernel.org> References: <20260717044303.425265-1-ebiggers@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Linus Torvalds commit e19e1b480ac73c3e62ffebbca1174f0f511f43e7 upstream. Most simple allocations use GFP_KERNEL, and with the new allocation helpers being introduced, let's just take advantage of that to simplify that default case. It's a numbers game: git grep 'alloc_obj(' | sed 's/.*\(GFP_[_A-Z]*\).*/\1/' | sort | uniq -c | sort -n | tail shows that about 90% of all those new allocator instances just use that standard GFP_KERNEL. Those helpers are already macros, and we can easily just make it be the default case when the gfp argument is missing. And yes, we could do that for all the legacy interfaces too, but let's keep it to just the new ones at least for now, since those all got converted recently anyway, so this is not any "extra" noise outside of that limited conversion. And, in fact, I want to do this before doing the -rc1 release, exactly so that we don't get extra merge conflicts. Signed-off-by: Linus Torvalds Signed-off-by: Eric Biggers --- include/linux/gfp.h | 4 ++++ include/linux/slab.h | 48 ++++++++++++++++++++++---------------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/include/linux/gfp.h b/include/linux/gfp.h index bc59016743fb..abe4282fbdb1 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -12,6 +12,10 @@ struct vm_area_struct; struct mempolicy; =20 +/* Helper macro to avoid gfp flags if they are the default one */ +#define __default_gfp(a,...) a +#define default_gfp(...) __default_gfp(__VA_ARGS__ __VA_OPT__(,) GFP_KERNE= L) + /* Convert GFP flags to their corresponding migrate type */ #define GFP_MOVABLE_MASK (__GFP_RECLAIMABLE|__GFP_MOVABLE) #define GFP_MOVABLE_SHIFT 3 diff --git a/include/linux/slab.h b/include/linux/slab.h index d54d44b053dd..18a149e51346 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -929,8 +929,8 @@ static __always_inline __alloc_size(1) void *kmalloc_no= prof(size_t size, gfp_t f * Returns: newly allocated pointer to a @VAR_OR_TYPE on success, or NULL * on failure. */ -#define kmalloc_obj(VAR_OR_TYPE, GFP) \ - __alloc_objs(kmalloc, GFP, typeof(VAR_OR_TYPE), 1) +#define kmalloc_obj(VAR_OR_TYPE, ...) \ + __alloc_objs(kmalloc, default_gfp(__VA_ARGS__), typeof(VAR_OR_TYPE), 1) =20 /** * kmalloc_objs - Allocate an array of the given type @@ -941,8 +941,8 @@ static __always_inline __alloc_size(1) void *kmalloc_no= prof(size_t size, gfp_t f * Returns: newly allocated pointer to array of @VAR_OR_TYPE on success, * or NULL on failure. */ -#define kmalloc_objs(VAR_OR_TYPE, COUNT, GFP) \ - __alloc_objs(kmalloc, GFP, typeof(VAR_OR_TYPE), COUNT) +#define kmalloc_objs(VAR_OR_TYPE, COUNT, ...) \ + __alloc_objs(kmalloc, default_gfp(__VA_ARGS__), typeof(VAR_OR_TYPE), COUN= T) =20 /** * kmalloc_flex - Allocate a single instance of the given flexible structu= re @@ -956,32 +956,32 @@ static __always_inline __alloc_size(1) void *kmalloc_= noprof(size_t size, gfp_t f * will immediately fail if @COUNT is larger than what the type of the * struct's counter variable can represent. */ -#define kmalloc_flex(VAR_OR_TYPE, FAM, COUNT, GFP) \ - __alloc_flex(kmalloc, GFP, typeof(VAR_OR_TYPE), FAM, COUNT) +#define kmalloc_flex(VAR_OR_TYPE, FAM, COUNT, ...) \ + __alloc_flex(kmalloc, default_gfp(__VA_ARGS__), typeof(VAR_OR_TYPE), FAM,= COUNT) =20 /* All kzalloc aliases for kmalloc_(obj|objs|flex). */ -#define kzalloc_obj(P, GFP) \ - __alloc_objs(kzalloc, GFP, typeof(P), 1) -#define kzalloc_objs(P, COUNT, GFP) \ - __alloc_objs(kzalloc, GFP, typeof(P), COUNT) -#define kzalloc_flex(P, FAM, COUNT, GFP) \ - __alloc_flex(kzalloc, GFP, typeof(P), FAM, COUNT) +#define kzalloc_obj(P, ...) \ + __alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), 1) +#define kzalloc_objs(P, COUNT, ...) \ + __alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), COUNT) +#define kzalloc_flex(P, FAM, COUNT, ...) \ + __alloc_flex(kzalloc, default_gfp(__VA_ARGS__), typeof(P), FAM, COUNT) =20 /* All kvmalloc aliases for kmalloc_(obj|objs|flex). */ -#define kvmalloc_obj(P, GFP) \ - __alloc_objs(kvmalloc, GFP, typeof(P), 1) -#define kvmalloc_objs(P, COUNT, GFP) \ - __alloc_objs(kvmalloc, GFP, typeof(P), COUNT) -#define kvmalloc_flex(P, FAM, COUNT, GFP) \ - __alloc_flex(kvmalloc, GFP, typeof(P), FAM, COUNT) +#define kvmalloc_obj(P, ...) \ + __alloc_objs(kvmalloc, default_gfp(__VA_ARGS__), typeof(P), 1) +#define kvmalloc_objs(P, COUNT, ...) \ + __alloc_objs(kvmalloc, default_gfp(__VA_ARGS__), typeof(P), COUNT) +#define kvmalloc_flex(P, FAM, COUNT, ...) \ + __alloc_flex(kvmalloc, default_gfp(__VA_ARGS__), typeof(P), FAM, COUNT) =20 /* All kvzalloc aliases for kmalloc_(obj|objs|flex). */ -#define kvzalloc_obj(P, GFP) \ - __alloc_objs(kvzalloc, GFP, typeof(P), 1) -#define kvzalloc_objs(P, COUNT, GFP) \ - __alloc_objs(kvzalloc, GFP, typeof(P), COUNT) -#define kvzalloc_flex(P, FAM, COUNT, GFP) \ - __alloc_flex(kvzalloc, GFP, typeof(P), FAM, COUNT) +#define kvzalloc_obj(P, ...) \ + __alloc_objs(kvzalloc, default_gfp(__VA_ARGS__), typeof(P), 1) +#define kvzalloc_objs(P, COUNT, ...) \ + __alloc_objs(kvzalloc, default_gfp(__VA_ARGS__), typeof(P), COUNT) +#define kvzalloc_flex(P, FAM, COUNT, ...) \ + __alloc_flex(kvzalloc, default_gfp(__VA_ARGS__), typeof(P), FAM, COUNT) =20 #define kmem_buckets_alloc(_b, _size, _flags) \ alloc_hooks(__kmalloc_node_noprof(PASS_BUCKET_PARAMS(_size, _b), _flags, = NUMA_NO_NODE)) --=20 2.55.0 From nobody Sat Jul 25 06:09:14 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6620E34D90D; Fri, 17 Jul 2026 04:46:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263613; cv=none; b=qLAlRi9okqIHALPNCnRcF2B/RO3eF58Pr/+cIhZDD+rIUEv2l+xStJr9SpHXYmN6xJZUsKzwFdj5XOPeEnjR/bzStE4PSGPMisDCO8PeL1F3KhBW6n2cTMkDFiuuI9OjHebLT97RcFlAvLlylBCGEOV+7krqYv6YLWkr+0/H8Uc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263613; c=relaxed/simple; bh=GLhdkU8m47IprGSuIcwY3Tl1jAW+D8g0WIW1djqvZgs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ml6tmua2dQs/5S8vRAUD0PBcv4qT4QIYWrqAGtDsJzmcuHylDdJ1f5Xwo3bM0Id+goIdu4p/DIgtefKpfW/89gs5rRUM0XnZGTC5GUi2eo/fRtc/0ZgWbtFqpk6xRooyid3uZUu+NFO8CfSYnrfiorFFVg5gl/7+qTJzfvTC0gQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XKMypAsl; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="XKMypAsl" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CD9CB1F00A3D; Fri, 17 Jul 2026 04:46:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784263612; bh=mGCNlnzFNUQxFaIeQbngU3BOCM42fvscJYZKV2r9/Is=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=XKMypAslUyq2d+TsvRVFLLan4yH7/bgRXzBOkX27wLBIQvIBWsX+HHk/K+DLvcQ7l J5zLS+5+AFIs/avRzJl/8vXgmwZlejnahPfbjjz3YX98nPh11fSW/dakpKXIk0qfKz /RaEbik7Q5vQO/h5RV49AbYsdD5UKHqF7lXmsSMXBMeTgrkYBnjsbgPamifa1wE31b XoA3Et6vyt6xFeA5VzOxPbv8Wj9mk+stW3dPZwIwblA42p8P+1GLHf8lXOMe4Wz8/g NWmIjwRpwdlsIaARyW0p5tWHzC3t6p9sJjB4eb5t858zluYFNM6VLRwRrKpqdRJEgY rYu7twQI+iUGQ== From: Eric Biggers To: stable@vger.kernel.org Cc: linux-hardening@vger.kernel.org, linux-fscrypt@vger.kernel.org, linux-kernel@vger.kernel.org, Linus Torvalds , Ricardo Ribalda , Richard Fitzgerald , Ben Dooks , Eric Biggers Subject: [PATCH 6.12 4/7] default_gfp(): avoid using the "newfangled" __VA_OPT__ trick Date: Thu, 16 Jul 2026 21:43:00 -0700 Message-ID: <20260717044303.425265-5-ebiggers@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260717044303.425265-1-ebiggers@kernel.org> References: <20260717044303.425265-1-ebiggers@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Linus Torvalds commit 551d44200152cb26f75d2ef990aeb6185b7e37fd upstream. The default_gfp() helper that I added is not wrong, but it turns out that it causes unnecessary headaches for 'sparse' which doesn't support the use of __VA_OPT__ (introduced in C++20 and C23, and supported by gcc and clang for a long time). We do already use __VA_OPT__ in some other cases in the kernel (drm/xe and btrfs), but it has been fairly limited. Now it triggers for pretty much everything, and sparse ends up not working at all. We can use the traditional gcc ',##__VA_ARGS__' syntax instead: it may not be the "C standard" way and is slightly less natural in this context, but it is the traditional model for this and avoids the sparse problem. Reported-and-tested-by: Ricardo Ribalda Reported-and-tested-by: Richard Fitzgerald Reported-by: Ben Dooks Fixes: e19e1b480ac7 ("add default_gfp() helper macro and use it in the new = *alloc_obj() helpers") Signed-off-by: Linus Torvalds Signed-off-by: Eric Biggers --- include/linux/gfp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/gfp.h b/include/linux/gfp.h index abe4282fbdb1..c7621a0714c1 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -13,8 +13,8 @@ struct vm_area_struct; struct mempolicy; =20 /* Helper macro to avoid gfp flags if they are the default one */ -#define __default_gfp(a,...) a -#define default_gfp(...) __default_gfp(__VA_ARGS__ __VA_OPT__(,) GFP_KERNE= L) +#define __default_gfp(a,b,...) b +#define default_gfp(...) __default_gfp(,##__VA_ARGS__,GFP_KERNEL) =20 /* Convert GFP flags to their corresponding migrate type */ #define GFP_MOVABLE_MASK (__GFP_RECLAIMABLE|__GFP_MOVABLE) --=20 2.55.0 From nobody Sat Jul 25 06:09:14 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B0C6936A34F; Fri, 17 Jul 2026 04:46:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263614; cv=none; b=qwIWxDc3mlvuxuwWXCVH/Z22i1NwlkhUlg+oaIPoBn/GXBm4dtTxL3yZe8GwhTJr91+SXYPwOHQQuLT+ud9cWsKDClTR5q9NNcT3flMP91cRsq3R1Bxj6HS1JfBcNGfBNppVZRmDJPnG96kk7f5Pb0Cj8PgIkhOY4Cj9d8ZuboE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263614; c=relaxed/simple; bh=PIgaHjUp+4+NhaLVAiXu0ME04kXs4iVeQNf6WFzpJR4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OqpOkyH6xMe0BTecseSSRBwHmFSZfsPYe2FfZp00cX192ZZYM1IM6obqn3zJkS4yMDkzcBELTIHdmIcfFsz5lQq6+laYAiWNFKj6881seHFBoqu2rszdfkcEJ2suscarxxJRUg+aYmw36L1MxAbsbArPTz+OQlyYu2lhSuVV/2w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kBChQT36; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kBChQT36" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3E6E81F00A3E; Fri, 17 Jul 2026 04:46:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784263612; bh=agHA0qcVhF5iE5cIt2nGc6gofuhGuU2FFdwJ8yLpydg=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=kBChQT36kDTyDFzwNkx03lKgNPpn57WXD8KZCwnDB6nSX+tLzk2yA4yZ9C27lyZWW +E5JBynR+xp2bNhy8l74ORHXVKH6A27+F4Yh7E7OZsPZkh8Ti3dmLVbgt3SPx5+wqD +rk5Eh96sSHT6J1aXBEHqg4TbCrZz3dml18trnltG+VGgboc5+ATGlJZMpm2VekIz5 /K6ElybAZhPwNwXeNoD7zxuGlREXKXkDVww3+ZFfDxGtxMs3kpFGKo1EeJLRN6gqK7 RZl8yWGpW0FpJD3y4J75escBuDqUvx8uTSqP6ytHOw/ovw7gUtnxHX7bR5z05JQ4Us tUaOqyERZmAHA== From: Eric Biggers To: stable@vger.kernel.org Cc: linux-hardening@vger.kernel.org, linux-fscrypt@vger.kernel.org, linux-kernel@vger.kernel.org, Randy Dunlap , "Harry Yoo (Oracle)" , "Vlastimil Babka (SUSE)" , Eric Biggers Subject: [PATCH 6.12 5/7] slab: recognize @GFP parameter as optional in kernel-doc Date: Thu, 16 Jul 2026 21:43:01 -0700 Message-ID: <20260717044303.425265-6-ebiggers@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260717044303.425265-1-ebiggers@kernel.org> References: <20260717044303.425265-1-ebiggers@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Randy Dunlap commit 7b5f5865fb11e60edd03c5e063e2d228b7062317 upstream. Since the @GFP parameter in kmalloc_obj() etc. is now optional, change the kernel-doc to indicate that it is optional. This avoids kernel-doc warnings: WARNING: include/linux/slab.h:1101 Excess function parameter 'GFP' descript= ion in 'kmalloc_obj' WARNING: include/linux/slab.h:1113 Excess function parameter 'GFP' descript= ion in 'kmalloc_objs' WARNING: include/linux/slab.h:1128 Excess function parameter 'GFP' descript= ion in 'kmalloc_flex' Fixes: e19e1b480ac7 ("add default_gfp() helper macro and use it in the new = *alloc_obj() helpers") Signed-off-by: Randy Dunlap Acked-by: Harry Yoo (Oracle) Link: https://patch.msgid.link/20260617163125.2716279-1-rdunlap@infradead.o= rg Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Eric Biggers --- include/linux/slab.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index 18a149e51346..773843a71960 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -924,7 +924,7 @@ static __always_inline __alloc_size(1) void *kmalloc_no= prof(size_t size, gfp_t f /** * kmalloc_obj - Allocate a single instance of the given type * @VAR_OR_TYPE: Variable or type to allocate. - * @GFP: GFP flags for the allocation. + * @...: optional GFP flags for the allocation (GFP_KERNEL when not specif= ied). * * Returns: newly allocated pointer to a @VAR_OR_TYPE on success, or NULL * on failure. @@ -936,7 +936,7 @@ static __always_inline __alloc_size(1) void *kmalloc_no= prof(size_t size, gfp_t f * kmalloc_objs - Allocate an array of the given type * @VAR_OR_TYPE: Variable or type to allocate an array of. * @COUNT: How many elements in the array. - * @GFP: GFP flags for the allocation. + * @...: optional GFP flags for the allocation (GFP_KERNEL when not specif= ied). * * Returns: newly allocated pointer to array of @VAR_OR_TYPE on success, * or NULL on failure. @@ -949,7 +949,7 @@ static __always_inline __alloc_size(1) void *kmalloc_no= prof(size_t size, gfp_t f * @VAR_OR_TYPE: Variable or type to allocate (with its flex array). * @FAM: The name of the flexible array member of the structure. * @COUNT: How many flexible array member elements are desired. - * @GFP: GFP flags for the allocation. + * @...: optional GFP flags for the allocation (GFP_KERNEL when not specif= ied). * * Returns: newly allocated pointer to @VAR_OR_TYPE on success, NULL on * failure. If @FAM has been annotated with __counted_by(), the allocation --=20 2.55.0 From nobody Sat Jul 25 06:09:14 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DE16A374A01; Fri, 17 Jul 2026 04:46:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263615; cv=none; b=QhFu72UluMlQ4WkT9Dd7Y7zShBc6hDUudP0EcB9hGvg9BbhT1ufNqdA13HJHxrLIwe5EPc0xD8zjdq4PllmItlCcph13vVxffUObLaXOz3WGUJCW3e+LcyOXpaOBpUtVg7cYqcacXmfUCrqrntRc76s9qmRb/Zfmoqu3K2k9S0A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263615; c=relaxed/simple; bh=gWo6nSZ2RcX678DVPQFdfQCcrtZzOIIyZDBl1EsoQuU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=V34JWvzQfU6SpNKz5NTu+IEI2OHYA4mhudPOnQcfqsm7IsseqKTzIJeC8I/1bkU7HJwtpAFAqoy4pbQuqZyyaEoLE8/ao1Bykj2ktVB62KJs7wV1wiz2CE4e5HK8bOdpD39EaZXmlUVzW8xN137QOysZo4lb3ErYFxSbwBiTsxA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Gm8iYe38; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Gm8iYe38" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9DAD41F00ADB; Fri, 17 Jul 2026 04:46:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784263612; bh=1nJtp0Sqs9QnVPEWLjYY5MJHk/dX57pHZP1V6fi2n8w=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Gm8iYe386/QBj1dpmr+lsxuGPaaSHnHecZcvChd0r3G/RUhxcG4odzU1iWJeBhLc2 G+xm9+xqRHyCsiYyNjyZsxfNX0uikn/w7Yo5PsIQpIKysR4upzwaAy+xoPaDVSY+nk NfZ9Og4EmL2HhjTMfHoqIeefcL/qwzhCWttFPcqOFO+zg28S3qhGsOIsOcCcnxLZPE xi4WC/N6xHgAerZjSuK50XIvyCOEfhyEW7c6y6QtY8vsJLeoH/LfFNMT4thukGeAWb PKIS6PWe+EJp4W1RAbrzwiZEvWKO0allJtvhMJfOw0tRvcckwucK6EBfjXMRT7AFwk 0bwG+WxC1oroA== From: Eric Biggers To: stable@vger.kernel.org Cc: linux-hardening@vger.kernel.org, linux-fscrypt@vger.kernel.org, linux-kernel@vger.kernel.org, Eric Biggers Subject: [PATCH 6.12 6/7] fscrypt: Fix key setup in edge case with multiple data unit sizes Date: Thu, 16 Jul 2026 21:43:02 -0700 Message-ID: <20260717044303.425265-7-ebiggers@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260717044303.425265-1-ebiggers@kernel.org> References: <20260717044303.425265-1-ebiggers@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" commit dd015b566d505d698386103e9c80b739c7336eb8 upstream. The addition of support for customizable data unit sizes introduced an edge case where a file's contents can be en/decrypted with the wrong data unit size. It occurs when there are multiple v2 policies that: - Have *different* data unit sizes, via the log2_data_unit_size field - Share the same master_key_identifier, contents_encryption_mode, and either FSCRYPT_POLICY_FLAG_DIRECT_KEY, FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32, or FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 - Are being used on the same filesystem, which also must be mounted with the "inlinecrypt" mount option. Fortunately this edge case doesn't actually occur in practice. I just found it via code review. But it needs to be fixed regardless. The bug is caused by the data unit size not being fully considered when blk_crypto_keys are cached in mk_direct_keys, mk_iv_ino_lblk_32_keys, and mk_iv_ino_lblk_64_keys. They're differentiated only by master key, encryption mode, and flag. However, each one actually has a data unit size too. Only the first data unit size that is cached is used. To fix this, start using the data unit size to differentiate the cached keys. For several reasons, including avoiding increasing the size of struct fscrypt_master_key, just replace all three arrays with a single linked list instead of changing them into two-dimensional arrays. This works well when considering that in practice at most 2 entries are used across all three arrays, so it was already mostly wasted space. For simplicity, make the list also take over the publish/subscribe of the prepared key itself. That is, create separate list nodes for blk_crypto_keys vs crypto_skciphers, and add nodes to the list only when their key is actually prepared. (Note that the legacy fscrypt_direct_keys table in fs/crypto/keysetup_v1.c already works this way.) This eliminates the need for the additional memory barriers when reading and writing the fields of struct fscrypt_prepared_key. Note that I technically should have included the data unit size in the HKDF info string as well. But it's too late to change that. Fixes: 5b1188847180 ("fscrypt: support crypto data unit size less than file= system block size") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618180652.52742-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/fscrypt_private.h | 55 ++++++++++------- fs/crypto/inline_crypt.c | 8 +-- fs/crypto/keyring.c | 23 ++++--- fs/crypto/keysetup.c | 119 +++++++++++++++++++++++------------- 4 files changed, 125 insertions(+), 80 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 25bcfcc2d706..dd169048017b 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -27,6 +27,9 @@ */ #define FSCRYPT_MIN_KEY_SIZE 16 =20 +/* Maximum size of a raw fscrypt master key */ +#define FSCRYPT_MAX_RAW_KEY_SIZE 64 + /* * This mask is passed as the third argument to the crypto_alloc_*() funct= ions * to prevent fscrypt from using the Crypto API drivers for non-inline cry= pto @@ -217,7 +220,7 @@ struct fscrypt_symlink_data { * @tfm: crypto API transform object * @blk_key: key for blk-crypto * - * Normally only one of the fields will be non-NULL. + * Only one of the fields is non-NULL. */ struct fscrypt_prepared_key { struct crypto_skcipher *tfm; @@ -226,6 +229,15 @@ struct fscrypt_prepared_key { #endif }; =20 +/* An entry in the linked list ->mk_mode_keys */ +struct fscrypt_mode_key { + struct fscrypt_prepared_key key; + struct list_head link; + u8 hkdf_context; + u8 mode_num; + u8 data_unit_bits; +}; + /* * fscrypt_inode_info - the "encryption key" for an inode * @@ -413,20 +425,12 @@ void fscrypt_destroy_inline_crypt_key(struct super_bl= ock *sb, * @prep_key, depending on which encryption implementation the file will u= se. */ static inline bool -fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, +fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, const struct fscrypt_inode_info *ci) { - /* - * The two smp_load_acquire()'s here pair with the smp_store_release()'s - * in fscrypt_prepare_inline_crypt_key() and fscrypt_prepare_key(). - * I.e., in some cases (namely, if this prep_key is a per-mode - * encryption key) another task can publish blk_key or tfm concurrently, - * executing a RELEASE barrier. We need to use smp_load_acquire() here - * to safely ACQUIRE the memory the other task published. - */ if (fscrypt_using_inline_encryption(ci)) - return smp_load_acquire(&prep_key->blk_key) !=3D NULL; - return smp_load_acquire(&prep_key->tfm) !=3D NULL; + return prep_key->blk_key !=3D NULL; + return prep_key->tfm !=3D NULL; } =20 #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ @@ -458,10 +462,10 @@ fscrypt_destroy_inline_crypt_key(struct super_block *= sb, } =20 static inline bool -fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, +fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, const struct fscrypt_inode_info *ci) { - return smp_load_acquire(&prep_key->tfm) !=3D NULL; + return prep_key->tfm !=3D NULL; } #endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ =20 @@ -531,8 +535,8 @@ struct fscrypt_master_key { /* * Active and structural reference counts. An active ref guarantees * that the struct continues to exist, continues to be in the keyring - * ->s_master_keys, and that any embedded subkeys (e.g. - * ->mk_direct_keys) that have been prepared continue to exist. + * ->s_master_keys, and that any non-file-scoped subkeys (e.g. + * ->mk_mode_keys) that have been prepared continue to exist. * A structural ref only guarantees that the struct continues to exist. * * There is one active ref associated with ->mk_present being true, and @@ -586,12 +590,21 @@ struct fscrypt_master_key { spinlock_t mk_decrypted_inodes_lock; =20 /* - * Per-mode encryption keys for the various types of encryption policies - * that use them. Allocated and derived on-demand. + * A list of 'struct fscrypt_mode_key' for the (hkdf_context, mode_num, + * data_unit_bits, inlinecrypt) combinations that are in use for this + * master key, for hkdf_context in [HKDF_CONTEXT_DIRECT_KEY, + * HKDF_CONTEXT_IV_INO_LBLK_32_KEY, HKDF_CONTEXT_IV_INO_LBLK_64_KEY]. + * + * This is a linked list and not a hash table because in practice + * there's just a single encryption policy per master key, using + * _at most_ 2 nodes in this list. Per-file keys don't use this at all. + * + * This list is append-only until the master key is fully removed, at + * which time the list is cleared. Before then, + * fscrypt_mode_key_setup_mutex synchronizes appends, and searches use + * the RCU read lock together with ->mk_sem held for read. */ - struct fscrypt_prepared_key mk_direct_keys[FSCRYPT_MODE_MAX + 1]; - struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[FSCRYPT_MODE_MAX + 1]; - struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[FSCRYPT_MODE_MAX + 1]; + struct list_head mk_mode_keys; =20 /* Hash key for inode numbers. Initialized only when needed. */ siphash_key_t mk_ino_hash_key; diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 40de69860dcf..4b1f10f1cbd8 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -191,13 +191,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_pr= epared_key *prep_key, goto fail; } =20 - /* - * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared(). - * I.e., here we publish ->blk_key with a RELEASE barrier so that - * concurrent tasks can ACQUIRE it. Note that this concurrency is only - * possible for per-mode keys, not for per-file keys. - */ - smp_store_release(&prep_key->blk_key, blk_key); + prep_key->blk_key =3D blk_key; return 0; =20 fail: diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 206835e31efa..6dd6d198be63 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -86,14 +86,14 @@ void fscrypt_put_master_key(struct fscrypt_master_key *= mk) void fscrypt_put_master_key_activeref(struct super_block *sb, struct fscrypt_master_key *mk) { - size_t i; + struct fscrypt_mode_key *node, *tmp; =20 if (!refcount_dec_and_test(&mk->mk_active_refs)) return; /* * No active references left, so complete the full removal of this * fscrypt_master_key struct by removing it from the keyring and - * destroying any subkeys embedded in it. + * destroying any non-file-scoped subkeys. */ =20 if (WARN_ON_ONCE(!sb->s_master_keys)) @@ -109,13 +109,16 @@ void fscrypt_put_master_key_activeref(struct super_bl= ock *sb, WARN_ON_ONCE(mk->mk_present); WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_inodes)); =20 - for (i =3D 0; i <=3D FSCRYPT_MODE_MAX; i++) { - fscrypt_destroy_prepared_key( - sb, &mk->mk_direct_keys[i]); - fscrypt_destroy_prepared_key( - sb, &mk->mk_iv_ino_lblk_64_keys[i]); - fscrypt_destroy_prepared_key( - sb, &mk->mk_iv_ino_lblk_32_keys[i]); + /* + * Destroy any non-file-scoped subkeys. Since ->mk_active_refs =3D=3D 0, + * they're no longer referenced by any inodes. Nor can key setup run + * and use them again. So they're no longer needed. (This implies no + * concurrent readers, so we don't need list_del_rcu() for example.) + */ + list_for_each_entry_safe(node, tmp, &mk->mk_mode_keys, link) { + fscrypt_destroy_prepared_key(sb, &node->key); + list_del(&node->link); + kfree(node); } memzero_explicit(&mk->mk_ino_hash_key, sizeof(mk->mk_ino_hash_key)); @@ -444,6 +447,8 @@ static int add_new_master_key(struct super_block *sb, INIT_LIST_HEAD(&mk->mk_decrypted_inodes); spin_lock_init(&mk->mk_decrypted_inodes_lock); =20 + INIT_LIST_HEAD(&mk->mk_mode_keys); + if (mk_spec->type =3D=3D FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { err =3D allocate_master_key_users_keyring(mk); if (err) diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index 2896046a4977..71586440d043 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -159,13 +159,7 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *p= rep_key, tfm =3D fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode); if (IS_ERR(tfm)) return PTR_ERR(tfm); - /* - * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared(). - * I.e., here we publish ->tfm with a RELEASE barrier so that - * concurrent tasks can ACQUIRE it. Note that this concurrency is only - * possible for per-mode keys, not for per-file keys. - */ - smp_store_release(&prep_key->tfm, tfm); + prep_key->tfm =3D tfm; return 0; } =20 @@ -186,9 +180,37 @@ int fscrypt_set_per_file_enc_key(struct fscrypt_inode_= info *ci, return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci); } =20 +/* + * Find the fscrypt_prepared_key (if any) for a particular (mk, hkdf_conte= xt, + * mode_num, data_unit_bits, inlinecrypt) combination. + * + * The caller must hold ->mk_sem for reading and ->mk_present must be true, + * ensuring that ->mk_mode_keys is still append-only. + */ +static struct fscrypt_prepared_key * +fscrypt_find_mode_key(struct fscrypt_master_key *mk, u8 hkdf_context, + u8 mode_num, const struct fscrypt_inode_info *ci) +{ + struct fscrypt_mode_key *node; + + /* + * The RCU read lock here is used only to synchronize with concurrent + * list_add_tail_rcu(). Concurrent deletions are impossible here, so + * returning a pointer to a node without taking any refcount is safe. + */ + guard(rcu)(); + list_for_each_entry_rcu(node, &mk->mk_mode_keys, link) { + if (node->hkdf_context =3D=3D hkdf_context && + node->mode_num =3D=3D mode_num && + node->data_unit_bits =3D=3D ci->ci_data_unit_bits && + fscrypt_is_key_prepared(&node->key, ci)) + return &node->key; + } + return NULL; +} + static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, struct fscrypt_master_key *mk, - struct fscrypt_prepared_key *keys, u8 hkdf_context, bool include_fs_uuid) { const struct inode *inode =3D ci->ci_inode; @@ -196,7 +218,8 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_= info *ci, struct fscrypt_mode *mode =3D ci->ci_mode; const u8 mode_num =3D mode - fscrypt_modes; struct fscrypt_prepared_key *prep_key; - u8 mode_key[FSCRYPT_MAX_KEY_SIZE]; + struct fscrypt_mode_key *new_node; + u8 raw_mode_key[FSCRYPT_MAX_RAW_KEY_SIZE]; u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)]; unsigned int hkdf_infolen =3D 0; int err; @@ -204,41 +227,52 @@ static int setup_per_mode_enc_key(struct fscrypt_inod= e_info *ci, if (WARN_ON_ONCE(mode_num > FSCRYPT_MODE_MAX)) return -EINVAL; =20 - prep_key =3D &keys[mode_num]; - if (fscrypt_is_key_prepared(prep_key, ci)) { + prep_key =3D fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci); + if (prep_key) { ci->ci_enc_key =3D *prep_key; return 0; } =20 - mutex_lock(&fscrypt_mode_key_setup_mutex); + guard(mutex)(&fscrypt_mode_key_setup_mutex); =20 - if (fscrypt_is_key_prepared(prep_key, ci)) - goto done_unlock; + prep_key =3D fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci); + if (prep_key) { + ci->ci_enc_key =3D *prep_key; + return 0; + } =20 - BUILD_BUG_ON(sizeof(mode_num) !=3D 1); - BUILD_BUG_ON(sizeof(sb->s_uuid) !=3D 16); - BUILD_BUG_ON(sizeof(hkdf_info) !=3D 17); - hkdf_info[hkdf_infolen++] =3D mode_num; - if (include_fs_uuid) { - memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid, - sizeof(sb->s_uuid)); - hkdf_infolen +=3D sizeof(sb->s_uuid); + new_node =3D kzalloc_obj(*new_node); + if (!new_node) + return -ENOMEM; + new_node->hkdf_context =3D hkdf_context; + new_node->mode_num =3D mode_num; + new_node->data_unit_bits =3D ci->ci_data_unit_bits; + prep_key =3D &new_node->key; + + { + static_assert(sizeof(mode_num) =3D=3D 1); + static_assert(sizeof(sb->s_uuid) =3D=3D 16); + static_assert(sizeof(hkdf_info) =3D=3D 17); + hkdf_info[hkdf_infolen++] =3D mode_num; + if (include_fs_uuid) { + memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid, + sizeof(sb->s_uuid)); + hkdf_infolen +=3D sizeof(sb->s_uuid); + } + err =3D fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, + hkdf_info, hkdf_infolen, raw_mode_key, + mode->keysize); + if (!err) + err =3D fscrypt_prepare_key(prep_key, raw_mode_key, ci); + memzero_explicit(raw_mode_key, mode->keysize); } - err =3D fscrypt_hkdf_expand(&mk->mk_secret.hkdf, - hkdf_context, hkdf_info, hkdf_infolen, - mode_key, mode->keysize); - if (err) - goto out_unlock; - err =3D fscrypt_prepare_key(prep_key, mode_key, ci); - memzero_explicit(mode_key, mode->keysize); - if (err) - goto out_unlock; -done_unlock: + if (err) { + kfree(new_node); + return err; + } + list_add_tail_rcu(&new_node->link, &mk->mk_mode_keys); ci->ci_enc_key =3D *prep_key; - err =3D 0; -out_unlock: - mutex_unlock(&fscrypt_mode_key_setup_mutex); - return err; + return 0; } =20 /* @@ -296,8 +330,8 @@ static int fscrypt_setup_iv_ino_lblk_32_key(struct fscr= ypt_inode_info *ci, { int err; =20 - err =3D setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_32_keys, - HKDF_CONTEXT_IV_INO_LBLK_32_KEY, true); + err =3D setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_IV_INO_LBLK_32_KEY, + true); if (err) return err; =20 @@ -346,8 +380,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_ino= de_info *ci, * encryption key. This ensures that the master key is * consistently used only for HKDF, avoiding key reuse issues. */ - err =3D setup_per_mode_enc_key(ci, mk, mk->mk_direct_keys, - HKDF_CONTEXT_DIRECT_KEY, false); + err =3D setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_DIRECT_KEY, + false); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) { /* @@ -356,9 +390,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_ino= de_info *ci, * the IVs. This format is optimized for use with inline * encryption hardware compliant with the UFS standard. */ - err =3D setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_64_keys, - HKDF_CONTEXT_IV_INO_LBLK_64_KEY, - true); + err =3D setup_per_mode_enc_key( + ci, mk, HKDF_CONTEXT_IV_INO_LBLK_64_KEY, true); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) { err =3D fscrypt_setup_iv_ino_lblk_32_key(ci, mk); --=20 2.55.0 From nobody Sat Jul 25 06:09:14 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 83E7034B1A3; Fri, 17 Jul 2026 04:46:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263615; cv=none; b=JtWb06RD/ds5IXlWyBBaHlkLzuraLp/gaiQ4/Rew2ng9FhGQH5tWg+hPs+anKf17BdvOKCNGAFD6YLpu5kIV+/bLHtDkNxCBFytOr3Yy7VooZLcyUHd3pUTLazHx82DxOWosCU/Jh7WOB1jc9RPbFhXK4JE/t+wuJHwfV0v0Ggo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263615; c=relaxed/simple; bh=or38u/wa0YCcq5F0sUPktffzm739+AZnea/sE1B0p/I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UhxH5uOxwiX6P9yd7FAaK+KKUSqRvTGfsnvvQuChtgATtfH/gXzip77CDfwmNz1qTEcJw0fonHLzD9DwlfyOx6QRUNgtFxQ4dRl6uP/4q1vgkkae0zxl9oHa5U6gEb055S5tvTmNpQ+7ub6u5U4vCjE9jy4KsdS2j86OmxQekII= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ddz52zvZ; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ddz52zvZ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E759A1F000E9; Fri, 17 Jul 2026 04:46:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784263613; bh=YcYo9r74+MX6PLfmylWqkVdmoLNm9U2nVauNbXuWdoc=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=ddz52zvZLd8vFyiiEIxXCJb68S9LnLJvIJ3in5DXODuVkW1aHIuSGHv17PcQlDNlj 6Q2F3auYHxRA7EsCon8IgbHAudUQGR/INFLOekPCeDr3q/xgu4Dh5JRlruQ+0It6gB 2JSsA3w5co5GioMXrlQc4qd3fZV8XLWypcQvY7Eu5pCCMBWbz1i0CenLXl2FwtqqjV OUh3YuByexAzKGj6EmXaKLYmg/7UfbpY5k48N8PK1zdGK/92LgODFT3QkfyZ2Igi25 ns6Q8piFxVHgM8/ZYlI9qU8jv1e7m1U3N1BTDK1+82DIqaDAD80tKO/f3QnYRF6+xa zB58eXra3wbSg== From: Eric Biggers To: stable@vger.kernel.org Cc: linux-hardening@vger.kernel.org, linux-fscrypt@vger.kernel.org, linux-kernel@vger.kernel.org, Eric Biggers , syzbot+f55b043dacf43776b50c@syzkaller.appspotmail.com, Mohammed EL Kadiri Subject: [PATCH 6.12 7/7] fscrypt: Replace mk_users keyring with simple list Date: Thu, 16 Jul 2026 21:43:03 -0700 Message-ID: <20260717044303.425265-8-ebiggers@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260717044303.425265-1-ebiggers@kernel.org> References: <20260717044303.425265-1-ebiggers@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" commit 696c030e1e3438955aba443b308ee8b6faa3983e upstream. Change mk_users (the set of user claims to an fscrypt master key) from a 'struct key' keyring to a simple linked list. It's still a collection of 'struct key' for quota tracking. It was originally thought to be natural that a collection of 'struct key' should be held in a 'struct key' keyring. In reality, it's just been causing problems, similar to how using 'struct key' for the filesystem keyring caused problems and was removed in commit d7e7b9af104c ("fscrypt: stop using keyrings subsystem for fscrypt_master_key"). Commit d3a7bd420076 ("fscrypt: clear keyring before calling key_put()") fixed mk_users cleanup to be synchronous. But that apparently wasn't enough: the keyring subsystem's redundant locking is still generating lockdep false positives due to the interaction with filesystem reclaim. With the simple list, the redundant locking and lockdep issue goes away. Of course, searching a linked list is linear-time whereas the 'struct key' keyring used a fancy constant-time associative array. But that's fine here, since in practice there's just one entry in the list. In fact the new code is much faster in practice, since it's much smaller and doesn't have to convert the kuid_t into a string to search for it. Reported-by: syzbot+f55b043dacf43776b50c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3Df55b043dacf43776b50c Reported-by: Mohammed EL Kadiri Closes: https://lore.kernel.org/keyrings/20260614150041.21172-1-med08elkadi= ri@gmail.com/ Fixes: 23c688b54016 ("fscrypt: allow unprivileged users to add/remove keys = for v2 policies") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618221921.87896-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/fscrypt_private.h | 32 ++++-- fs/crypto/keyring.c | 216 +++++++++++++++--------------------- 2 files changed, 113 insertions(+), 135 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index dd169048017b..eac9f657caea 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -471,6 +471,19 @@ fscrypt_is_key_prepared(const struct fscrypt_prepared_= key *prep_key, =20 /* keyring.c */ =20 +/* + * fscrypt_master_key_user - a user's claim to a master key + */ +struct fscrypt_master_key_user { + struct list_head link; + kuid_t uid; + /* + * This 'struct key' contains no secret. It exists solely to charge the + * appropriate user's key quota. + */ + struct key *quota_key; +}; + /* * fscrypt_master_key_secret - secret key material of an in-use master key */ @@ -568,19 +581,18 @@ struct fscrypt_master_key { struct fscrypt_key_specifier mk_spec; =20 /* - * Keyring which contains a key of type 'key_type_fscrypt_user' for each - * user who has added this key. Normally each key will be added by just - * one user, but it's possible that multiple users share a key, and in - * that case we need to keep track of those users so that one user can't - * remove the key before the others want it removed too. + * List of user claims to this key (struct fscrypt_master_key_user). + * Normally each key will be added by just one user, but it's possible + * that multiple users share a key, and in that case we need to keep + * track of those users so that one user can't remove the key before the + * others want it removed too. * - * This is NULL for v1 policy keys; those can only be added by root. + * Used only for v2 policy keys. v1 policy keys can be added only by + * root, so user tracking doesn't apply to them. * - * Locking: protected by ->mk_sem. (We don't just rely on the keyrings - * subsystem semaphore ->mk_users->sem, as we need support for atomic - * search+insert along with proper synchronization with other fields.) + * Locking: protected by ->mk_sem. */ - struct key *mk_users; + struct list_head mk_users; =20 /* * List of inodes that were unlocked using this key. This allows the diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 6dd6d198be63..46e2bf043e73 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -64,22 +64,19 @@ static void fscrypt_free_master_key(struct rcu_head *he= ad) kfree_sensitive(mk); } =20 +static void clear_mk_users(struct fscrypt_master_key *mk); + void fscrypt_put_master_key(struct fscrypt_master_key *mk) { if (!refcount_dec_and_test(&mk->mk_struct_refs)) return; /* - * No structural references left, so free ->mk_users, and also free the + * No structural references left, so clear ->mk_users, and also free the * fscrypt_master_key struct itself after an RCU grace period ensures * that concurrent keyring lookups can no longer find it. */ WARN_ON_ONCE(refcount_read(&mk->mk_active_refs) !=3D 0); - if (mk->mk_users) { - /* Clear the keyring so the quota gets released right away. */ - keyring_clear(mk->mk_users); - key_put(mk->mk_users); - mk->mk_users =3D NULL; - } + clear_mk_users(mk); call_rcu(&mk->mk_rcu_head, fscrypt_free_master_key); } =20 @@ -164,8 +161,8 @@ static void fscrypt_user_key_describe(const struct key = *key, struct seq_file *m) } =20 /* - * Type of key in ->mk_users. Each key of this type represents a particul= ar - * user who has added a particular master key. + * Type of fscrypt_master_key_user::quota_key. This contains no secret; it + * exists solely to charge a user's key quota. * * Note that the name of this key type really should be something like * ".fscrypt-user" instead of simply ".fscrypt". But the shorter name is = chosen @@ -179,30 +176,9 @@ static struct key_type key_type_fscrypt_user =3D { .describe =3D fscrypt_user_key_describe, }; =20 -#define FSCRYPT_MK_USERS_DESCRIPTION_SIZE \ - (CONST_STRLEN("fscrypt-") + 2 * FSCRYPT_KEY_IDENTIFIER_SIZE + \ - CONST_STRLEN("-users") + 1) - #define FSCRYPT_MK_USER_DESCRIPTION_SIZE \ (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + CONST_STRLEN(".uid.") + 10 + 1) =20 -static void format_mk_users_keyring_description( - char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE], - const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) -{ - sprintf(description, "fscrypt-%*phN-users", - FSCRYPT_KEY_IDENTIFIER_SIZE, mk_identifier); -} - -static void format_mk_user_description( - char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE], - const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) -{ - - sprintf(description, "%*phN.uid.%u", FSCRYPT_KEY_IDENTIFIER_SIZE, - mk_identifier, __kuid_val(current_fsuid())); -} - /* Create ->s_master_keys if needed. Synchronized by fscrypt_add_key_mute= x. */ static int allocate_filesystem_keyring(struct super_block *sb) { @@ -337,91 +313,94 @@ fscrypt_find_master_key(struct super_block *sb, return mk; } =20 -static int allocate_master_key_users_keyring(struct fscrypt_master_key *mk) -{ - char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE]; - struct key *keyring; - - format_mk_users_keyring_description(description, - mk->mk_spec.u.identifier); - keyring =3D keyring_alloc(description, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, - current_cred(), KEY_POS_SEARCH | - KEY_USR_SEARCH | KEY_USR_READ | KEY_USR_VIEW, - KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); - if (IS_ERR(keyring)) - return PTR_ERR(keyring); - - mk->mk_users =3D keyring; - return 0; -} - -/* - * Find the current user's "key" in the master key's ->mk_users. - * Returns ERR_PTR(-ENOKEY) if not found. - */ -static struct key *find_master_key_user(struct fscrypt_master_key *mk) +/* Find the current user's claim in ->mk_users. ->mk_sem must be held. */ +static struct fscrypt_master_key_user * +find_master_key_user(struct fscrypt_master_key *mk) { - char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; - key_ref_t keyref; + struct fscrypt_master_key_user *mk_user; + kuid_t uid =3D current_fsuid(); =20 - format_mk_user_description(description, mk->mk_spec.u.identifier); - - /* - * We need to mark the keyring reference as "possessed" so that we - * acquire permission to search it, via the KEY_POS_SEARCH permission. - */ - keyref =3D keyring_search(make_key_ref(mk->mk_users, true /*possessed*/), - &key_type_fscrypt_user, description, false); - if (IS_ERR(keyref)) { - if (PTR_ERR(keyref) =3D=3D -EAGAIN || /* not found */ - PTR_ERR(keyref) =3D=3D -EKEYREVOKED) /* recently invalidated */ - keyref =3D ERR_PTR(-ENOKEY); - return ERR_CAST(keyref); + list_for_each_entry(mk_user, &mk->mk_users, link) { + if (uid_eq(mk_user->uid, uid)) + return mk_user; } - return key_ref_to_ptr(keyref); + return NULL; } =20 /* - * Give the current user a "key" in ->mk_users. This charges the user's q= uota + * Give the current user a claim in ->mk_users. This charges the user's q= uota * and marks the master key as added by the current user, so that it canno= t be * removed by another user with the key. Either ->mk_sem must be held for * write, or the master key must be still undergoing initialization. */ static int add_master_key_user(struct fscrypt_master_key *mk) { + kuid_t uid =3D current_fsuid(); char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; - struct key *mk_user; + struct key *quota_key; + struct fscrypt_master_key_user *mk_user; int err; =20 - format_mk_user_description(description, mk->mk_spec.u.identifier); - mk_user =3D key_alloc(&key_type_fscrypt_user, description, - current_fsuid(), current_gid(), current_cred(), - KEY_POS_SEARCH | KEY_USR_VIEW, 0, NULL); - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); + snprintf(description, sizeof(description), "%*phN.uid.%u", + FSCRYPT_KEY_IDENTIFIER_SIZE, mk->mk_spec.u.identifier, + __kuid_val(uid)); + quota_key =3D key_alloc(&key_type_fscrypt_user, description, uid, + current_gid(), current_cred(), + KEY_POS_SEARCH | KEY_USR_VIEW, 0, NULL); + if (IS_ERR(quota_key)) + return PTR_ERR(quota_key); + + err =3D key_instantiate_and_link(quota_key, NULL, 0, NULL, NULL); + if (err) { + key_put(quota_key); + return err; + } =20 - err =3D key_instantiate_and_link(mk_user, NULL, 0, mk->mk_users, NULL); - key_put(mk_user); - return err; + mk_user =3D kzalloc_obj(*mk_user); + if (!mk_user) { + key_put(quota_key); + return -ENOMEM; + } + mk_user->uid =3D uid; + mk_user->quota_key =3D quota_key; + list_add(&mk_user->link, &mk->mk_users); + return 0; +} + +static void unlink_and_free_mk_user(struct fscrypt_master_key_user *mk_use= r) +{ + list_del(&mk_user->link); + key_put(mk_user->quota_key); + kfree(mk_user); } =20 /* - * Remove the current user's "key" from ->mk_users. + * Remove the current user's claim from ->mk_users. * ->mk_sem must be held for write. * - * Returns 0 if removed, -ENOKEY if not found, or another -errno code. + * Returns 0 if removed or -ENOKEY if not found. */ static int remove_master_key_user(struct fscrypt_master_key *mk) { - struct key *mk_user; - int err; + struct fscrypt_master_key_user *mk_user; =20 mk_user =3D find_master_key_user(mk); - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); - err =3D key_unlink(mk->mk_users, mk_user); - key_put(mk_user); - return err; + if (!mk_user) + return -ENOKEY; + unlink_and_free_mk_user(mk_user); + return 0; +} + +/* + * Clear ->mk_users. Either ->mk_sem must be held for write, or 'mk' must= have + * no structural references left. + */ +static void clear_mk_users(struct fscrypt_master_key *mk) +{ + struct fscrypt_master_key_user *mk_user, *tmp; + + list_for_each_entry_safe(mk_user, tmp, &mk->mk_users, link) + unlink_and_free_mk_user(mk_user); } =20 /* @@ -444,15 +423,14 @@ static int add_new_master_key(struct super_block *sb, refcount_set(&mk->mk_struct_refs, 1); mk->mk_spec =3D *mk_spec; =20 + INIT_LIST_HEAD(&mk->mk_users); + INIT_LIST_HEAD(&mk->mk_decrypted_inodes); spin_lock_init(&mk->mk_decrypted_inodes_lock); =20 INIT_LIST_HEAD(&mk->mk_mode_keys); =20 if (mk_spec->type =3D=3D FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { - err =3D allocate_master_key_users_keyring(mk); - if (err) - goto out_put; err =3D add_master_key_user(mk); if (err) goto out_put; @@ -481,19 +459,13 @@ static int add_existing_master_key(struct fscrypt_mas= ter_key *mk, int err; =20 /* - * If the current user is already in ->mk_users, then there's nothing to - * do. Otherwise, we need to add the user to ->mk_users. (Neither is - * applicable for v1 policy keys, which have NULL ->mk_users.) + * For v2 policy keys (FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER): If the current + * user is already in ->mk_users, then there's nothing to do. + * Otherwise, add the user to ->mk_users. */ - if (mk->mk_users) { - struct key *mk_user =3D find_master_key_user(mk); - - if (mk_user !=3D ERR_PTR(-ENOKEY)) { - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); - key_put(mk_user); + if (mk->mk_spec.type =3D=3D FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { + if (find_master_key_user(mk) !=3D NULL) return 0; - } err =3D add_master_key_user(mk); if (err) return err; @@ -847,7 +819,6 @@ int fscrypt_verify_key_added(struct super_block *sb, { struct fscrypt_key_specifier mk_spec; struct fscrypt_master_key *mk; - struct key *mk_user; int err; =20 mk_spec.type =3D FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; @@ -859,13 +830,10 @@ int fscrypt_verify_key_added(struct super_block *sb, goto out; } down_read(&mk->mk_sem); - mk_user =3D find_master_key_user(mk); - if (IS_ERR(mk_user)) { - err =3D PTR_ERR(mk_user); - } else { - key_put(mk_user); + if (find_master_key_user(mk) !=3D NULL) err =3D 0; - } + else + err =3D -ENOKEY; up_read(&mk->mk_sem); fscrypt_put_master_key(mk); out: @@ -1057,16 +1025,18 @@ static int do_remove_key(struct file *filp, void __= user *_uarg, bool all_users) down_write(&mk->mk_sem); =20 /* If relevant, remove current user's (or all users) claim to the key */ - if (mk->mk_users && mk->mk_users->keys.nr_leaves_on_tree !=3D 0) { - if (all_users) - err =3D keyring_clear(mk->mk_users); - else + if (!list_empty(&mk->mk_users)) { + if (all_users) { + clear_mk_users(mk); + err =3D 0; + } else { err =3D remove_master_key_user(mk); + } if (err) { up_write(&mk->mk_sem); goto out_put_key; } - if (mk->mk_users->keys.nr_leaves_on_tree !=3D 0) { + if (!list_empty(&mk->mk_users)) { /* * Other users have still added the key too. We removed * the current user's claim to the key, but we still @@ -1152,6 +1122,8 @@ int fscrypt_ioctl_get_key_status(struct file *filp, v= oid __user *uarg) struct super_block *sb =3D file_inode(filp)->i_sb; struct fscrypt_get_key_status_arg arg; struct fscrypt_master_key *mk; + kuid_t uid; + const struct fscrypt_master_key_user *mk_user; int err; =20 if (copy_from_user(&arg, uarg, sizeof(arg))) @@ -1184,19 +1156,13 @@ int fscrypt_ioctl_get_key_status(struct file *filp,= void __user *uarg) } =20 arg.status =3D FSCRYPT_KEY_STATUS_PRESENT; - if (mk->mk_users) { - struct key *mk_user; =20 - arg.user_count =3D mk->mk_users->keys.nr_leaves_on_tree; - mk_user =3D find_master_key_user(mk); - if (!IS_ERR(mk_user)) { + uid =3D current_fsuid(); + list_for_each_entry(mk_user, &mk->mk_users, link) { + arg.user_count++; + if (uid_eq(mk_user->uid, uid)) arg.status_flags |=3D FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF; - key_put(mk_user); - } else if (mk_user !=3D ERR_PTR(-ENOKEY)) { - err =3D PTR_ERR(mk_user); - goto out_release_key; - } } err =3D 0; out_release_key: --=20 2.55.0