Linux Security Modules development
 help / color / mirror / Atom feed
* Re: [bug report] keys: request_key_auth payload use-after-free in keyctl_instantiate_key_common()
From: Jarkko Sakkinen @ 2026-05-21 23:52 UTC (permalink / raw)
  To: Shaomin Chen
  Cc: keyrings, linux-security-module, linux-kernel, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn
In-Reply-To: <20260519144403.436694-1-eeesssooo020@gmail.com>

On Tue, May 19, 2026 at 10:44:03PM +0800, Shaomin Chen wrote:
> Hi,
> 
> keyctl_instantiate_key_common() can use a stale request_key_auth payload after
> the current request-key authorisation key has been revoked.
> 
> The relevant code pattern is:
> 
> 	rka = instkey->payload.data[0];
> 	...
> 	copy_from_iter_full(payload, plen, from);   /* can fault and sleep */
> 	...
> 	get_instantiation_keyring(ringid, rka, &dest_keyring);
> 	key_instantiate_and_link(rka->target_key, payload, plen,
> 				 dest_keyring, instkey);
> 
> keyctl_instantiate_key_common() does not hold authkey->sem, an RCU read-side
> critical section, or a reference to the request_key_auth payload across the
> sleeping copy and later rka dereferences.
> 
> One race sequence is:
> 
> 	Task A: request-key helper child        Task B: original request_key path
> 	-------------------------------        ---------------------------------
> 	assume request-key authority
> 	enter KEYCTL_INSTANTIATE_IOV
> 	rka = instkey->payload.data[0]
> 	block in copy_from_iter_full()
> 	                                      helper parent instantiates target key
> 	                                      helper returns to kernel
> 	                                      complete_request_key(authkey, 0)
> 	                                      key_revoke(authkey)
> 	                                      request_key_auth_revoke(authkey)
> 	                                      rcu_assign_keypointer(authkey, NULL)
> 	                                      call_rcu(&rka->rcu, ...)
> 	                                      request_key_auth_rcu_disposal()
> 	                                      free_request_key_auth(rka)
> 	resume from copy_from_iter_full()
> 	get_instantiation_keyring(..., rka, ...)
> 	key_instantiate_and_link(rka->target_key, ...)
> 
> I reproduced this on a current upstream v7.1-rc3 based tree,
> HEAD ab5fce87a778c, with KASAN enabled:
> 
> 	BUG: KASAN: slab-use-after-free in keyctl_instantiate_key_common+0x1dc/0x2a0
> 	Read of size 8
> 	Allocated by task:
> 	  request_key_auth_new+0xe0/0x4d0
> 	Freed by task:
> 	  key_revoke+0x62/0xc0
> 	  call_sbin_request_key+0x6cb/0x740
> 
> The reproducer uses a request-key helper that forks a second process with the
> request-key authority.  The second process enters KEYCTL_INSTANTIATE_IOV and
> blocks in copy_from_iter_full() on a user fault after rka has been loaded.  The
> original helper then instantiates the target key and returns, which revokes the
> auth key and queues the request_key_auth payload for RCU freeing.  When the
> blocked instantiate path resumes, it dereferences the stale rka pointer.
> 
> I can provide the reproducer and a candidate patch.
> 
> Regards,
> Shaomin Chen

Please, just send them and we go from there. On surface looks legit.

BR, Jarkko

^ permalink raw reply

* [linux-next:master] BUILD REGRESSION 550604d6c9b9efc8d068aff94dc301694a7afdee
From: kernel test robot @ 2026-05-22  0:33 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Linux Memory Management List, amd-gfx, dri-devel, kexec, keyrings,
	linux-aio, linux-block, linux-fsdevel, linux-gpio, linux-hexagon,
	linux-modules, linux-nfs, linux-perf-users, linux-riscv,
	linux-security-module, linux-serial, linux-um, linuxppc-dev,
	netdev, sparclinux, Mark Brown

tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git master
branch HEAD: 550604d6c9b9efc8d068aff94dc301694a7afdee  Add linux-next specific files for 20260521

Error/Warning (recently discovered and may have been fixed):

    https://lore.kernel.org/oe-kbuild-all/202605212104.SAIMqegX-lkp@intel.com
    https://lore.kernel.org/oe-kbuild-all/202605220247.dQAHslyv-lkp@intel.com
    https://lore.kernel.org/oe-kbuild-all/202605220312.Pu7UO05u-lkp@intel.com
    https://lore.kernel.org/oe-kbuild-all/202605220631.ugDr2VPb-lkp@intel.com

    /usr/bin/ld: sound/soc/codecs/es9356.o:(.rodata+0x2c30): undefined reference to `sdca_asoc_q78_get_volsw'
    /usr/bin/ld: sound/soc/codecs/es9356.o:(.rodata+0x2c38): undefined reference to `sdca_asoc_q78_put_volsw'
    arch/hexagon/kernel/syscalltab.c:19:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    arch/powerpc/kernel/pci_64.c:226:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    arch/powerpc/kernel/rtas.c:1850:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    arch/powerpc/kernel/signal_32.c:990:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    arch/powerpc/kernel/signal_64.c:657:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    arch/powerpc/kernel/sys_ppc32.c:70:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    arch/powerpc/kernel/syscalls.c:52:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    arch/riscv/kernel/sys_hwprobe.c:606:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    arch/riscv/kernel/sys_riscv.c:43:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    arch/sparc/kernel/sys_sparc32.c:54:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    arch/sparc/kernel/sys_sparc_64.c:351:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    arch/x86/um/syscalls_64.c:43:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    block/ioprio.c:65:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    drivers/gpu/drm/amd/amdgpu/../amdkfd/kfd_crat.c:1830:26: error: cannot take the address of an rvalue of type 'int'
    drivers/gpu/drm/amd/amdgpu/../amdkfd/kfd_crat.c:1830:27: error: call to undeclared function 'cpu_data'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    drivers/gpu/drm/amd/amdgpu/../amdkfd/kfd_crat.c:1833:23: error: use of undeclared identifier 'X86_VENDOR_AMD'
    drivers/gpu/drm/amd/amdgpu/../amdkfd/kfd_crat.c:1833:7: error: incomplete definition of type 'struct cpuinfo_x86'
    drivers/gpu/drm/amd/amdgpu/../amdkfd/kfd_topology.c:2353:41: error: member reference base type 'int' is not a structure or union
    drivers/gpu/drm/amd/amdgpu/../amdkfd/kfd_topology.c:2353:9: error: call to undeclared function 'cpu_data'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c:152:68: warning: '%d' directive output may be truncated writing between 1 and 11 bytes into a region of size 7 [-Wformat-truncation=]
    drivers/gpu/drm/scheduler/tests/tests_scheduler.c:675:10: error: initializer element is not a compile-time constant
    drivers/pinctrl/pinctrl-generic.c:130:5: error: redefinition of 'pinctrl_generic_pins_function_dt_node_to_map'
    drivers/pinctrl/pinctrl-generic.c:20:5: error: conflicting types for 'pinctrl_generic_to_map'; have 'int(struct pinctrl_dev *, struct device_node *, struct device_node *, struct pinctrl_map **, unsigned int *, unsigned int *, const char **, unsigned int,  const char **, unsigned int *, unsigned int)'
    fs/aio.c:1436:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/d_path.c:413:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/eventfd.c:414:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/eventpoll.c:2200:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/eventpoll.c:2483:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/exec.c:1924:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/exec.c:1925:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/fcntl.c:587:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/fhandle.c:129:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/file.c:818:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/file.c:819:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/file_attr.c:374:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/fsopen.c:120:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/ioctl.c:583:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/locks.c:2214:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/locks.c:2280:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/namei.c:5186:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/namei.c:5197:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/namespace.c:2068:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/namespace.c:2073:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/notify/inotify/inotify_user.c:720:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/open.c:152:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/pipe.c:1054:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/pipe.c:1055:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/read_write.c:412:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/readdir.c:215:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/readdir.c:304:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/select.c:722:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/select.c:733:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/signalfd.c:299:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/splice.c:1578:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/stat.c:420:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/stat.c:505:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/statfs.c:191:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/sync.c:148:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/timerfd.c:394:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/timerfd.c:424:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/utimes.c:142:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/xattr.c:732:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    fs/xattr.c:735:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    ipc/msg.c:315:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    ipc/sem.c:624:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    ipc/shm.c:847:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    ipc/shm.c:849:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/capability.c:137:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/events/core.c:13844:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/events/core.c:13853:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/exec_domain.c:38:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/exit.c:1082:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/exit.c:1112:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/fork.c:1785:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/fork.c:1808:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/futex/syscalls.c:28:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/groups.c:161:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/kcmp.c:135:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/kexec.c:242:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/module/main.c:804:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/nsproxy.c:569:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/nstree.c:763:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/pid.c:695:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/printk/printk.c:1853:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/ptrace.c:1388:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/ptrace.c:1415:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/reboot.c:728:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/rseq.c:547:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/sched/membarrier.c:634:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/sched/membarrier.c:636:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/sched/syscalls.c:132:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/seccomp.c:2126:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/signal.c:3319:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/sys.c:259:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/time/hrtimer.c:2381:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/time/hrtimer.c:2466:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/time/hrtimer.c:2487:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/time/itimer.c:113:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/time/posix-stubs.c:26:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/time/posix-timers.c:566:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/time/posix-timers.c:574:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/time/time.c:105:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/time/time.c:140:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/time/time.c:62:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    kernel/uid16.c:23:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    ld.lld: error: undefined symbol: sdca_asoc_q78_get_volsw
    ld.lld: error: undefined symbol: sdca_asoc_q78_put_volsw
    mm/fadvise.c:200:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/filemap.c:4713:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/filemap.c:4718:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/madvise.c:2013:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/madvise.c:2029:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/memfd.c:505:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/mincore.c:292:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/mlock.c:665:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/mmap.c:116:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/mprotect.c:985:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/mremap.c:2023:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/msync.c:32:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/oom_kill.c:1195:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/process_vm_access.c:292:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/readahead.c:736:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/readahead.c:760:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/swapfile.c:2903:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    mm/swapfile.c:3124:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    net/socket.c:1818:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    net/socket.c:1833:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    security/keys/compat.c:17:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    security/keys/keyctl.c:74:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    security/landlock/syscalls.c:203:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]
    security/lsm_syscalls.c:57:1: error: unknown warning group '-Wattribute-alias', ignored [-Werror,-Wunknown-warning-option]

Unverified Error/Warning (likely false positive, kindly check if interested):

    Warning: block/blk-map.c:366 Excess function parameter 'op' description in 'bio_copy_kern'
    csky-linux-ld: sound/soc/codecs/es9356.o:(.rodata+0x16c8): undefined reference to `sdca_asoc_q78_get_volsw'
    csky-linux-ld: sound/soc/codecs/es9356.o:(.rodata+0x16cc): undefined reference to `sdca_asoc_q78_put_volsw'
    drivers/android/binder_alloc.c:389:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/auxdisplay/panel.c:1508:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/base/regmap/regmap-debugfs.c:180:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/char/xillybus/xillybus_core.c:425:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/dma/qcom/hidma.c:389:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/gpu/drm/amd/amdgpu/dce_v10_0.c:2853:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c:357:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c:295:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/gpu/drm/udl/udl_main.c:260:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/gpu/drm/xe/xe_sched_job.c:162:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/greybus/es2.c:1428:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/hwmon/ibmpex.c:433:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/media/i2c/ds90ub960.c:4790:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/mtd/parsers/redboot.c:304:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/mtd/spi-nor/core.c:1645:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/net/ethernet/mellanox/mlx5/core/cmd.c:1508:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/scsi/aacraid/commsup.c:1928:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/scsi/aacraid/dpcsup.c:216:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/thunderbolt/stream.c:1388:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/tty/serial/max310x.c:1727:24: error: incompatible pointer types passing 'struct i2c_driver *' to parameter of type 'struct spi_driver *' [-Wincompatible-pointer-types]
    drivers/tty/serial/max310x.c:1727:25: error: use of undeclared identifier 'max310x_spi_driver'; did you mean 'max310x_i2c_driver'?
    drivers/tty/serial/max310x.c:1727:32: error: 'max310x_spi_driver' undeclared (first use in this function); did you mean 'max310x_i2c_driver'?
    drivers/tty/serial/max310x.c:1745:25: error: 'max310x_spi_driver' undeclared (first use in this function); did you mean 'max310x_i2c_driver'?
    drivers/tty/serial/max310x.c:1745:32: error: 'max310x_spi_driver' undeclared (first use in this function); did you mean 'max310x_i2c_driver'?
    drivers/usb/host/xhci-mem.c:866:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/usb/misc/usbtest.c:1416:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    drivers/usb/serial/usb_wwan.c:501:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    fs/btrfs/file.c:3288:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    fs/btrfs/inode.c:8975:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    fs/isofs/inode.c:1267:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    fs/nilfs2/recovery.c:397:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    fs/xfs/scrub/bitmap.c:116:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    kernel/bpf/syscall.c:3797:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    kernel/events/core.c:12131:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    kernel/trace/ring_buffer.c:2348:1: internal compiler error: in final_scan_insn_1, at final.cc:2813
    ld.lld: error: call to __compiletime_assert_537 marked "dontcall-error": BUILD_BUG_ON failed: 21 - 1 != HWEIGHT32( (VALID_OPENAT2_FLAGS & ~(O_NONBLOCK | O_NDELAY)) | __FMODE_EXEC)
    lib/raid/raid6/powerpc/altivec1.c:37:16: sparse: sparse: Trying to use reserved word 'signed' as identifier
    lib/raid/raid6/powerpc/altivec1.c:37:16: sparse: sparse: two or more data types in declaration specifiers
    lib/raid/raid6/powerpc/altivec2.c:37:16: sparse: sparse: Trying to use reserved word 'signed' as identifier
    lib/raid/raid6/powerpc/altivec2.c:37:16: sparse: sparse: two or more data types in declaration specifiers
    lib/raid/raid6/powerpc/altivec4.c:37:16: sparse: sparse: Trying to use reserved word 'signed' as identifier
    lib/raid/raid6/powerpc/altivec4.c:37:16: sparse: sparse: two or more data types in declaration specifiers
    lib/raid/raid6/powerpc/altivec8.c:37:16: sparse: sparse: Trying to use reserved word 'signed' as identifier
    lib/raid/raid6/powerpc/altivec8.c:37:16: sparse: sparse: two or more data types in declaration specifiers
    net/bluetooth/mgmt.c:4400:1: internal compiler error: in final_scan_insn_1, at final.cc:2813

Error/Warning ids grouped by kconfigs:

recent_errors
|-- alpha-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- alpha-allyesconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- alpha-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- alpha-randconfig-r052-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- arc-allmodconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- arc-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- arc-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- arm-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- arm-allyesconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- arm-defconfig
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   |-- block-ioprio.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-aio.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-d_path.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-eventfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-eventpoll.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-exec.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fcntl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fhandle.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-file.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-file_attr.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fsopen.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-ioctl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-locks.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-namei.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-namespace.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-notify-inotify-inotify_user.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-open.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-pipe.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-read_write.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-readdir.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-select.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-signalfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-splice.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-stat.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-statfs.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-sync.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-timerfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-utimes.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-xattr.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- ipc-msg.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- ipc-sem.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- ipc-shm.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-capability.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-events-core.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-exec_domain.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-exit.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-fork.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-futex-syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-groups.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-kcmp.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-kexec.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-module-main.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-nsproxy.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-nstree.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-pid.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-printk-printk.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-ptrace.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-reboot.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-rseq.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sched-membarrier.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sched-syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-seccomp.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-signal.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sys.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-hrtimer.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-itimer.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-posix-timers.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-time.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-uid16.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-fadvise.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-filemap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-madvise.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-memfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mincore.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mlock.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mmap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mprotect.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mremap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-msync.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-oom_kill.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-process_vm_access.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-readahead.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-swapfile.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- net-socket.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   `-- security-keys-keyctl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|-- arm-randconfig-001-20260521
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   `-- block-ioprio.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|-- arm-randconfig-002-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- arm-randconfig-004-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- arm-randconfig-r062-20260521
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   |-- block-ioprio.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-d_path.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-eventfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-eventpoll.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-exec.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fcntl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-file.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-file_attr.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fsopen.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-ioctl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-namei.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-namespace.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-notify-inotify-inotify_user.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-open.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-pipe.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-read_write.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-readdir.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-select.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-signalfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-splice.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-stat.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-statfs.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-sync.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-utimes.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-xattr.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- ipc-msg.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- ipc-sem.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- ipc-shm.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-capability.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-events-core.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-exec_domain.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-exit.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-fork.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-futex-syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-groups.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-module-main.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-nsproxy.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-nstree.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-pid.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-printk-printk.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-ptrace.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-reboot.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-rseq.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sched-membarrier.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sched-syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-signal.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sys.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-hrtimer.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-itimer.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-posix-timers.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-time.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-uid16.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-fadvise.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-filemap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-madvise.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-memfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mincore.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mlock.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mmap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mprotect.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mremap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-msync.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-oom_kill.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-process_vm_access.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-readahead.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- net-socket.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   `-- security-keys-keyctl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|-- arm64-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- arm64-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- arm64-randconfig-001-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- arm64-randconfig-003-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- arm64-randconfig-004
|   |-- ld.lld:error:undefined-symbol:sdca_asoc_q78_get_volsw
|   `-- ld.lld:error:undefined-symbol:sdca_asoc_q78_put_volsw
|-- arm64-randconfig-004-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- arm64-randconfig-r072-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- csky-allmodconfig
|   `-- drivers-thunderbolt-stream.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|-- csky-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- csky-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- csky-randconfig-001-20260521
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   |-- drivers-base-regmap-regmap-debugfs.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-gpu-drm-nouveau-nvkm-subdev-acr-base.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-gpu-drm-nouveau-nvkm-subdev-iccsense-base.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-gpu-drm-udl-udl_main.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-gpu-drm-xe-xe_sched_job.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-greybus-es2.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-mtd-parsers-redboot.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-scsi-aacraid-commsup.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-scsi-aacraid-dpcsup.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-usb-host-xhci-mem.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-usb-misc-usbtest.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-usb-serial-usb_wwan.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- fs-btrfs-file.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- fs-btrfs-inode.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- fs-isofs-inode.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- fs-nilfs2-recovery.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   `-- fs-xfs-scrub-bitmap.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|-- csky-randconfig-002-20260521
|   |-- csky-linux-ld:sound-soc-codecs-es9356.o:(.rodata):undefined-reference-to-sdca_asoc_q78_get_volsw
|   |-- csky-linux-ld:sound-soc-codecs-es9356.o:(.rodata):undefined-reference-to-sdca_asoc_q78_put_volsw
|   |-- drivers-android-binder_alloc.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-auxdisplay-panel.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-char-xillybus-xillybus_core.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-hwmon-ibmpex.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-media-i2c-ds90ub960.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-mtd-spi-nor-core.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- kernel-events-core.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   `-- kernel-trace-ring_buffer.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|-- csky-randconfig-r062-20260521
|   |-- drivers-dma-qcom-hidma.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-gpu-drm-amd-amdgpu-dce_v10_0.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- drivers-net-ethernet-mellanox-mlx5-core-cmd.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   |-- kernel-bpf-syscall.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|   `-- net-bluetooth-mgmt.c:internal-compiler-error:in-final_scan_insn_1-at-final.cc
|-- hexagon-allmodconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- hexagon-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- hexagon-defconfig
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   `-- arch-hexagon-kernel-syscalltab.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|-- hexagon-randconfig-001-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- hexagon-randconfig-002
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-buildonly-randconfig-001
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-buildonly-randconfig-001-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-buildonly-randconfig-003
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-buildonly-randconfig-003-20260521
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   `-- drivers-tty-serial-max31.c:error:use-of-undeclared-identifier-max31_spi_driver
|-- i386-buildonly-randconfig-004
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-buildonly-randconfig-005-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-buildonly-randconfig-006-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-randconfig-001
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-randconfig-001-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-randconfig-002
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-randconfig-002-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-randconfig-003-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-randconfig-004
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-randconfig-005
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-randconfig-006-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-randconfig-007
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-randconfig-051-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-randconfig-052-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-randconfig-054-20260521
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   `-- drivers-tty-serial-max31.c:error:max31_spi_driver-undeclared-(first-use-in-this-function)
|-- i386-randconfig-062-20260522
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- i386-randconfig-063-20260522
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- loongarch-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- loongarch-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- loongarch-randconfig-001
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- loongarch-randconfig-002
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- m68k-allmodconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- m68k-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- m68k-allyesconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- m68k-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- microblaze-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- microblaze-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- microblaze-randconfig-r061-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- mips-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- mips-randconfig-r071-20260521
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   |-- kernel-time-posix-stubs.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   `-- security-keys-keyctl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|-- nios2-allmodconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- nios2-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- nios2-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- nios2-randconfig-001
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- nios2-randconfig-002
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- nios2-randconfig-r054-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- openrisc-allmodconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- openrisc-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- parisc-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- parisc-allyesconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- parisc-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- parisc-randconfig-r073-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- parisc64-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- powerpc-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- powerpc-randconfig-r063-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- powerpc-randconfig-r064-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- powerpc-sam440ep_defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- powerpc64-randconfig-r061-20260522
|   |-- arch-powerpc-kernel-pci_64.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- arch-powerpc-kernel-rtas.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- arch-powerpc-kernel-signal_32.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- arch-powerpc-kernel-signal_64.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- arch-powerpc-kernel-sys_ppc32.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   `-- arch-powerpc-kernel-syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|-- powerpc64-randconfig-r131-20260521
|   |-- lib-raid-raid6-powerpc-altivec1.c:sparse:sparse:Trying-to-use-reserved-word-signed-as-identifier
|   |-- lib-raid-raid6-powerpc-altivec1.c:sparse:sparse:two-or-more-data-types-in-declaration-specifiers
|   |-- lib-raid-raid6-powerpc-altivec2.c:sparse:sparse:Trying-to-use-reserved-word-signed-as-identifier
|   |-- lib-raid-raid6-powerpc-altivec2.c:sparse:sparse:two-or-more-data-types-in-declaration-specifiers
|   |-- lib-raid-raid6-powerpc-altivec4.c:sparse:sparse:Trying-to-use-reserved-word-signed-as-identifier
|   |-- lib-raid-raid6-powerpc-altivec4.c:sparse:sparse:two-or-more-data-types-in-declaration-specifiers
|   |-- lib-raid-raid6-powerpc-altivec8.c:sparse:sparse:Trying-to-use-reserved-word-signed-as-identifier
|   `-- lib-raid-raid6-powerpc-altivec8.c:sparse:sparse:two-or-more-data-types-in-declaration-specifiers
|-- riscv-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- riscv-allyesconfig
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   `-- drivers-gpu-drm-scheduler-tests-tests_scheduler.c:error:initializer-element-is-not-a-compile-time-constant
|-- riscv-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- riscv-randconfig-001-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- riscv-randconfig-002
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- riscv-randconfig-002-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- riscv-randconfig-r131-20260521
|   |-- arch-riscv-kernel-sys_hwprobe.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- arch-riscv-kernel-sys_riscv.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   `-- ld.lld:error:call-to-__compiletime_assert_NNN-marked-dontcall-error:BUILD_BUG_ON-failed:HWEIGHT32(-(VALID_OPENAT2_FLAGS-(O_NONBLOCK-O_NDELAY))-__FMODE_EXEC)
|-- s390-allmodconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- s390-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- s390-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- s390-randconfig-001
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- s390-randconfig-001-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- s390-randconfig-002-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- s390-randconfig-r064-20260522
|   `-- drivers-tty-serial-max31.c:error:incompatible-pointer-types-passing-struct-i2c_driver-to-parameter-of-type-struct-spi_driver
|-- sh-allmodconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sh-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sh-allyesconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sh-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sh-randconfig-001
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sh-randconfig-001-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sh-randconfig-002
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sh-randconfig-002-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sparc-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sparc-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sparc-randconfig-001
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sparc-randconfig-001-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sparc-randconfig-002
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sparc-randconfig-002-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sparc64-allmodconfig
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   |-- arch-sparc-kernel-sys_sparc32.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- arch-sparc-kernel-sys_sparc_64.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- block-ioprio.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-aio.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-d_path.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-eventfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-eventpoll.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-exec.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fcntl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fhandle.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-file.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-file_attr.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fsopen.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-ioctl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-locks.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-namei.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-namespace.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-notify-inotify-inotify_user.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-open.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-pipe.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-read_write.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-readdir.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-select.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-signalfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-splice.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-stat.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-statfs.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-sync.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-timerfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-utimes.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-xattr.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- ipc-msg.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- ipc-sem.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- ipc-shm.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-capability.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-events-core.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-exec_domain.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-exit.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-fork.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-futex-syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-groups.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-kcmp.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-module-main.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-nsproxy.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-nstree.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-pid.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-printk-printk.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-ptrace.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-reboot.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sched-membarrier.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sched-syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-seccomp.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-signal.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sys.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-hrtimer.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-itimer.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-posix-timers.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-time.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-uid16.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-fadvise.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-filemap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-madvise.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-memfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mincore.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mlock.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mmap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mprotect.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mremap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-msync.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-oom_kill.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-process_vm_access.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-readahead.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-swapfile.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- net-socket.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- security-keys-compat.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- security-landlock-syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   `-- security-lsm_syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|-- sparc64-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sparc64-randconfig-001
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- sparc64-randconfig-001-20260521
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   `-- drivers-tty-serial-max31.c:error:max31_spi_driver-undeclared-(first-use-in-this-function)
|-- sparc64-randconfig-002
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   |-- arch-sparc-kernel-sys_sparc32.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- arch-sparc-kernel-sys_sparc_64.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- block-ioprio.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-aio.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-d_path.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-eventfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-eventpoll.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-exec.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fcntl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fhandle.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-file.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-file_attr.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fsopen.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-ioctl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-locks.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-namei.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-namespace.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-notify-inotify-inotify_user.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-open.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-pipe.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-read_write.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-readdir.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-select.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-signalfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-splice.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-stat.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-statfs.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-sync.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-timerfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-utimes.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-xattr.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-capability.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-events-core.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-exec_domain.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-exit.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-fork.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-futex-syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-groups.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-kcmp.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-nsproxy.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-nstree.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-pid.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-printk-printk.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-ptrace.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-reboot.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sched-membarrier.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sched-syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-seccomp.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-signal.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sys.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-hrtimer.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-itimer.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-posix-timers.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-time.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-uid16.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-fadvise.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-filemap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-madvise.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mincore.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mlock.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mmap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mprotect.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mremap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-msync.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-oom_kill.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-process_vm_access.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-readahead.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- net-socket.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- security-keys-compat.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- security-keys-keyctl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   `-- security-lsm_syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|-- sparc64-randconfig-002-20260521
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   `-- drivers-tty-serial-max31.c:error:max31_spi_driver-undeclared-(first-use-in-this-function)
|-- um-allmodconfig
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   |-- drivers-gpu-drm-amd-amdgpu-..-amdkfd-kfd_crat.c:error:call-to-undeclared-function-cpu_data-ISO-C99-and-later-do-not-support-implicit-function-declarations
|   |-- drivers-gpu-drm-amd-amdgpu-..-amdkfd-kfd_crat.c:error:cannot-take-the-address-of-an-rvalue-of-type-int
|   |-- drivers-gpu-drm-amd-amdgpu-..-amdkfd-kfd_crat.c:error:incomplete-definition-of-type-struct-cpuinfo_x86
|   |-- drivers-gpu-drm-amd-amdgpu-..-amdkfd-kfd_crat.c:error:use-of-undeclared-identifier-X86_VENDOR_AMD
|   |-- drivers-gpu-drm-amd-amdgpu-..-amdkfd-kfd_topology.c:error:call-to-undeclared-function-cpu_data-ISO-C99-and-later-do-not-support-implicit-function-declarations
|   `-- drivers-gpu-drm-amd-amdgpu-..-amdkfd-kfd_topology.c:error:member-reference-base-type-int-is-not-a-structure-or-union
|-- um-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- um-allyesconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- um-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- um-i386_defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- um-randconfig-001
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   |-- block-ioprio.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-d_path.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-eventfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-eventpoll.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-exec.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fcntl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fhandle.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-file.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-file_attr.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fsopen.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-ioctl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-namei.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-namespace.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-open.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-pipe.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-read_write.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-readdir.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-select.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-splice.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-stat.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-statfs.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-sync.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-timerfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-utimes.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-xattr.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-exec_domain.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-exit.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-fork.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-kcmp.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-nsproxy.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-nstree.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-pid.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-printk-printk.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-ptrace.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-reboot.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sched-syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-signal.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sys.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-hrtimer.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-posix-stubs.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-time.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mincore.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mlock.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mmap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mprotect.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mremap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-msync.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-oom_kill.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-process_vm_access.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-readahead.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- net-socket.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   `-- security-keys-keyctl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|-- um-randconfig-001-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- um-randconfig-002
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   |-- arch-x86-um-syscalls_64.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- block-ioprio.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-aio.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-d_path.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-eventfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-eventpoll.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-exec.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fcntl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fhandle.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-file.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-file_attr.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-fsopen.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-ioctl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-locks.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-namei.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-namespace.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-notify-inotify-inotify_user.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-open.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-pipe.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-read_write.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-readdir.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-select.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-signalfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-splice.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-stat.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-statfs.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-sync.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-timerfd.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-utimes.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- fs-xattr.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-capability.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-exec_domain.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-exit.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-fork.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-futex-syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-groups.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-module-main.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-nsproxy.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-nstree.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-pid.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-printk-printk.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-ptrace.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-reboot.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sched-membarrier.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sched-syscalls.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-seccomp.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-signal.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-sys.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-hrtimer.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-itimer.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-posix-timers.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-time-time.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- kernel-uid16.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-fadvise.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-filemap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-madvise.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mincore.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mlock.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mmap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mprotect.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-mremap.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-msync.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-oom_kill.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-process_vm_access.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-readahead.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   |-- mm-swapfile.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|   `-- security-keys-keyctl.c:error:unknown-warning-group-Wattribute-alias-ignored-Werror-Wunknown-warning-option
|-- um-randconfig-002-20260521
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   |-- drivers-tty-serial-max31.c:error:max31_spi_driver-undeclared-(first-use-in-this-function)
|   |-- usr-bin-ld:sound-soc-codecs-es9356.o:(.rodata):undefined-reference-to-sdca_asoc_q78_get_volsw
|   `-- usr-bin-ld:sound-soc-codecs-es9356.o:(.rodata):undefined-reference-to-sdca_asoc_q78_put_volsw
|-- um-randconfig-r053-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- um-x86_64_defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-buildonly-randconfig-002-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-buildonly-randconfig-003-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-buildonly-randconfig-004-20260521
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   |-- drivers-gpu-drm-amd-amdgpu-jpeg_v2_5.c:warning:d-directive-output-may-be-truncated-writing-between-and-bytes-into-a-region-of-size
|   |-- drivers-pinctrl-pinctrl-generic.c:error:conflicting-types-for-pinctrl_generic_to_map-have-int(struct-pinctrl_dev-struct-device_node-struct-device_node-struct-pinctrl_map-unsigned-int-unsigned-int-cons
|   |-- drivers-pinctrl-pinctrl-generic.c:error:redefinition-of-pinctrl_generic_pins_function_dt_node_to_map
|   `-- drivers-tty-serial-max31.c:error:max31_spi_driver-undeclared-(first-use-in-this-function)
|-- x86_64-buildonly-randconfig-005-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-defconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-kexec
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-001-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-002-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-004-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-006-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-011-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-012-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-013-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-014-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-015-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-016-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-071
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-071-20260521
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   `-- drivers-pinctrl-pinctrl-generic.c:error:redefinition-of-pinctrl_generic_pins_function_dt_node_to_map
|-- x86_64-randconfig-072
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-072-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-073-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-074
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-074-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-075
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-076
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-076-20260521
|   `-- drivers-pinctrl-pinctrl-generic.c:error:redefinition-of-pinctrl_generic_pins_function_dt_node_to_map
|-- x86_64-randconfig-101
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-101-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-102
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-102-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-103
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-104
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-104-20260521
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-121-20260522
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-122-20260522
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-randconfig-123-20260522
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   `-- drivers-pinctrl-pinctrl-generic.c:error:redefinition-of-pinctrl_generic_pins_function_dt_node_to_map
|-- x86_64-randconfig-161
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-rhel-9.4
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-rhel-9.4-bpf
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-rhel-9.4-func
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-rhel-9.4-kselftests
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-rhel-9.4-kunit
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- x86_64-rhel-9.4-ltp
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- xtensa-allnoconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- xtensa-allyesconfig
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- xtensa-randconfig-001
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|-- xtensa-randconfig-001-20260521
|   |-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
|   `-- drivers-tty-serial-max31.c:error:max31_spi_driver-undeclared-(first-use-in-this-function)
|-- xtensa-randconfig-002
|   `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern
`-- xtensa-randconfig-002-20260521
    `-- Warning:block-blk-map.c-Excess-function-parameter-op-description-in-bio_copy_kern

elapsed time: 724m

configs tested: 282
configs skipped: 11

tested configs:
alpha                             allnoconfig    gcc-15.2.0
alpha                            allyesconfig    gcc-15.2.0
alpha                               defconfig    gcc-15.2.0
arc                              allmodconfig    clang-16
arc                              allmodconfig    gcc-15.2.0
arc                               allnoconfig    gcc-15.2.0
arc                              allyesconfig    clang-23
arc                              allyesconfig    gcc-15.2.0
arc                                 defconfig    gcc-15.2.0
arc                            randconfig-001    gcc-8.5.0
arc                   randconfig-001-20260521    gcc-13.4.0
arc                   randconfig-001-20260521    gcc-8.5.0
arc                            randconfig-002    gcc-8.5.0
arc                   randconfig-002-20260521    gcc-12.5.0
arc                   randconfig-002-20260521    gcc-8.5.0
arm                               allnoconfig    clang-23
arm                               allnoconfig    gcc-15.2.0
arm                              allyesconfig    clang-16
arm                              allyesconfig    gcc-15.2.0
arm                                 defconfig    clang-23
arm                                 defconfig    gcc-15.2.0
arm                            randconfig-001    gcc-8.5.0
arm                   randconfig-001-20260521    clang-23
arm                   randconfig-001-20260521    gcc-8.5.0
arm                            randconfig-002    gcc-11.5.0
arm                   randconfig-002-20260521    gcc-12.5.0
arm                   randconfig-002-20260521    gcc-8.5.0
arm                            randconfig-003    clang-20
arm                   randconfig-003-20260521    gcc-13.4.0
arm                   randconfig-003-20260521    gcc-8.5.0
arm                            randconfig-004    gcc-14.3.0
arm                   randconfig-004-20260521    gcc-8.5.0
arm64                            allmodconfig    clang-19
arm64                            allmodconfig    clang-23
arm64                             allnoconfig    gcc-15.2.0
arm64                               defconfig    gcc-15.2.0
arm64                 randconfig-001-20260521    clang-23
arm64                 randconfig-001-20260521    gcc-8.5.0
arm64                 randconfig-002-20260521    gcc-8.5.0
arm64                 randconfig-003-20260521    gcc-8.5.0
arm64                 randconfig-004-20260521    gcc-8.5.0
csky                             allmodconfig    gcc-15.2.0
csky                              allnoconfig    gcc-15.2.0
csky                                defconfig    gcc-15.2.0
csky                  randconfig-001-20260521    gcc-15.2.0
csky                  randconfig-001-20260521    gcc-8.5.0
csky                  randconfig-002-20260521    gcc-15.2.0
csky                  randconfig-002-20260521    gcc-8.5.0
hexagon                          allmodconfig    clang-17
hexagon                          allmodconfig    gcc-15.2.0
hexagon                           allnoconfig    clang-23
hexagon                           allnoconfig    gcc-15.2.0
hexagon                             defconfig    clang-23
hexagon                             defconfig    gcc-15.2.0
hexagon                        randconfig-001    gcc-11.5.0
hexagon               randconfig-001-20260521    clang-23
hexagon               randconfig-001-20260521    gcc-11.5.0
hexagon                        randconfig-002    gcc-11.5.0
hexagon               randconfig-002-20260521    clang-23
hexagon               randconfig-002-20260521    gcc-11.5.0
i386                             allmodconfig    clang-20
i386                             allmodconfig    gcc-14
i386                              allnoconfig    gcc-14
i386                              allnoconfig    gcc-15.2.0
i386                             allyesconfig    clang-20
i386                             allyesconfig    gcc-14
i386                 buildonly-randconfig-001    clang-20
i386        buildonly-randconfig-001-20260521    clang-20
i386        buildonly-randconfig-001-20260522    clang-20
i386                 buildonly-randconfig-002    clang-20
i386        buildonly-randconfig-002-20260521    clang-20
i386        buildonly-randconfig-002-20260522    gcc-14
i386                 buildonly-randconfig-003    clang-20
i386        buildonly-randconfig-003-20260521    clang-20
i386        buildonly-randconfig-003-20260522    clang-20
i386                 buildonly-randconfig-004    clang-20
i386        buildonly-randconfig-004-20260521    clang-20
i386        buildonly-randconfig-004-20260522    gcc-14
i386                 buildonly-randconfig-005    clang-20
i386        buildonly-randconfig-005-20260521    clang-20
i386        buildonly-randconfig-005-20260522    clang-20
i386                 buildonly-randconfig-006    clang-20
i386        buildonly-randconfig-006-20260521    clang-20
i386        buildonly-randconfig-006-20260522    gcc-13
i386                                defconfig    clang-20
i386                                defconfig    gcc-15.2.0
i386                  randconfig-001-20260521    clang-20
i386                  randconfig-002-20260521    gcc-14
i386                  randconfig-003-20260521    clang-20
i386                  randconfig-004-20260521    gcc-14
i386                  randconfig-005-20260521    clang-20
i386                  randconfig-006-20260521    clang-20
i386                  randconfig-007-20260521    clang-20
i386                  randconfig-011-20260521    gcc-14
i386                  randconfig-012-20260521    clang-20
i386                  randconfig-013-20260521    gcc-13
i386                  randconfig-014-20260521    gcc-14
i386                  randconfig-015-20260521    gcc-14
i386                  randconfig-016-20260521    clang-20
i386                  randconfig-017-20260521    clang-20
loongarch                        allmodconfig    clang-19
loongarch                        allmodconfig    clang-23
loongarch                         allnoconfig    clang-23
loongarch                         allnoconfig    gcc-15.2.0
loongarch                           defconfig    clang-19
loongarch                      randconfig-001    gcc-11.5.0
loongarch             randconfig-001-20260521    gcc-11.5.0
loongarch             randconfig-001-20260521    gcc-12.5.0
loongarch                      randconfig-002    gcc-11.5.0
loongarch             randconfig-002-20260521    gcc-11.5.0
loongarch             randconfig-002-20260521    gcc-15.2.0
m68k                             allmodconfig    gcc-15.2.0
m68k                              allnoconfig    gcc-15.2.0
m68k                             allyesconfig    clang-16
m68k                             allyesconfig    gcc-15.2.0
m68k                                defconfig    clang-19
m68k                                defconfig    gcc-15.2.0
microblaze                        allnoconfig    gcc-15.2.0
microblaze                       allyesconfig    gcc-15.2.0
microblaze                          defconfig    clang-19
microblaze                          defconfig    gcc-15.2.0
mips                             allmodconfig    gcc-15.2.0
mips                              allnoconfig    gcc-15.2.0
mips                             allyesconfig    gcc-15.2.0
nios2                            allmodconfig    clang-23
nios2                            allmodconfig    gcc-11.5.0
nios2                             allnoconfig    clang-23
nios2                             allnoconfig    gcc-11.5.0
nios2                               defconfig    clang-19
nios2                               defconfig    gcc-11.5.0
nios2                          randconfig-001    gcc-11.5.0
nios2                 randconfig-001-20260521    gcc-11.5.0
nios2                          randconfig-002    gcc-11.5.0
nios2                 randconfig-002-20260521    gcc-11.5.0
openrisc                         allmodconfig    clang-23
openrisc                         allmodconfig    gcc-15.2.0
openrisc                          allnoconfig    clang-23
openrisc                          allnoconfig    gcc-15.2.0
openrisc                            defconfig    gcc-15.2.0
parisc                           allmodconfig    gcc-15.2.0
parisc                            allnoconfig    clang-23
parisc                            allnoconfig    gcc-15.2.0
parisc                           allyesconfig    clang-19
parisc                           allyesconfig    gcc-15.2.0
parisc                              defconfig    gcc-15.2.0
parisc                randconfig-001-20260521    gcc-12.5.0
parisc                randconfig-002-20260521    gcc-15.2.0
parisc64                            defconfig    clang-19
parisc64                            defconfig    gcc-15.2.0
powerpc                          allmodconfig    gcc-15.2.0
powerpc                           allnoconfig    clang-23
powerpc                           allnoconfig    gcc-15.2.0
powerpc               randconfig-001-20260521    clang-23
powerpc               randconfig-002-20260521    clang-17
powerpc                    sam440ep_defconfig    gcc-15.2.0
powerpc64             randconfig-001-20260521    clang-23
powerpc64             randconfig-002-20260521    gcc-12.5.0
riscv                            allmodconfig    clang-23
riscv                             allnoconfig    clang-23
riscv                             allnoconfig    gcc-15.2.0
riscv                            allyesconfig    clang-16
riscv                               defconfig    gcc-15.2.0
riscv                          randconfig-001    gcc-15.2.0
riscv                 randconfig-001-20260521    clang-23
riscv                 randconfig-001-20260521    gcc-15.2.0
riscv                          randconfig-002    gcc-15.2.0
riscv                 randconfig-002-20260521    gcc-13.4.0
riscv                 randconfig-002-20260521    gcc-15.2.0
s390                             allmodconfig    clang-18
s390                             allmodconfig    clang-19
s390                              allnoconfig    clang-23
s390                             allyesconfig    gcc-15.2.0
s390                                defconfig    gcc-15.2.0
s390                           randconfig-001    gcc-15.2.0
s390                  randconfig-001-20260521    gcc-15.2.0
s390                  randconfig-001-20260521    gcc-8.5.0
s390                           randconfig-002    gcc-15.2.0
s390                  randconfig-002-20260521    clang-17
s390                  randconfig-002-20260521    gcc-15.2.0
sh                               allmodconfig    gcc-15.2.0
sh                                allnoconfig    clang-23
sh                                allnoconfig    gcc-15.2.0
sh                               allyesconfig    clang-19
sh                               allyesconfig    gcc-15.2.0
sh                                  defconfig    gcc-14
sh                                  defconfig    gcc-15.2.0
sh                             randconfig-001    gcc-15.2.0
sh                    randconfig-001-20260521    gcc-15.2.0
sh                             randconfig-002    gcc-15.2.0
sh                    randconfig-002-20260521    gcc-12.5.0
sh                    randconfig-002-20260521    gcc-15.2.0
sh                          rsk7264_defconfig    gcc-15.2.0
sparc                             allnoconfig    clang-23
sparc                             allnoconfig    gcc-15.2.0
sparc                               defconfig    gcc-15.2.0
sparc                          randconfig-001    gcc-8.5.0
sparc                 randconfig-001-20260521    gcc-8.5.0
sparc                          randconfig-002    gcc-8.5.0
sparc                 randconfig-002-20260521    gcc-8.5.0
sparc64                          allmodconfig    clang-23
sparc64                             defconfig    clang-20
sparc64                             defconfig    gcc-14
sparc64                        randconfig-001    gcc-8.5.0
sparc64               randconfig-001-20260521    gcc-8.5.0
sparc64                        randconfig-002    gcc-8.5.0
sparc64               randconfig-002-20260521    gcc-8.5.0
um                               allmodconfig    clang-19
um                                allnoconfig    clang-23
um                               allyesconfig    gcc-14
um                               allyesconfig    gcc-15.2.0
um                                  defconfig    clang-23
um                                  defconfig    gcc-14
um                             i386_defconfig    gcc-14
um                             randconfig-001    gcc-8.5.0
um                    randconfig-001-20260521    gcc-14
um                    randconfig-001-20260521    gcc-8.5.0
um                             randconfig-002    gcc-8.5.0
um                    randconfig-002-20260521    gcc-14
um                    randconfig-002-20260521    gcc-8.5.0
um                           x86_64_defconfig    clang-23
um                           x86_64_defconfig    gcc-14
x86_64                           allmodconfig    clang-20
x86_64                            allnoconfig    clang-20
x86_64                            allnoconfig    clang-23
x86_64                           allyesconfig    clang-20
x86_64      buildonly-randconfig-001-20260521    clang-20
x86_64      buildonly-randconfig-001-20260521    gcc-12
x86_64      buildonly-randconfig-002-20260521    clang-20
x86_64      buildonly-randconfig-002-20260521    gcc-14
x86_64      buildonly-randconfig-003-20260521    clang-20
x86_64      buildonly-randconfig-004-20260521    clang-20
x86_64      buildonly-randconfig-004-20260521    gcc-14
x86_64      buildonly-randconfig-005-20260521    clang-20
x86_64      buildonly-randconfig-006-20260521    clang-20
x86_64                              defconfig    gcc-14
x86_64                                  kexec    clang-20
x86_64                randconfig-001-20260521    clang-20
x86_64                randconfig-002-20260521    clang-20
x86_64                randconfig-003-20260521    clang-20
x86_64                randconfig-003-20260521    gcc-14
x86_64                randconfig-004-20260521    clang-20
x86_64                randconfig-005-20260521    clang-20
x86_64                randconfig-006-20260521    clang-20
x86_64                randconfig-011-20260521    clang-20
x86_64                randconfig-011-20260521    gcc-14
x86_64                randconfig-012-20260521    clang-20
x86_64                randconfig-012-20260521    gcc-14
x86_64                randconfig-013-20260521    clang-20
x86_64                randconfig-013-20260521    gcc-14
x86_64                randconfig-014-20260521    clang-20
x86_64                randconfig-014-20260521    gcc-14
x86_64                randconfig-015-20260521    clang-20
x86_64                randconfig-015-20260521    gcc-14
x86_64                randconfig-016-20260521    gcc-14
x86_64                         randconfig-071    gcc-14
x86_64                randconfig-071-20260521    clang-20
x86_64                         randconfig-072    gcc-14
x86_64                randconfig-072-20260521    clang-20
x86_64                         randconfig-073    clang-20
x86_64                randconfig-073-20260521    clang-20
x86_64                         randconfig-074    gcc-14
x86_64                randconfig-074-20260521    gcc-14
x86_64                         randconfig-075    gcc-13
x86_64                randconfig-075-20260521    clang-20
x86_64                         randconfig-076    clang-20
x86_64                randconfig-076-20260521    clang-20
x86_64                               rhel-9.4    clang-20
x86_64                           rhel-9.4-bpf    gcc-14
x86_64                          rhel-9.4-func    clang-20
x86_64                    rhel-9.4-kselftests    clang-20
x86_64                         rhel-9.4-kunit    gcc-14
x86_64                           rhel-9.4-ltp    gcc-14
x86_64                          rhel-9.4-rust    clang-20
xtensa                            allnoconfig    clang-23
xtensa                            allnoconfig    gcc-15.2.0
xtensa                           allyesconfig    clang-23
xtensa                           allyesconfig    gcc-15.2.0
xtensa                         randconfig-001    gcc-8.5.0
xtensa                randconfig-001-20260521    gcc-8.5.0
xtensa                         randconfig-002    gcc-8.5.0
xtensa                randconfig-002-20260521    gcc-11.5.0
xtensa                randconfig-002-20260521    gcc-8.5.0

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* [PATCH] tpm-buf: memory-safe allocations
From: Jarkko Sakkinen @ 2026-05-22  1:35 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, Arun Menon, Daniel P. Smith, Alec Brown,
	Ross Philipson, Stefan Berger, Peter Huewe, Jarkko Sakkinen,
	Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn, linux-kernel, keyrings,
	linux-security-module

From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>

Decouple kzalloc from buffer creation, so that a managed allocation can be
used:

	struct tpm_buf *buf __free(kfree) buf = kzalloc(TPM_BUFSIZE,
						GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	tpm_buf_init(buf, TPM_BUFSIZE);

Alternatively, stack allocations are also possible:

	u8 buf_data[512];
	struct tpm_buf *buf = (struct tpm_buf *)buf_data;
	tpm_buf_init(buf, sizeof(buf_data));

This is achieved by embedding buffer's header inside the allocated blob,
instead of having an outer wrapper.

Cc: Arun Menon <armenon@redhat.com>
Cc: Daniel P. Smith <dpsmith@apertussolutions.com>
Cc: Alec Brown <alec.r.brown@oracle.com>
Cc: Ross Philipson <ross.philipson@gmail.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
---
Rebased the managed allocations patch, which has been probably like a
year in circulation.
 drivers/char/tpm/tpm-buf.c                | 122 ++++++----
 drivers/char/tpm/tpm-sysfs.c              |  17 +-
 drivers/char/tpm/tpm.h                    |   1 -
 drivers/char/tpm/tpm1-cmd.c               | 150 ++++++------
 drivers/char/tpm/tpm2-cmd.c               | 277 +++++++++++-----------
 drivers/char/tpm/tpm2-sessions.c          | 149 ++++++------
 drivers/char/tpm/tpm2-space.c             |  44 ++--
 drivers/char/tpm/tpm_vtpm_proxy.c         |  30 +--
 include/linux/tpm.h                       |  18 +-
 security/keys/trusted-keys/trusted_tpm1.c |  44 ++--
 security/keys/trusted-keys/trusted_tpm2.c | 165 ++++++-------
 11 files changed, 505 insertions(+), 512 deletions(-)

diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
index dc882fc9fa9e..b16d824ef0af 100644
--- a/drivers/char/tpm/tpm-buf.c
+++ b/drivers/char/tpm/tpm-buf.c
@@ -7,82 +7,110 @@
 #include <linux/module.h>
 #include <linux/tpm.h>

-/**
- * tpm_buf_init() - Allocate and initialize a TPM command
- * @buf:	A &tpm_buf
- * @tag:	TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
- * @ordinal:	A command ordinal
- *
- * Return: 0 or -ENOMEM
- */
-int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
+static void __tpm_buf_size_invariant(struct tpm_buf *buf, u16 buf_size)
 {
-	buf->data = (u8 *)__get_free_page(GFP_KERNEL);
-	if (!buf->data)
-		return -ENOMEM;
-
-	tpm_buf_reset(buf, tag, ordinal);
-	return 0;
+	u32 buf_size_2 = (u32)buf->capacity + (u32)sizeof(*buf);
+
+	if (!buf->capacity) {
+		if (buf_size > TPM_BUFSIZE) {
+			WARN(1, "%s: size overflow: %u\n", __func__, buf_size);
+			buf->flags |= TPM_BUF_OVERFLOW;
+		}
+	} else {
+		if (buf_size != buf_size_2) {
+			WARN(1, "%s: size mismatch: %u != %u\n", __func__,
+			     buf_size, buf_size_2);
+			buf->flags |= TPM_BUF_OVERFLOW;
+		}
+	}
 }
-EXPORT_SYMBOL_GPL(tpm_buf_init);

-/**
- * tpm_buf_reset() - Initialize a TPM command
- * @buf:	A &tpm_buf
- * @tag:	TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
- * @ordinal:	A command ordinal
- */
-void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
+static void __tpm_buf_reset(struct tpm_buf *buf, u16 buf_size, u16 tag,
+			    u32 ordinal)
 {
 	struct tpm_header *head = (struct tpm_header *)buf->data;

+	__tpm_buf_size_invariant(buf, buf_size);
+
+	if (buf->flags & TPM_BUF_OVERFLOW)
+		return;
+
 	WARN_ON(tag != TPM_TAG_RQU_COMMAND && tag != TPM2_ST_NO_SESSIONS &&
 		tag != TPM2_ST_SESSIONS && tag != 0);

 	buf->flags = 0;
 	buf->length = sizeof(*head);
+	buf->capacity = buf_size - sizeof(*buf);
+	buf->handles = 0;
 	head->tag = cpu_to_be16(tag);
 	head->length = cpu_to_be32(sizeof(*head));
 	head->ordinal = cpu_to_be32(ordinal);
+}
+
+static void __tpm_buf_reset_sized(struct tpm_buf *buf, u16 buf_size)
+{
+	__tpm_buf_size_invariant(buf, buf_size);
+
+	if (buf->flags & TPM_BUF_OVERFLOW)
+		return;
+
+	buf->flags = TPM_BUF_TPM2B;
+	buf->length = 2;
+	buf->capacity = buf_size - sizeof(*buf);
 	buf->handles = 0;
+	buf->data[0] = 0;
+	buf->data[1] = 0;
 }
-EXPORT_SYMBOL_GPL(tpm_buf_reset);

 /**
- * tpm_buf_init_sized() - Allocate and initialize a sized (TPM2B) buffer
- * @buf:	A @tpm_buf
- *
- * Return: 0 or -ENOMEM
+ * tpm_buf_init() - Initialize a TPM command
+ * @buf:	A &tpm_buf
+ * @buf_size:	Size of the buffer.
  */
-int tpm_buf_init_sized(struct tpm_buf *buf)
+void tpm_buf_init(struct tpm_buf *buf, u16 buf_size)
 {
-	buf->data = (u8 *)__get_free_page(GFP_KERNEL);
-	if (!buf->data)
-		return -ENOMEM;
+	memset(buf, 0, buf_size);
+	__tpm_buf_reset(buf, buf_size, TPM_TAG_RQU_COMMAND, 0);
+}
+EXPORT_SYMBOL_GPL(tpm_buf_init);

-	tpm_buf_reset_sized(buf);
-	return 0;
+/**
+ * tpm_buf_init_sized() - Initialize a sized buffer
+ * @buf:	A &tpm_buf
+ * @buf_size:	Size of the buffer.
+ */
+void tpm_buf_init_sized(struct tpm_buf *buf, u16 buf_size)
+{
+	memset(buf, 0, buf_size);
+	__tpm_buf_reset_sized(buf, buf_size);
 }
 EXPORT_SYMBOL_GPL(tpm_buf_init_sized);

 /**
- * tpm_buf_reset_sized() - Initialize a sized buffer
+ * tpm_buf_reset() - Re-initialize a TPM command
  * @buf:	A &tpm_buf
+ * @tag:	TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
+ * @ordinal:	A command ordinal
  */
-void tpm_buf_reset_sized(struct tpm_buf *buf)
+void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
 {
-	buf->flags = TPM_BUF_TPM2B;
-	buf->length = 2;
-	buf->data[0] = 0;
-	buf->data[1] = 0;
+	u16 buf_size = buf->capacity + sizeof(*buf);
+
+	__tpm_buf_reset(buf, buf_size, tag, ordinal);
 }
-EXPORT_SYMBOL_GPL(tpm_buf_reset_sized);
+EXPORT_SYMBOL_GPL(tpm_buf_reset);

-void tpm_buf_destroy(struct tpm_buf *buf)
+/**
+ * tpm_buf_reset_sized() - Re-initialize a sized buffer
+ * @buf:	A &tpm_buf
+ */
+void tpm_buf_reset_sized(struct tpm_buf *buf)
 {
-	free_page((unsigned long)buf->data);
+	u16 buf_size = buf->capacity + sizeof(*buf);
+
+	__tpm_buf_reset_sized(buf, buf_size);
 }
-EXPORT_SYMBOL_GPL(tpm_buf_destroy);
+EXPORT_SYMBOL_GPL(tpm_buf_reset_sized);

 /**
  * tpm_buf_length() - Return the number of bytes consumed by the data
@@ -90,7 +118,7 @@ EXPORT_SYMBOL_GPL(tpm_buf_destroy);
  *
  * Return: The number of bytes consumed by the buffer
  */
-u32 tpm_buf_length(struct tpm_buf *buf)
+u16 tpm_buf_length(struct tpm_buf *buf)
 {
 	return buf->length;
 }
@@ -104,11 +132,13 @@ EXPORT_SYMBOL_GPL(tpm_buf_length);
  */
 void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length)
 {
+	u32 total_length = (u32)buf->length + (u32)new_length;
+
 	/* Return silently if overflow has already happened. */
 	if (buf->flags & TPM_BUF_OVERFLOW)
 		return;

-	if ((buf->length + new_length) > PAGE_SIZE) {
+	if (total_length > (u32)buf->capacity) {
 		WARN(1, "tpm_buf: write overflow\n");
 		buf->flags |= TPM_BUF_OVERFLOW;
 		return;
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index 94231f052ea7..1de03cf340b3 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -32,28 +32,31 @@ struct tpm_readpubek_out {
 static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
 			  char *buf)
 {
-	struct tpm_buf tpm_buf;
 	struct tpm_readpubek_out *out;
 	int i;
 	char *str = buf;
 	struct tpm_chip *chip = to_tpm_chip(dev);
 	char anti_replay[20];
+	struct tpm_buf *tpm_buf __free(kfree) = NULL;

 	memset(&anti_replay, 0, sizeof(anti_replay));

 	if (tpm_try_get_ops(chip))
 		return 0;

-	if (tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK))
+	tpm_buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!tpm_buf)
 		goto out_ops;

-	tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay));
+	tpm_buf_init(tpm_buf, TPM_BUFSIZE);
+	tpm_buf_reset(tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK);
+	tpm_buf_append(tpm_buf, anti_replay, sizeof(anti_replay));

-	if (tpm_transmit_cmd(chip, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE,
+	if (tpm_transmit_cmd(chip, tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE,
 			     "attempting to read the PUBEK"))
-		goto out_buf;
+		goto out_ops;

-	out = (struct tpm_readpubek_out *)&tpm_buf.data[10];
+	out = (struct tpm_readpubek_out *)&tpm_buf->data[10];
 	str +=
 	    sprintf(str,
 		    "Algorithm: %4ph\n"
@@ -71,8 +74,6 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
 	for (i = 0; i < 256; i += 16)
 		str += sprintf(str, "%16ph\n", &out->modulus[i]);

-out_buf:
-	tpm_buf_destroy(&tpm_buf);
 out_ops:
 	tpm_put_ops(chip);
 	return str - buf;
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 87d68ddf270a..03f5346343ab 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -33,7 +33,6 @@
 #endif

 #define TPM_MINOR		224	/* officially assigned */
-#define TPM_BUFSIZE		4096
 #define TPM_NUM_DEVICES		65536
 #define TPM_RETRY		50

diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
index b49a790f1bd5..6facc3de2c46 100644
--- a/drivers/char/tpm/tpm1-cmd.c
+++ b/drivers/char/tpm/tpm1-cmd.c
@@ -323,20 +323,18 @@ unsigned long tpm1_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
  */
 static int tpm1_startup(struct tpm_chip *chip)
 {
-	struct tpm_buf buf;
-	int rc;
+	struct tpm_buf *buf __free(kfree) = NULL;

 	dev_info(&chip->dev, "starting up the TPM manually\n");

-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_STARTUP);
-	if (rc < 0)
-		return rc;
-
-	tpm_buf_append_u16(&buf, TPM_ST_CLEAR);
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;

-	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
-	tpm_buf_destroy(&buf);
-	return rc;
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_STARTUP);
+	tpm_buf_append_u16(buf, TPM_ST_CLEAR);
+	return tpm_transmit_cmd(chip, buf, 0, "attempting to start the TPM");
 }

 int tpm1_get_timeouts(struct tpm_chip *chip)
@@ -463,50 +461,47 @@ int tpm1_get_timeouts(struct tpm_chip *chip)
 int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
 		    const char *log_msg)
 {
-	struct tpm_buf buf;
-	int rc;
-
-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
-	if (rc)
-		return rc;
-
-	tpm_buf_append_u32(&buf, pcr_idx);
-	tpm_buf_append(&buf, hash, TPM_DIGEST_SIZE);
-
-	rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, log_msg);
-	tpm_buf_destroy(&buf);
-	return rc;
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
+	tpm_buf_append_u32(buf, pcr_idx);
+	tpm_buf_append(buf, hash, TPM_DIGEST_SIZE);
+	return tpm_transmit_cmd(chip, buf, TPM_DIGEST_SIZE, log_msg);
 }

 #define TPM_ORD_GET_CAP 101
 ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
 		    const char *desc, size_t min_cap_length)
 {
-	struct tpm_buf buf;
 	int rc;

-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_CAP);
-	if (rc)
-		return rc;
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_CAP);

 	if (subcap_id == TPM_CAP_VERSION_1_1 ||
 	    subcap_id == TPM_CAP_VERSION_1_2) {
-		tpm_buf_append_u32(&buf, subcap_id);
-		tpm_buf_append_u32(&buf, 0);
+		tpm_buf_append_u32(buf, subcap_id);
+		tpm_buf_append_u32(buf, 0);
 	} else {
 		if (subcap_id == TPM_CAP_FLAG_PERM ||
 		    subcap_id == TPM_CAP_FLAG_VOL)
-			tpm_buf_append_u32(&buf, TPM_CAP_FLAG);
+			tpm_buf_append_u32(buf, TPM_CAP_FLAG);
 		else
-			tpm_buf_append_u32(&buf, TPM_CAP_PROP);
+			tpm_buf_append_u32(buf, TPM_CAP_PROP);

-		tpm_buf_append_u32(&buf, 4);
-		tpm_buf_append_u32(&buf, subcap_id);
+		tpm_buf_append_u32(buf, 4);
+		tpm_buf_append_u32(buf, subcap_id);
 	}
-	rc = tpm_transmit_cmd(chip, &buf, min_cap_length, desc);
+	rc = tpm_transmit_cmd(chip, buf, min_cap_length, desc);
 	if (!rc)
-		*cap = *(cap_t *)&buf.data[TPM_HEADER_SIZE + 4];
-	tpm_buf_destroy(&buf);
+		*cap = *(cap_t *)&buf->data[TPM_HEADER_SIZE + 4];
 	return rc;
 }
 EXPORT_SYMBOL_GPL(tpm1_getcap);
@@ -530,21 +525,24 @@ struct tpm1_get_random_out {
 int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {
 	struct tpm1_get_random_out *out;
+	struct tpm_buf *buf __free(kfree) = NULL;
 	u32 num_bytes =  min_t(u32, max, TPM_MAX_RNG_DATA);
-	struct tpm_buf buf;
 	u32 total = 0;
 	int retries = 5;
 	u32 recd;
 	int rc;

-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
-	if (rc)
-		return rc;
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);

 	do {
-		tpm_buf_append_u32(&buf, num_bytes);
+		tpm_buf_append_u32(buf, num_bytes);

-		rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
+		rc = tpm_transmit_cmd(chip, buf, sizeof(out->rng_data_len),
 				      "attempting get random");
 		if (rc) {
 			if (rc > 0)
@@ -552,7 +550,7 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 			goto out;
 		}

-		out = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE];
+		out = (struct tpm1_get_random_out *)&buf->data[TPM_HEADER_SIZE];

 		recd = be32_to_cpu(out->rng_data_len);
 		if (recd > num_bytes) {
@@ -560,8 +558,8 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 			goto out;
 		}

-		if (tpm_buf_length(&buf) < TPM_HEADER_SIZE +
-					   sizeof(out->rng_data_len) + recd) {
+		if (tpm_buf_length(buf) < TPM_HEADER_SIZE +
+					  sizeof(out->rng_data_len) + recd) {
 			rc = -EFAULT;
 			goto out;
 		}
@@ -571,41 +569,36 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 		total += recd;
 		num_bytes -= recd;

-		tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
+		tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
 	} while (retries-- && total < max);

 	rc = total ? (int)total : -EIO;
 out:
-	tpm_buf_destroy(&buf);
 	return rc;
 }

 #define TPM_ORD_PCRREAD 21
 int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
 {
-	struct tpm_buf buf;
 	int rc;

-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCRREAD);
-	if (rc)
-		return rc;
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;

-	tpm_buf_append_u32(&buf, pcr_idx);
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCRREAD);
+	tpm_buf_append_u32(buf, pcr_idx);

-	rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE,
+	rc = tpm_transmit_cmd(chip, buf, TPM_DIGEST_SIZE,
 			      "attempting to read a pcr value");
 	if (rc)
-		goto out;
-
-	if (tpm_buf_length(&buf) < TPM_DIGEST_SIZE) {
-		rc = -EFAULT;
-		goto out;
-	}
+		return rc;

-	memcpy(res_buf, &buf.data[TPM_HEADER_SIZE], TPM_DIGEST_SIZE);
+	if (tpm_buf_length(buf) < TPM_DIGEST_SIZE)
+		return -EFAULT;

-out:
-	tpm_buf_destroy(&buf);
+	memcpy(res_buf, &buf->data[TPM_HEADER_SIZE], TPM_DIGEST_SIZE);
 	return rc;
 }

@@ -619,16 +612,13 @@ int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
  */
 static int tpm1_continue_selftest(struct tpm_chip *chip)
 {
-	struct tpm_buf buf;
-	int rc;
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;

-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
-	if (rc)
-		return rc;
-
-	rc = tpm_transmit_cmd(chip, &buf, 0, "continue selftest");
-	tpm_buf_destroy(&buf);
-	return rc;
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
+	return tpm_transmit_cmd(chip, buf, 0, "continue selftest");
 }

 /**
@@ -742,22 +732,24 @@ int tpm1_auto_startup(struct tpm_chip *chip)
 int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
 {
 	u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 };
-	struct tpm_buf buf;
 	unsigned int try;
+	struct tpm_buf *buf __free(kfree) = NULL;
 	int rc;

-
 	/* for buggy tpm, flush pcrs with extend to selected dummy */
 	if (tpm_suspend_pcr)
 		rc = tpm1_pcr_extend(chip, tpm_suspend_pcr, dummy_hash,
 				     "extending dummy pcr before suspend");
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);

-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
-	if (rc)
-		return rc;
 	/* now do the actual savestate */
 	for (try = 0; try < TPM_RETRY; try++) {
-		rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
+		rc = tpm_transmit_cmd(chip, buf, 0, NULL);
 		/*
 		 * If the TPM indicates that it is too busy to respond to
 		 * this command then retry before giving up.  It can take
@@ -772,7 +764,7 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
 			break;
 		tpm_msleep(TPM_TIMEOUT_RETRY);

-		tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
+		tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
 	}

 	if (rc)
@@ -782,8 +774,6 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
 		dev_warn(&chip->dev, "TPM savestate took %dms\n",
 			 try * TPM_TIMEOUT_RETRY);

-	tpm_buf_destroy(&buf);
-
 	return rc;
 }

diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 52ee350da867..f619ce390f6d 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -119,12 +119,13 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
 {
 	int i;
 	int rc;
-	struct tpm_buf buf;
 	struct tpm2_pcr_read_out *out;
 	u8 pcr_select[TPM2_PCR_SELECT_MIN] = {0};
 	u16 digest_size;
 	u16 expected_digest_size = 0;

+	struct tpm_buf *buf __free(kfree) = NULL;
+
 	if (pcr_idx >= TPM2_PLATFORM_PCR)
 		return -EINVAL;

@@ -139,36 +140,35 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
 		expected_digest_size = chip->allocated_banks[i].digest_size;
 	}

-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
-	if (rc)
-		return rc;
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);

 	pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);

-	tpm_buf_append_u32(&buf, 1);
-	tpm_buf_append_u16(&buf, digest->alg_id);
-	tpm_buf_append_u8(&buf, TPM2_PCR_SELECT_MIN);
-	tpm_buf_append(&buf, (const unsigned char *)pcr_select,
+	tpm_buf_append_u32(buf, 1);
+	tpm_buf_append_u16(buf, digest->alg_id);
+	tpm_buf_append_u8(buf, TPM2_PCR_SELECT_MIN);
+	tpm_buf_append(buf, (const unsigned char *)pcr_select,
 		       sizeof(pcr_select));

-	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to read a pcr value");
+	rc = tpm_transmit_cmd(chip, buf, 0, "attempting to read a pcr value");
 	if (rc)
-		goto out;
+		return rc;

-	out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE];
+	out = (struct tpm2_pcr_read_out *)&buf->data[TPM_HEADER_SIZE];
 	digest_size = be16_to_cpu(out->digest_size);
 	if (digest_size > sizeof(digest->digest) ||
-	    (!digest_size_ptr && digest_size != expected_digest_size)) {
-		rc = -EINVAL;
-		goto out;
-	}
+	    (!digest_size_ptr && digest_size != expected_digest_size))
+		return -EINVAL;

 	if (digest_size_ptr)
 		*digest_size_ptr = digest_size;

 	memcpy(digest->digest, out->digest, digest_size);
-out:
-	tpm_buf_destroy(&buf);
 	return rc;
 }

@@ -184,56 +184,54 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
 int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
 		    struct tpm_digest *digests)
 {
-	struct tpm_buf buf;
 	int rc;
 	int i;

+	struct tpm_buf *buf __free(kfree) = NULL;
+
 	if (!disable_pcr_integrity) {
 		rc = tpm2_start_auth_session(chip);
 		if (rc)
 			return rc;
 	}

-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
-	if (rc) {
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf) {
 		if (!disable_pcr_integrity)
 			tpm2_end_auth_session(chip);
-		return rc;
+		return -ENOMEM;
 	}

+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
+
 	if (!disable_pcr_integrity) {
-		rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
-		if (rc) {
-			tpm_buf_destroy(&buf);
+		rc = tpm_buf_append_name(chip, buf, pcr_idx, NULL);
+		if (rc)
 			return rc;
-		}
-		tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
+		tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
 	} else {
-		tpm_buf_append_handle(chip, &buf, pcr_idx);
-		tpm_buf_append_auth(chip, &buf, NULL, 0);
+		tpm_buf_append_handle(chip, buf, pcr_idx);
+		tpm_buf_append_auth(chip, buf, NULL, 0);
 	}

-	tpm_buf_append_u32(&buf, chip->nr_allocated_banks);
+	tpm_buf_append_u32(buf, chip->nr_allocated_banks);

 	for (i = 0; i < chip->nr_allocated_banks; i++) {
-		tpm_buf_append_u16(&buf, digests[i].alg_id);
-		tpm_buf_append(&buf, (const unsigned char *)&digests[i].digest,
+		tpm_buf_append_u16(buf, digests[i].alg_id);
+		tpm_buf_append(buf, (const unsigned char *)&digests[i].digest,
 			       chip->allocated_banks[i].digest_size);
 	}

 	if (!disable_pcr_integrity) {
-		rc = tpm_buf_fill_hmac_session(chip, &buf);
-		if (rc) {
-			tpm_buf_destroy(&buf);
+		rc = tpm_buf_fill_hmac_session(chip, buf);
+		if (rc)
 			return rc;
-		}
 	}

-	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
+	rc = tpm_transmit_cmd(chip, buf, 0, "attempting extend a PCR value");
 	if (!disable_pcr_integrity)
-		rc = tpm_buf_check_hmac_response(chip, &buf, rc);
-
-	tpm_buf_destroy(&buf);
+		rc = tpm_buf_check_hmac_response(chip, buf, rc);

 	return rc;
 }
@@ -258,7 +256,6 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {
 	struct tpm2_get_random_out *out;
 	struct tpm_header *head;
-	struct tpm_buf buf;
 	u32 recd;
 	u32 num_bytes = max;
 	int err;
@@ -267,6 +264,8 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 	u8 *dest_ptr = dest;
 	off_t offset;

+	struct tpm_buf *buf __free(kfree) = NULL;
+
 	if (!num_bytes || max > TPM_MAX_RNG_DATA)
 		return -EINVAL;

@@ -274,50 +273,52 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 	if (err)
 		return err;

-	err = tpm_buf_init(&buf, 0, 0);
-	if (err) {
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf) {
 		tpm2_end_auth_session(chip);
-		return err;
+		return -ENOMEM;
 	}

+	tpm_buf_init(buf, TPM_BUFSIZE);
+
 	do {
-		tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
+		tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
 		if (tpm2_chip_auth(chip)) {
-			tpm_buf_append_hmac_session(chip, &buf,
+			tpm_buf_append_hmac_session(chip, buf,
 						    TPM2_SA_ENCRYPT |
 						    TPM2_SA_CONTINUE_SESSION,
 						    NULL, 0);
 		} else  {
-			offset = buf.handles * 4 + TPM_HEADER_SIZE;
-			head = (struct tpm_header *)buf.data;
-			if (tpm_buf_length(&buf) == offset)
+			offset = buf->handles * 4 + TPM_HEADER_SIZE;
+			head = (struct tpm_header *)buf->data;
+			if (tpm_buf_length(buf) == offset)
 				head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
 		}
-		tpm_buf_append_u16(&buf, num_bytes);
-		err = tpm_buf_fill_hmac_session(chip, &buf);
+		tpm_buf_append_u16(buf, num_bytes);
+		err = tpm_buf_fill_hmac_session(chip, buf);
 		if (err)
 			goto out;

-		err = tpm_transmit_cmd(chip, &buf,
+		err = tpm_transmit_cmd(chip, buf,
 				       offsetof(struct tpm2_get_random_out,
 						buffer),
 				       "attempting get random");
-		err = tpm_buf_check_hmac_response(chip, &buf, err);
+		err = tpm_buf_check_hmac_response(chip, buf, err);
 		if (err) {
 			if (err > 0)
 				err = -EIO;
 			goto out;
 		}

-		head = (struct tpm_header *)buf.data;
+		head = (struct tpm_header *)buf->data;
 		offset = TPM_HEADER_SIZE;
 		/* Skip the parameter size field: */
 		if (be16_to_cpu(head->tag) == TPM2_ST_SESSIONS)
 			offset += 4;

-		out = (struct tpm2_get_random_out *)&buf.data[offset];
+		out = (struct tpm2_get_random_out *)&buf->data[offset];
 		recd = min_t(u32, be16_to_cpu(out->size), num_bytes);
-		if (tpm_buf_length(&buf) <
+		if (tpm_buf_length(buf) <
 		    TPM_HEADER_SIZE +
 		    offsetof(struct tpm2_get_random_out, buffer) +
 		    recd) {
@@ -331,11 +332,8 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 		num_bytes -= recd;
 	} while (retries-- && total < max);

-	tpm_buf_destroy(&buf);
-
 	return total ? total : -EIO;
 out:
-	tpm_buf_destroy(&buf);
 	tpm2_end_auth_session(chip);
 	return err;
 }
@@ -347,20 +345,18 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
  */
 void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
 {
-	struct tpm_buf buf;
-	int rc;
-
-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
-	if (rc) {
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf) {
 		dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
 			 handle);
 		return;
 	}

-	tpm_buf_append_u32(&buf, handle);
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
+	tpm_buf_append_u32(buf, handle);

-	tpm_transmit_cmd(chip, &buf, 0, "flushing context");
-	tpm_buf_destroy(&buf);
+	tpm_transmit_cmd(chip, buf, 0, "flushing context");
 }
 EXPORT_SYMBOL_GPL(tpm2_flush_context);

@@ -387,19 +383,21 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,  u32 *value,
 			const char *desc)
 {
 	struct tpm2_get_cap_out *out;
-	struct tpm_buf buf;
 	int rc;

-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
-	if (rc)
-		return rc;
-	tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
-	tpm_buf_append_u32(&buf, property_id);
-	tpm_buf_append_u32(&buf, 1);
-	rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
+	tpm_buf_append_u32(buf, TPM2_CAP_TPM_PROPERTIES);
+	tpm_buf_append_u32(buf, property_id);
+	tpm_buf_append_u32(buf, 1);
+	rc = tpm_transmit_cmd(chip, buf, 0, NULL);
 	if (!rc) {
 		out = (struct tpm2_get_cap_out *)
-			&buf.data[TPM_HEADER_SIZE];
+			&buf->data[TPM_HEADER_SIZE];
 		/*
 		 * To prevent failing boot up of some systems, Infineon TPM2.0
 		 * returns SUCCESS on TPM2_Startup in field upgrade mode. Also
@@ -411,7 +409,6 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,  u32 *value,
 		else
 			rc = -ENODATA;
 	}
-	tpm_buf_destroy(&buf);
 	return rc;
 }
 EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
@@ -428,15 +425,14 @@ EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
  */
 void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
 {
-	struct tpm_buf buf;
-	int rc;
-
-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN);
-	if (rc)
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
 		return;
-	tpm_buf_append_u16(&buf, shutdown_type);
-	tpm_transmit_cmd(chip, &buf, 0, "stopping the TPM");
-	tpm_buf_destroy(&buf);
+
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN);
+	tpm_buf_append_u16(buf, shutdown_type);
+	tpm_transmit_cmd(chip, buf, 0, "stopping the TPM");
 }

 /**
@@ -454,20 +450,21 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
  */
 static int tpm2_do_selftest(struct tpm_chip *chip)
 {
-	struct tpm_buf buf;
 	int full;
 	int rc;

 	for (full = 0; full < 2; full++) {
-		rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
-		if (rc)
-			return rc;
+		struct tpm_buf *buf __free(kfree) = NULL;

-		tpm_buf_append_u8(&buf, full);
-		rc = tpm_transmit_cmd(chip, &buf, 0,
-				      "attempting the self test");
-		tpm_buf_destroy(&buf);
+		buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;

+		tpm_buf_init(buf, TPM_BUFSIZE);
+		tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
+		tpm_buf_append_u8(buf, full);
+		rc = tpm_transmit_cmd(chip, buf, 0,
+				      "attempting the self test");
 		if (rc == TPM2_RC_TESTING)
 			rc = TPM2_RC_SUCCESS;
 		if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS)
@@ -492,23 +489,24 @@ static int tpm2_do_selftest(struct tpm_chip *chip)
 int tpm2_probe(struct tpm_chip *chip)
 {
 	struct tpm_header *out;
-	struct tpm_buf buf;
 	int rc;

-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
-	if (rc)
-		return rc;
-	tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
-	tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS);
-	tpm_buf_append_u32(&buf, 1);
-	rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
+	tpm_buf_append_u32(buf, TPM2_CAP_TPM_PROPERTIES);
+	tpm_buf_append_u32(buf, TPM_PT_TOTAL_COMMANDS);
+	tpm_buf_append_u32(buf, 1);
+	rc = tpm_transmit_cmd(chip, buf, 0, NULL);
 	/* We ignore TPM return codes on purpose. */
 	if (rc >=  0) {
-		out = (struct tpm_header *)buf.data;
+		out = (struct tpm_header *)buf->data;
 		if (be16_to_cpu(out->tag) == TPM2_ST_NO_SESSIONS)
 			chip->flags |= TPM_CHIP_FLAG_TPM2;
 	}
-	tpm_buf_destroy(&buf);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(tpm2_probe);
@@ -548,7 +546,6 @@ struct tpm2_pcr_selection {
 ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
 {
 	struct tpm2_pcr_selection pcr_selection;
-	struct tpm_buf buf;
 	void *marker;
 	void *end;
 	void *pcr_select_offset;
@@ -560,20 +557,22 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
 	int rc;
 	int i = 0;

-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
-	if (rc)
-		return rc;
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;

-	tpm_buf_append_u32(&buf, TPM2_CAP_PCRS);
-	tpm_buf_append_u32(&buf, 0);
-	tpm_buf_append_u32(&buf, 1);
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
+	tpm_buf_append_u32(buf, TPM2_CAP_PCRS);
+	tpm_buf_append_u32(buf, 0);
+	tpm_buf_append_u32(buf, 1);

-	rc = tpm_transmit_cmd(chip, &buf, 9, "get tpm pcr allocation");
+	rc = tpm_transmit_cmd(chip, buf, 9, "get tpm pcr allocation");
 	if (rc)
 		goto out;

 	nr_possible_banks = be32_to_cpup(
-		(__be32 *)&buf.data[TPM_HEADER_SIZE + 5]);
+		(__be32 *)&buf->data[TPM_HEADER_SIZE + 5]);
 	if (nr_possible_banks > TPM2_MAX_PCR_BANKS) {
 		pr_err("tpm: out of bank capacity: %u > %u\n",
 		       nr_possible_banks, TPM2_MAX_PCR_BANKS);
@@ -581,10 +580,10 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
 		goto out;
 	}

-	marker = &buf.data[TPM_HEADER_SIZE + 9];
+	marker = &buf->data[TPM_HEADER_SIZE + 9];

-	rsp_len = be32_to_cpup((__be32 *)&buf.data[2]);
-	end = &buf.data[rsp_len];
+	rsp_len = be32_to_cpup((__be32 *)&buf->data[2]);
+	end = &buf->data[rsp_len];

 	for (i = 0; i < nr_possible_banks; i++) {
 		pcr_select_offset = marker +
@@ -617,20 +616,19 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)

 	chip->nr_allocated_banks = nr_alloc_banks;
 out:
-	tpm_buf_destroy(&buf);
-
 	return rc;
 }

 int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
 {
-	struct tpm_buf buf;
 	u32 nr_commands;
 	__be32 *attrs;
 	u32 cc;
 	int i;
 	int rc;

+	struct tpm_buf *buf __free(kfree) = NULL;
+
 	rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL);
 	if (rc)
 		goto out;
@@ -647,30 +645,31 @@ int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
 		goto out;
 	}

-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
-	if (rc)
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf) {
+		rc = -ENOMEM;
 		goto out;
+	}

-	tpm_buf_append_u32(&buf, TPM2_CAP_COMMANDS);
-	tpm_buf_append_u32(&buf, TPM2_CC_FIRST);
-	tpm_buf_append_u32(&buf, nr_commands);
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
+	tpm_buf_append_u32(buf, TPM2_CAP_COMMANDS);
+	tpm_buf_append_u32(buf, TPM2_CC_FIRST);
+	tpm_buf_append_u32(buf, nr_commands);

-	rc = tpm_transmit_cmd(chip, &buf, 9 + 4 * nr_commands, NULL);
-	if (rc) {
-		tpm_buf_destroy(&buf);
+	rc = tpm_transmit_cmd(chip, buf, 9 + 4 * nr_commands, NULL);
+	if (rc)
 		goto out;
-	}

 	if (nr_commands !=
-	    be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5])) {
+	    be32_to_cpup((__be32 *)&buf->data[TPM_HEADER_SIZE + 5])) {
 		rc = -EFAULT;
-		tpm_buf_destroy(&buf);
 		goto out;
 	}

 	chip->nr_commands = nr_commands;

-	attrs = (__be32 *)&buf.data[TPM_HEADER_SIZE + 9];
+	attrs = (__be32 *)&buf->data[TPM_HEADER_SIZE + 9];
 	for (i = 0; i < nr_commands; i++, attrs++) {
 		chip->cc_attrs_tbl[i] = be32_to_cpup(attrs);
 		cc = chip->cc_attrs_tbl[i] & 0xFFFF;
@@ -682,8 +681,6 @@ int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
 		}
 	}

-	tpm_buf_destroy(&buf);
-
 out:
 	if (rc > 0)
 		rc = -ENODEV;
@@ -704,20 +701,18 @@ EXPORT_SYMBOL_GPL(tpm2_get_cc_attrs_tbl);

 static int tpm2_startup(struct tpm_chip *chip)
 {
-	struct tpm_buf buf;
-	int rc;
+	struct tpm_buf *buf __free(kfree) = NULL;

 	dev_info(&chip->dev, "starting up the TPM manually\n");

-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
-	if (rc < 0)
-		return rc;
-
-	tpm_buf_append_u16(&buf, TPM2_SU_CLEAR);
-	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
-	tpm_buf_destroy(&buf);
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;

-	return rc;
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
+	tpm_buf_append_u16(buf, TPM2_SU_CLEAR);
+	return tpm_transmit_cmd(chip, buf, 0, "attempting to start the TPM");
 }

 /**
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 795cd99dc6fe..b6a93db5a5ee 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -167,8 +167,8 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 {
 	u32 mso = tpm2_handle_mso(handle);
 	off_t offset = TPM_HEADER_SIZE;
+	struct tpm_buf *buf __free(kfree) = NULL;
 	int rc, name_size_alg;
-	struct tpm_buf buf;

 	if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
 	    mso != TPM2_MSO_NVRAM) {
@@ -176,50 +176,40 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 		return sizeof(u32);
 	}

-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
-	if (rc)
-		return rc;
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;

-	tpm_buf_append_u32(&buf, handle);
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
+	tpm_buf_append_u32(buf, handle);

-	rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
-	if (rc) {
-		tpm_buf_destroy(&buf);
+	rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_ReadPublic");
+	if (rc)
 		return tpm_ret_to_err(rc);
-	}

 	/* Skip TPMT_PUBLIC: */
-	offset += tpm_buf_read_u16(&buf, &offset);
+	offset += tpm_buf_read_u16(buf, &offset);

 	/*
 	 * Ensure space for the length field of TPM2B_NAME and hashAlg field of
 	 * TPMT_HA (the extra four bytes).
 	 */
-	if (offset + 4 > tpm_buf_length(&buf)) {
-		tpm_buf_destroy(&buf);
+	if (offset + 4 > tpm_buf_length(buf))
 		return -EIO;
-	}
-
-	rc = tpm_buf_read_u16(&buf, &offset);
-	name_size_alg = name_size(&buf.data[offset]);

-	if (name_size_alg < 0) {
-		tpm_buf_destroy(&buf);
+	rc = tpm_buf_read_u16(buf, &offset);
+	name_size_alg = name_size(&buf->data[offset]);
+	if (name_size_alg < 0)
 		return name_size_alg;
-	}

-	if (rc != name_size_alg) {
-		tpm_buf_destroy(&buf);
+	if (rc != name_size_alg)
 		return -EIO;
-	}

-	if (offset + rc > tpm_buf_length(&buf)) {
-		tpm_buf_destroy(&buf);
+	if (offset + rc > tpm_buf_length(buf))
 		return -EIO;
-	}

-	memcpy(name, &buf.data[offset], rc);
-	tpm_buf_destroy(&buf);
+	memcpy(name, &buf->data[offset], rc);
 	return name_size_alg;
 }
 #endif /* CONFIG_TCG_TPM2_HMAC */
@@ -987,8 +977,8 @@ static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key)
  */
 int tpm2_start_auth_session(struct tpm_chip *chip)
 {
+	struct tpm_buf *buf __free(kfree) = NULL;
 	struct tpm2_auth *auth;
-	struct tpm_buf buf;
 	u32 null_key;
 	int rc;

@@ -1007,41 +997,43 @@ int tpm2_start_auth_session(struct tpm_chip *chip)

 	auth->session = TPM_HEADER_SIZE;

-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
-	if (rc)
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf) {
+		rc = -ENOMEM;
 		goto out;
+	}

+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
 	/* salt key handle */
-	tpm_buf_append_u32(&buf, null_key);
+	tpm_buf_append_u32(buf, null_key);
 	/* bind key handle */
-	tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+	tpm_buf_append_u32(buf, TPM2_RH_NULL);
 	/* nonce caller */
 	get_random_bytes(auth->our_nonce, sizeof(auth->our_nonce));
-	tpm_buf_append_u16(&buf, sizeof(auth->our_nonce));
-	tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce));
+	tpm_buf_append_u16(buf, sizeof(auth->our_nonce));
+	tpm_buf_append(buf, auth->our_nonce, sizeof(auth->our_nonce));

 	/* append encrypted salt and squirrel away unencrypted in auth */
-	tpm_buf_append_salt(&buf, chip, auth);
+	tpm_buf_append_salt(buf, chip, auth);
 	/* session type (HMAC, audit or policy) */
-	tpm_buf_append_u8(&buf, TPM2_SE_HMAC);
+	tpm_buf_append_u8(buf, TPM2_SE_HMAC);

 	/* symmetric encryption parameters */
 	/* symmetric algorithm */
-	tpm_buf_append_u16(&buf, TPM_ALG_AES);
+	tpm_buf_append_u16(buf, TPM_ALG_AES);
 	/* bits for symmetric algorithm */
-	tpm_buf_append_u16(&buf, AES_KEY_BITS);
+	tpm_buf_append_u16(buf, AES_KEY_BITS);
 	/* symmetric algorithm mode (must be CFB) */
-	tpm_buf_append_u16(&buf, TPM_ALG_CFB);
+	tpm_buf_append_u16(buf, TPM_ALG_CFB);
 	/* hash algorithm for session */
-	tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
+	tpm_buf_append_u16(buf, TPM_ALG_SHA256);

-	rc = tpm_ret_to_err(tpm_transmit_cmd(chip, &buf, 0, "StartAuthSession"));
+	rc = tpm_ret_to_err(tpm_transmit_cmd(chip, buf, 0, "StartAuthSession"));
 	tpm2_flush_context(chip, null_key);

 	if (rc == TPM2_RC_SUCCESS)
-		rc = tpm2_parse_start_auth_session(auth, &buf);
-
-	tpm_buf_destroy(&buf);
+		rc = tpm2_parse_start_auth_session(auth, buf);

 	if (rc == TPM2_RC_SUCCESS) {
 		chip->auth = auth;
@@ -1262,19 +1254,21 @@ static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf,
 static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
 			       u32 *handle, u8 *name)
 {
+	struct tpm_buf *template __free(kfree) = NULL;
+	struct tpm_buf *buf __free(kfree) = NULL;
 	int rc;
-	struct tpm_buf buf;
-	struct tpm_buf template;

-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
-	if (rc)
-		return rc;
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;

-	rc = tpm_buf_init_sized(&template);
-	if (rc) {
-		tpm_buf_destroy(&buf);
-		return rc;
-	}
+	template = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!template)
+		return -ENOMEM;
+
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
+	tpm_buf_init_sized(template, TPM_BUFSIZE);

 	/*
 	 * create the template.  Note: in order for userspace to
@@ -1286,75 +1280,72 @@ static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
 	 */

 	/* key type */
-	tpm_buf_append_u16(&template, TPM_ALG_ECC);
+	tpm_buf_append_u16(template, TPM_ALG_ECC);

 	/* name algorithm */
-	tpm_buf_append_u16(&template, TPM_ALG_SHA256);
+	tpm_buf_append_u16(template, TPM_ALG_SHA256);

 	/* object properties */
-	tpm_buf_append_u32(&template, TPM2_OA_NULL_KEY);
+	tpm_buf_append_u32(template, TPM2_OA_NULL_KEY);

 	/* sauth policy (empty) */
-	tpm_buf_append_u16(&template, 0);
+	tpm_buf_append_u16(template, 0);

 	/* BEGIN parameters: key specific; for ECC*/

 	/* symmetric algorithm */
-	tpm_buf_append_u16(&template, TPM_ALG_AES);
+	tpm_buf_append_u16(template, TPM_ALG_AES);

 	/* bits for symmetric algorithm */
-	tpm_buf_append_u16(&template, AES_KEY_BITS);
+	tpm_buf_append_u16(template, AES_KEY_BITS);

 	/* algorithm mode (must be CFB) */
-	tpm_buf_append_u16(&template, TPM_ALG_CFB);
+	tpm_buf_append_u16(template, TPM_ALG_CFB);

 	/* scheme (NULL means any scheme) */
-	tpm_buf_append_u16(&template, TPM_ALG_NULL);
+	tpm_buf_append_u16(template, TPM_ALG_NULL);

 	/* ECC Curve ID */
-	tpm_buf_append_u16(&template, TPM2_ECC_NIST_P256);
+	tpm_buf_append_u16(template, TPM2_ECC_NIST_P256);

 	/* KDF Scheme */
-	tpm_buf_append_u16(&template, TPM_ALG_NULL);
+	tpm_buf_append_u16(template, TPM_ALG_NULL);

 	/* unique: key specific; for ECC it is two zero size points */
-	tpm_buf_append_u16(&template, 0);
-	tpm_buf_append_u16(&template, 0);
+	tpm_buf_append_u16(template, 0);
+	tpm_buf_append_u16(template, 0);

 	/* END parameters */

 	/* primary handle */
-	tpm_buf_append_u32(&buf, hierarchy);
-	tpm_buf_append_empty_auth(&buf, TPM2_RS_PW);
+	tpm_buf_append_u32(buf, hierarchy);
+	tpm_buf_append_empty_auth(buf, TPM2_RS_PW);

 	/* sensitive create size is 4 for two empty buffers */
-	tpm_buf_append_u16(&buf, 4);
+	tpm_buf_append_u16(buf, 4);

 	/* sensitive create auth data (empty) */
-	tpm_buf_append_u16(&buf, 0);
+	tpm_buf_append_u16(buf, 0);

 	/* sensitive create sensitive data (empty) */
-	tpm_buf_append_u16(&buf, 0);
+	tpm_buf_append_u16(buf, 0);

 	/* the public template */
-	tpm_buf_append(&buf, template.data, template.length);
-	tpm_buf_destroy(&template);
+	tpm_buf_append(buf, template->data, template->length);

 	/* outside info (empty) */
-	tpm_buf_append_u16(&buf, 0);
+	tpm_buf_append_u16(buf, 0);

 	/* creation PCR (none) */
-	tpm_buf_append_u32(&buf, 0);
+	tpm_buf_append_u32(buf, 0);

-	rc = tpm_transmit_cmd(chip, &buf, 0,
+	rc = tpm_transmit_cmd(chip, buf, 0,
 			      "attempting to create NULL primary");

 	if (rc == TPM2_RC_SUCCESS)
-		rc = tpm2_parse_create_primary(chip, &buf, handle, hierarchy,
+		rc = tpm2_parse_create_primary(chip, buf, handle, hierarchy,
 					       name);

-	tpm_buf_destroy(&buf);
-
 	return rc;
 }

diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c
index 60354cd53b5c..cbf86ff5931f 100644
--- a/drivers/char/tpm/tpm2-space.c
+++ b/drivers/char/tpm/tpm2-space.c
@@ -71,24 +71,25 @@ void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
 int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
 		      unsigned int *offset, u32 *handle)
 {
-	struct tpm_buf tbuf;
 	struct tpm2_context *ctx;
 	unsigned int body_size;
 	int rc;

-	rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
-	if (rc)
-		return rc;
+	struct tpm_buf *tbuf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!tbuf)
+		return -ENOMEM;
+
+	tpm_buf_init(tbuf, TPM_BUFSIZE);
+	tpm_buf_reset(tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);

 	ctx = (struct tpm2_context *)&buf[*offset];
 	body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
-	tpm_buf_append(&tbuf, &buf[*offset], body_size);
+	tpm_buf_append(tbuf, &buf[*offset], body_size);

-	rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL);
+	rc = tpm_transmit_cmd(chip, tbuf, 4, NULL);
 	if (rc < 0) {
 		dev_warn(&chip->dev, "%s: failed with a system error %d\n",
 			 __func__, rc);
-		tpm_buf_destroy(&tbuf);
 		return -EFAULT;
 	} else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||
 		   rc == TPM2_RC_REFERENCE_H0) {
@@ -103,64 +104,55 @@ int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
 		 * flushed outside the space
 		 */
 		*handle = 0;
-		tpm_buf_destroy(&tbuf);
 		return -ENOENT;
 	} else if (tpm2_rc_value(rc) == TPM2_RC_INTEGRITY) {
-		tpm_buf_destroy(&tbuf);
 		return -EINVAL;
 	} else if (rc > 0) {
 		dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
 			 __func__, rc);
-		tpm_buf_destroy(&tbuf);
 		return -EFAULT;
 	}

-	*handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
+	*handle = be32_to_cpup((__be32 *)&tbuf->data[TPM_HEADER_SIZE]);
 	*offset += body_size;
-
-	tpm_buf_destroy(&tbuf);
 	return 0;
 }

 int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
 		      unsigned int buf_size, unsigned int *offset)
 {
-	struct tpm_buf tbuf;
 	unsigned int body_size;
 	int rc;

-	rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
-	if (rc)
-		return rc;
+	struct tpm_buf *tbuf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!tbuf)
+		return -ENOMEM;

-	tpm_buf_append_u32(&tbuf, handle);
+	tpm_buf_init(tbuf, TPM_BUFSIZE);
+	tpm_buf_reset(tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
+	tpm_buf_append_u32(tbuf, handle);

-	rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL);
+	rc = tpm_transmit_cmd(chip, tbuf, 0, NULL);
 	if (rc < 0) {
 		dev_warn(&chip->dev, "%s: failed with a system error %d\n",
 			 __func__, rc);
-		tpm_buf_destroy(&tbuf);
 		return -EFAULT;
 	} else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
-		tpm_buf_destroy(&tbuf);
 		return -ENOENT;
 	} else if (rc) {
 		dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
 			 __func__, rc);
-		tpm_buf_destroy(&tbuf);
 		return -EFAULT;
 	}

-	body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
+	body_size = tpm_buf_length(tbuf) - TPM_HEADER_SIZE;
 	if ((*offset + body_size) > buf_size) {
 		dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
-		tpm_buf_destroy(&tbuf);
 		return -ENOMEM;
 	}

-	memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
+	memcpy(&buf[*offset], &tbuf->data[TPM_HEADER_SIZE], body_size);
 	*offset += body_size;
-	tpm_buf_destroy(&tbuf);
 	return 0;
 }

diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
index 7bb0f4d4a2ed..b81fd2a537df 100644
--- a/drivers/char/tpm/tpm_vtpm_proxy.c
+++ b/drivers/char/tpm/tpm_vtpm_proxy.c
@@ -395,40 +395,36 @@ static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip  *chip, u8 status)

 static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
 {
-	struct tpm_buf buf;
 	int rc;
 	const struct tpm_header *header;
 	struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);

+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	tpm_buf_init(buf, TPM_BUFSIZE);
 	if (chip->flags & TPM_CHIP_FLAG_TPM2)
-		rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS,
-				  TPM2_CC_SET_LOCALITY);
+		tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_SET_LOCALITY);
 	else
-		rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND,
-				  TPM_ORD_SET_LOCALITY);
-	if (rc)
-		return rc;
-	tpm_buf_append_u8(&buf, locality);
+		tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SET_LOCALITY);
+
+	tpm_buf_append_u8(buf, locality);

 	proxy_dev->state |= STATE_DRIVER_COMMAND;

-	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to set locality");
+	rc = tpm_transmit_cmd(chip, buf, 0, "attempting to set locality");

 	proxy_dev->state &= ~STATE_DRIVER_COMMAND;

-	if (rc < 0) {
-		locality = rc;
-		goto out;
-	}
+	if (rc < 0)
+		return rc;

-	header = (const struct tpm_header *)buf.data;
+	header = (const struct tpm_header *)buf->data;
 	rc = be32_to_cpu(header->return_code);
 	if (rc)
 		locality = -1;

-out:
-	tpm_buf_destroy(&buf);
-
 	return locality;
 }

diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 202da079d500..14d75c1482d6 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -26,6 +26,7 @@
 #include <crypto/aes.h>

 #define TPM_DIGEST_SIZE 20	/* Max TPM v1.2 PCR size */
+#define TPM_BUFSIZE		4096

 #define TPM2_MAX_DIGEST_SIZE	SHA512_DIGEST_SIZE
 #define TPM2_MAX_PCR_BANKS	8
@@ -378,13 +379,15 @@ enum tpm_buf_flags {
 };

 /*
- * A string buffer type for constructing TPM commands.
+ * A buffer for constructing and parsing TPM commands, responses and sized
+ * (TPM2B) buffers.
  */
 struct tpm_buf {
-	u32 flags;
-	u32 length;
-	u8 *data;
+	u8 flags;
+	u16 length;
+	u16 capacity;
 	u8 handles;
+	u8 data[];
 };

 enum tpm2_object_attributes {
@@ -415,12 +418,11 @@ struct tpm2_hash {
 	unsigned int tpm_id;
 };

-int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal);
+void tpm_buf_init(struct tpm_buf *buf, u16 buf_size);
+void tpm_buf_init_sized(struct tpm_buf *buf, u16 buf_size);
 void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal);
-int tpm_buf_init_sized(struct tpm_buf *buf);
 void tpm_buf_reset_sized(struct tpm_buf *buf);
-void tpm_buf_destroy(struct tpm_buf *buf);
-u32 tpm_buf_length(struct tpm_buf *buf);
+u16 tpm_buf_length(struct tpm_buf *buf);
 void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length);
 void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value);
 void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value);
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 13513819991e..6e03fa7227e4 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -317,9 +317,8 @@ static int TSS_checkhmac2(unsigned char *buffer,
  * For key specific tpm requests, we will generate and send our
  * own TPM command packets using the drivers send function.
  */
-static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
+static int trusted_tpm_send(struct tpm_buf *buf)
 {
-	struct tpm_buf buf;
 	int rc;

 	if (!chip)
@@ -329,12 +328,9 @@ static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
 	if (rc)
 		return rc;

-	buf.flags = 0;
-	buf.length = buflen;
-	buf.data = cmd;
-	dump_tpm_buf(cmd);
-	rc = tpm_transmit_cmd(chip, &buf, 4, "sending data");
-	dump_tpm_buf(cmd);
+	dump_tpm_buf(buf->data);
+	rc = tpm_transmit_cmd(chip, buf, 4, "sending data");
+	dump_tpm_buf(buf->data);

 	if (rc > 0)
 		/* TPM error */
@@ -380,7 +376,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
 	tpm_buf_append_u32(tb, handle);
 	tpm_buf_append(tb, ononce, TPM_NONCE_SIZE);

-	ret = trusted_tpm_send(tb->data, tb->length);
+	ret = trusted_tpm_send(tb);
 	if (ret < 0)
 		return ret;

@@ -404,7 +400,7 @@ static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
 		return -ENODEV;

 	tpm_buf_reset(tb, TPM_TAG_RQU_COMMAND, TPM_ORD_OIAP);
-	ret = trusted_tpm_send(tb->data, tb->length);
+	ret = trusted_tpm_send(tb);
 	if (ret < 0)
 		return ret;

@@ -513,7 +509,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
 	tpm_buf_append_u8(tb, cont);
 	tpm_buf_append(tb, td->pubauth, SHA1_DIGEST_SIZE);

-	ret = trusted_tpm_send(tb->data, tb->length);
+	ret = trusted_tpm_send(tb);
 	if (ret < 0)
 		goto out;

@@ -604,7 +600,7 @@ static int tpm_unseal(struct tpm_buf *tb,
 	tpm_buf_append_u8(tb, cont);
 	tpm_buf_append(tb, authdata2, SHA1_DIGEST_SIZE);

-	ret = trusted_tpm_send(tb->data, tb->length);
+	ret = trusted_tpm_send(tb);
 	if (ret < 0) {
 		pr_info("authhmac failed (%d)\n", ret);
 		return ret;
@@ -631,23 +627,23 @@ static int tpm_unseal(struct tpm_buf *tb,
 static int key_seal(struct trusted_key_payload *p,
 		    struct trusted_key_options *o)
 {
-	struct tpm_buf tb;
 	int ret;

-	ret = tpm_buf_init(&tb, 0, 0);
-	if (ret)
-		return ret;
+	struct tpm_buf *tb __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!tb)
+		return -ENOMEM;
+
+	tpm_buf_init(tb, TPM_BUFSIZE);

 	/* include migratable flag at end of sealed key */
 	p->key[p->key_len] = p->migratable;

-	ret = tpm_seal(&tb, o->keytype, o->keyhandle, o->keyauth,
+	ret = tpm_seal(tb, o->keytype, o->keyhandle, o->keyauth,
 		       p->key, p->key_len + 1, p->blob, &p->blob_len,
 		       o->blobauth, o->pcrinfo, o->pcrinfo_len);
 	if (ret < 0)
 		pr_info("srkseal failed (%d)\n", ret);

-	tpm_buf_destroy(&tb);
 	return ret;
 }

@@ -657,14 +653,15 @@ static int key_seal(struct trusted_key_payload *p,
 static int key_unseal(struct trusted_key_payload *p,
 		      struct trusted_key_options *o)
 {
-	struct tpm_buf tb;
 	int ret;

-	ret = tpm_buf_init(&tb, 0, 0);
-	if (ret)
-		return ret;
+	struct tpm_buf *tb __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!tb)
+		return -ENOMEM;
+
+	tpm_buf_init(tb, TPM_BUFSIZE);

-	ret = tpm_unseal(&tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
+	ret = tpm_unseal(tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
 			 o->blobauth, p->key, &p->key_len);
 	if (ret < 0)
 		pr_info("srkunseal failed (%d)\n", ret);
@@ -672,7 +669,6 @@ static int key_unseal(struct trusted_key_payload *p,
 		/* pull migratable flag out of sealed key */
 		p->migratable = p->key[--p->key_len];

-	tpm_buf_destroy(&tb);
 	return ret;
 }

diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 6340823f8b53..6f5c34b885fb 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -234,7 +234,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_options *options)
 {
 	off_t offset = TPM_HEADER_SIZE;
-	struct tpm_buf buf, sized;
+	struct tpm_buf *buf __free(kfree) = NULL;
+	struct tpm_buf *sized __free(kfree) = NULL;
 	int blob_len = 0;
 	int hash;
 	u32 flags;
@@ -255,97 +256,100 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	if (rc)
 		goto out_put;

-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
-	if (rc) {
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf) {
+		rc = -ENOMEM;
 		tpm2_end_auth_session(chip);
 		goto out_put;
 	}

-	rc = tpm_buf_init_sized(&sized);
-	if (rc) {
-		tpm_buf_destroy(&buf);
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
+
+	sized = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!sized) {
+		rc = -ENOMEM;
 		tpm2_end_auth_session(chip);
 		goto out_put;
 	}

-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	tpm_buf_init_sized(sized, TPM_BUFSIZE);
+
+	rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
 	if (rc)
 		goto out;

-	tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
+	tpm_buf_append_hmac_session(chip, buf, TPM2_SA_DECRYPT,
 				    options->keyauth, TPM_DIGEST_SIZE);

 	/* sensitive */
-	tpm_buf_append_u16(&sized, options->blobauth_len);
+	tpm_buf_append_u16(sized, options->blobauth_len);

 	if (options->blobauth_len)
-		tpm_buf_append(&sized, options->blobauth, options->blobauth_len);
+		tpm_buf_append(sized, options->blobauth, options->blobauth_len);

-	tpm_buf_append_u16(&sized, payload->key_len);
-	tpm_buf_append(&sized, payload->key, payload->key_len);
-	tpm_buf_append(&buf, sized.data, sized.length);
+	tpm_buf_append_u16(sized, payload->key_len);
+	tpm_buf_append(sized, payload->key, payload->key_len);
+	tpm_buf_append(buf, sized->data, sized->length);

 	/* public */
-	tpm_buf_reset_sized(&sized);
-	tpm_buf_append_u16(&sized, TPM_ALG_KEYEDHASH);
-	tpm_buf_append_u16(&sized, hash);
+	tpm_buf_reset_sized(sized);
+	tpm_buf_append_u16(sized, TPM_ALG_KEYEDHASH);
+	tpm_buf_append_u16(sized, hash);

 	/* key properties */
 	flags = 0;
 	flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
 	flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT);
-	tpm_buf_append_u32(&sized, flags);
+	tpm_buf_append_u32(sized, flags);

 	/* policy */
-	tpm_buf_append_u16(&sized, options->policydigest_len);
+	tpm_buf_append_u16(sized, options->policydigest_len);
 	if (options->policydigest_len)
-		tpm_buf_append(&sized, options->policydigest, options->policydigest_len);
+		tpm_buf_append(sized, options->policydigest, options->policydigest_len);

 	/* public parameters */
-	tpm_buf_append_u16(&sized, TPM_ALG_NULL);
-	tpm_buf_append_u16(&sized, 0);
+	tpm_buf_append_u16(sized, TPM_ALG_NULL);
+	tpm_buf_append_u16(sized, 0);

-	tpm_buf_append(&buf, sized.data, sized.length);
+	tpm_buf_append(buf, sized->data, sized->length);

 	/* outside info */
-	tpm_buf_append_u16(&buf, 0);
+	tpm_buf_append_u16(buf, 0);

 	/* creation PCR */
-	tpm_buf_append_u32(&buf, 0);
+	tpm_buf_append_u32(buf, 0);

-	if (buf.flags & TPM_BUF_OVERFLOW) {
+	if (buf->flags & TPM_BUF_OVERFLOW) {
 		rc = -E2BIG;
 		tpm2_end_auth_session(chip);
 		goto out;
 	}

-	rc = tpm_buf_fill_hmac_session(chip, &buf);
+	rc = tpm_buf_fill_hmac_session(chip, buf);
 	if (rc)
 		goto out;

-	rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
-	rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+	rc = tpm_transmit_cmd(chip, buf, 4, "sealing data");
+	rc = tpm_buf_check_hmac_response(chip, buf, rc);
 	if (rc)
 		goto out;

-	blob_len = tpm_buf_read_u32(&buf, &offset);
-	if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_BOUNDARY_ERROR) {
+	blob_len = tpm_buf_read_u32(buf, &offset);
+	if (blob_len > MAX_BLOB_SIZE || buf->flags & TPM_BUF_BOUNDARY_ERROR) {
 		rc = -E2BIG;
 		goto out;
 	}
-	if (buf.length - offset < blob_len) {
+	if (buf->length - offset < blob_len) {
 		rc = -EFAULT;
 		goto out;
 	}

-	blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len);
+	blob_len = tpm2_key_encode(payload, options, &buf->data[offset], blob_len);
 	if (blob_len < 0)
 		rc = blob_len;

 out:
-	tpm_buf_destroy(&sized);
-	tpm_buf_destroy(&buf);
-
 	if (!rc)
 		payload->blob_len = blob_len;

@@ -373,7 +377,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 			 u32 *blob_handle)
 {
 	u8 *blob_ref __free(kfree) = NULL;
-	struct tpm_buf buf;
+	struct tpm_buf *buf __free(kfree) = NULL;
 	unsigned int private_len;
 	unsigned int public_len;
 	unsigned int blob_len;
@@ -427,39 +431,38 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 	if (rc)
 		return rc;

-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
-	if (rc) {
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf) {
 		tpm2_end_auth_session(chip);
-		return rc;
+		return -ENOMEM;
 	}

-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+
+	rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
 	if (rc)
-		goto out;
+		return rc;

-	tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
+	tpm_buf_append_hmac_session(chip, buf, 0, options->keyauth,
 				    TPM_DIGEST_SIZE);

-	tpm_buf_append(&buf, blob, blob_len);
+	tpm_buf_append(buf, blob, blob_len);

-	if (buf.flags & TPM_BUF_OVERFLOW) {
-		rc = -E2BIG;
+	if (buf->flags & TPM_BUF_OVERFLOW) {
 		tpm2_end_auth_session(chip);
-		goto out;
+		return -E2BIG;
 	}

-	rc = tpm_buf_fill_hmac_session(chip, &buf);
+	rc = tpm_buf_fill_hmac_session(chip, buf);
 	if (rc)
-		goto out;
+		return rc;

-	rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob");
-	rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+	rc = tpm_transmit_cmd(chip, buf, 4, "loading blob");
+	rc = tpm_buf_check_hmac_response(chip, buf, rc);
 	if (!rc)
 		*blob_handle = be32_to_cpup(
-			(__be32 *) &buf.data[TPM_HEADER_SIZE]);
-
-out:
-	tpm_buf_destroy(&buf);
+			(__be32 *)&buf->data[TPM_HEADER_SIZE]);

 	return tpm_ret_to_err(rc);
 }
@@ -482,7 +485,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 			   u32 blob_handle)
 {
 	struct tpm_header *head;
-	struct tpm_buf buf;
+	struct tpm_buf *buf __free(kfree) = NULL;
 	u16 data_len;
 	int offset;
 	u8 *data;
@@ -492,18 +495,21 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 	if (rc)
 		return rc;

-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
-	if (rc) {
+	buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf) {
 		tpm2_end_auth_session(chip);
-		return rc;
+		return -ENOMEM;
 	}

-	rc = tpm_buf_append_name(chip, &buf, blob_handle, NULL);
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
+
+	rc = tpm_buf_append_name(chip, buf, blob_handle, NULL);
 	if (rc)
-		goto out;
+		return rc;

 	if (!options->policyhandle) {
-		tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
+		tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT,
 					    options->blobauth,
 					    options->blobauth_len);
 	} else {
@@ -518,39 +524,36 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 		 * could repeat our actions with the exfiltrated
 		 * password.
 		 */
-		tpm2_buf_append_auth(&buf, options->policyhandle,
+		tpm2_buf_append_auth(buf, options->policyhandle,
 				     NULL /* nonce */, 0, 0,
 				     options->blobauth, options->blobauth_len);
 		if (tpm2_chip_auth(chip)) {
-			tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0);
+			tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT,
+						    NULL, 0);
 		} else  {
-			offset = buf.handles * 4 + TPM_HEADER_SIZE;
-			head = (struct tpm_header *)buf.data;
-			if (tpm_buf_length(&buf) == offset)
+			offset = buf->handles * 4 + TPM_HEADER_SIZE;
+			head = (struct tpm_header *)buf->data;
+			if (tpm_buf_length(buf) == offset)
 				head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
 		}
 	}

-	rc = tpm_buf_fill_hmac_session(chip, &buf);
+	rc = tpm_buf_fill_hmac_session(chip, buf);
 	if (rc)
-		goto out;
+		return rc;

-	rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
-	rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+	rc = tpm_transmit_cmd(chip, buf, 6, "unsealing");
+	rc = tpm_buf_check_hmac_response(chip, buf, rc);

 	if (!rc) {
 		data_len = be16_to_cpup(
-			(__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
-		if (data_len < MIN_KEY_SIZE ||  data_len > MAX_KEY_SIZE) {
-			rc = -EFAULT;
-			goto out;
-		}
+			(__be16 *)&buf->data[TPM_HEADER_SIZE + 4]);
+		if (data_len < MIN_KEY_SIZE ||  data_len > MAX_KEY_SIZE)
+			return -EFAULT;

-		if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 6 + data_len) {
-			rc = -EFAULT;
-			goto out;
-		}
-		data = &buf.data[TPM_HEADER_SIZE + 6];
+		if (tpm_buf_length(buf) < TPM_HEADER_SIZE + 6 + data_len)
+			return -EFAULT;
+		data = &buf->data[TPM_HEADER_SIZE + 6];

 		if (payload->old_format) {
 			/* migratable flag is at the end of the key */
@@ -567,8 +570,6 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 		}
 	}

-out:
-	tpm_buf_destroy(&buf);
 	return tpm_ret_to_err(rc);
 }

--
2.47.3


^ permalink raw reply related

* [net-next] netlabel: fix IPv6 unlabeled address add error handling
From: Chenguang Zhao @ 2026-05-22  2:29 UTC (permalink / raw)
  To: Paul Moore, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman
  Cc: Chenguang Zhao, netdev, linux-security-module

netlbl_unlhsh_add_addr6() always returned zero after
netlbl_af6list_add(), masking failures such as duplicate
IPv6 static label entries.

Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
 net/netlabel/netlabel_unlabeled.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c
index ca7a9e2a3de7..0ab825d7f637 100644
--- a/net/netlabel/netlabel_unlabeled.c
+++ b/net/netlabel/netlabel_unlabeled.c
@@ -295,7 +295,7 @@ static int netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface,
 
 	if (ret_val != 0)
 		kfree(entry);
-	return 0;
+	return ret_val;
 }
 #endif /* IPv6 */
 
-- 
2.25.1


^ permalink raw reply related

* [PATCH bpf-next 00/13] Signed BPF + IPE Policies
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf; +Cc: ast, daniel, memxor, James.Bottomley, paul

This series continues the "Signed BPF programs" work and adds
the missing pieces needed for an LSM to do policy enforcement
and addresses the concerns raised by the developers of Hornet.

One signing scheme, please.

BPF does not need a second signing scheme. It needs a policy
framework that consumes the verdict the existing signing pipeline
produces. Two parallel signing stacks is harmful UX for Cilium,
bpftrace, systemd, distros, and everyone shipping signed lskels.
Hornet has been NACK'd repeatedly by the BPF maintainers [1][2]
on layering and TOCTOU grounds.

What this series adds

- prog->aux->sig (verdict + keyring) and prog->aux->is_kernel,
  populated by the syscall path before security_bpf_prog_load
  fires.
- bpf_loader_verify_metadata kfunc -- the metadata check is now
  kernel C code, not BPF bytecode. The verifier injects the
  calling prog->aux as an implicit argument via KF_IMPLICIT_ARGS.
- Loader-side prog BTF with BPF_PSEUDO_KFUNC_CALL_PROG_BTF so
  the kfunc CALL is reproducible across build hosts and resolved
  at load time.
- security_bpf_prog_load_post_integrity LSM hook, fired by the
  kfunc on a successful metadata check.
- IPE properties (bpf_signature, bpf_keyring, bpf_kernel) and
  two ops (BPF_PROG_LOAD, BPF_PROG_LOAD_POST_INTEGRITY).

This series address concerns raised by the Hornet developers:

* The metadata hash check should be in kernel C, not BPF
  bytecode -- Blaise Boscaccy [3]:

  The bpf_loader_verify_metadata kfunc moves the hash check from
  inline BPF instructions into kernel C code.

* LSMs cannot observe the verification result at hook time --
  Paul Moore [4]:

  prog->aux->sig.verdict and sig.keyring are populated before any
  LSM hook runs. Furthermore, security_bpf_prog_load_post_integrity
  hook fires after the in-kernel hash check for consumers that want
  to observe or gate the post-integrity transition.


[1] Alexei Starovoitov, NACK on Hornet (TOCTOU + layering),
    https://lore.kernel.org/all/CAADnVQJ1CRvTXBU771KaYzrx-vRaWF+k164DcFOqOsCxmuL+ig@mail.gmail.com/
[2] Daniel Borkmann, NACK on Hornet v3,
    https://lore.kernel.org/all/798dba24-b5a7-4584-a1f6-793883fe9b5e@iogearbox.net/
[3] Blaise Boscaccy, Hornet v6 (C-side hash verification rationale),
    https://lore.kernel.org/all/20260429191431.2345448-1-bboscaccy@linux.microsoft.com/
[4] Paul Moore, push for post-verifier observability,
    https://lore.kernel.org/all/CACYkzJ4+=3owK+ELD9Nw7Rrm-UajxXEw8kVtOTJJ+SNAXpsOpw@mail.gmail.com/


KP Singh (13):
  bpf: expose signature verdict to LSMs via bpf_prog_aux
  bpf: include prog BTF in the signed loader signature scope
  bpf, libbpf: load prog BTF in the skel_internal loader
  bpf: add bpf_loader_verify_metadata kfunc
  bpf: compute prog->digest at BPF_PROG_LOAD entry
  bpf: resolve loader-style kfunc CALLs against prog BTF
  libbpf: generate prog BTF for loader programs
  bpftool gen: embed loader prog BTF in the lskel header
  lsm: add bpf_prog_load_post_integrity hook
  bpf: invoke security_bpf_prog_load_post_integrity from the metadata
    kfunc
  ipe: add BPF program signature properties
  ipe: gate post-integrity BPF program loads
  selftests/bpf: add IPE BPF policy integration tests

 include/linux/bpf.h                           |  19 +++
 include/linux/bpf_verifier.h                  |   6 +
 include/linux/btf.h                           |   1 +
 include/linux/lsm_hook_defs.h                 |   1 +
 include/linux/security.h                      |   6 +
 include/uapi/linux/bpf.h                      |   5 +
 kernel/bpf/btf.c                              |   8 +
 kernel/bpf/check_btf.c                        |  18 +-
 kernel/bpf/helpers.c                          |  65 ++++++++
 kernel/bpf/syscall.c                          |  76 ++++++++-
 kernel/bpf/verifier.c                         |  58 ++++++-
 security/ipe/Kconfig                          |  14 ++
 security/ipe/audit.c                          |  13 ++
 security/ipe/eval.c                           |  57 +++++++
 security/ipe/eval.h                           |   5 +
 security/ipe/hooks.c                          |  42 +++++
 security/ipe/hooks.h                          |   9 +
 security/ipe/ipe.c                            |   4 +
 security/ipe/policy.h                         |  11 ++
 security/ipe/policy_parser.c                  |  20 +++
 security/security.c                           |  17 ++
 tools/bpf/bpftool/gen.c                       |  21 +++
 tools/bpf/bpftool/sign.c                      |  17 +-
 tools/include/uapi/linux/bpf.h                |   5 +
 tools/lib/bpf/bpf_gen_internal.h              |   2 +
 tools/lib/bpf/gen_loader.c                    | 127 +++++++++++---
 tools/lib/bpf/libbpf.h                        |   4 +-
 tools/lib/bpf/skel_internal.h                 |  67 +++++---
 .../selftests/bpf/test_signed_bpf_ipe.sh      | 156 ++++++++++++++++++
 tools/testing/selftests/bpf/vmtest.sh         |   4 +-
 30 files changed, 775 insertions(+), 83 deletions(-)
 create mode 100755 tools/testing/selftests/bpf/test_signed_bpf_ipe.sh

-- 
2.53.0


^ permalink raw reply

* [PATCH bpf-next 01/13] bpf: expose signature verdict to LSMs via bpf_prog_aux
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: ast, daniel, memxor, James.Bottomley, paul, KP Singh
In-Reply-To: <20260522023234.3778588-1-kpsingh@kernel.org>

BPF_PROG_LOAD verifies the loader signature but does not record the
outcome on the prog. LSMs and audit can read attr->signature and
attr->keyring_id to infer "was this signed, against which keyring",
but there is no canonical state for "loader signature + map content
verified". Only the in-kernel kfunc can confirm the latter.

Add prog->aux->sig (verdict + keyring) and prog->aux->is_kernel,
populated by bpf_prog_load before the LSM hook. Failed verifications
reject the load before the hook runs, so it observes only UNSIGNED
or OK. The bpf_loader_verify_metadata kfunc promotes to
METADATA_VERIFIED later.

Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 include/linux/bpf.h  | 19 +++++++++++++++++++
 kernel/bpf/syscall.c | 20 ++++++++++++++++++++
 2 files changed, 39 insertions(+)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 11bec73db199..14f65259f414 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1656,6 +1656,20 @@ struct bpf_stream_stage {
 	int len;
 };
 
+enum bpf_sig_verdict {
+	BPF_SIG_UNSIGNED = 0,
+	BPF_SIG_OK,                  /* loader signature verified */
+	BPF_SIG_METADATA_VERIFIED,   /* loader signature + map content verified */
+};
+
+enum bpf_sig_keyring {
+	BPF_SIG_KEYRING_NONE = 0,
+	BPF_SIG_KEYRING_BUILTIN,
+	BPF_SIG_KEYRING_SECONDARY,
+	BPF_SIG_KEYRING_PLATFORM,
+	BPF_SIG_KEYRING_USER,
+};
+
 struct bpf_prog_aux {
 	atomic64_t refcnt;
 	u32 used_map_cnt;
@@ -1698,6 +1712,11 @@ struct bpf_prog_aux {
 	bool changes_pkt_data;
 	bool might_sleep;
 	bool kprobe_write_ctx;
+	struct {
+		u8 verdict;
+		u8 keyring;
+	} sig;
+	bool is_kernel;
 	u64 prog_array_member_cnt; /* counts how many times as member of prog_array */
 	struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cnt */
 	struct bpf_arena *arena;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 630d530782fe..51fe8d77bb39 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2798,6 +2798,20 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
 	}
 }
 
+static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)
+{
+	switch (keyring_id) {
+	case 0:
+		return BPF_SIG_KEYRING_BUILTIN;
+	case (s32)(unsigned long)VERIFY_USE_SECONDARY_KEYRING:
+		return BPF_SIG_KEYRING_SECONDARY;
+	case (s32)(unsigned long)VERIFY_USE_PLATFORM_KEYRING:
+		return BPF_SIG_KEYRING_PLATFORM;
+	default:
+		return BPF_SIG_KEYRING_USER;
+	}
+}
+
 static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr,
 				     bool is_kernel)
 {
@@ -3027,7 +3041,13 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
 		err = bpf_prog_verify_signature(prog, attr, uattr.is_kernel);
 		if (err)
 			goto free_prog;
+		prog->aux->sig.verdict  = BPF_SIG_OK;
+		prog->aux->sig.keyring = bpf_classify_keyring(attr->keyring_id);
+	} else {
+		prog->aux->sig.verdict  = BPF_SIG_UNSIGNED;
+		prog->aux->sig.keyring = BPF_SIG_KEYRING_NONE;
 	}
+	prog->aux->is_kernel = uattr.is_kernel;
 
 	prog->orig_prog = NULL;
 	prog->jited = 0;
-- 
2.53.0


^ permalink raw reply related

* [PATCH bpf-next 02/13] bpf: include prog BTF in the signed loader signature scope
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: ast, daniel, memxor, James.Bottomley, paul, KP Singh
In-Reply-To: <20260522023234.3778588-1-kpsingh@kernel.org>

bpf_prog_verify_signature hashes only prog->insnsi, so FUNC names
in prog BTF are not covered by the signature. Since we need to
support kfuncs for loader programs with prog BTF information, the
signature needs to cover BTF so that it can be trusted.

When attr->prog_btf_fd is set, build the verify dynptr over
insns || raw_btf so the signature covers both. Fall back to insns-only
otherwise so existing signed lskels keep loading. Hoist the
prog_btf_fd resolution into bpf_prog_load so the BTF is available
before signature verification.

Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 include/linux/btf.h    |  1 +
 kernel/bpf/btf.c       |  8 +++++++
 kernel/bpf/check_btf.c | 18 +++-------------
 kernel/bpf/syscall.c   | 49 +++++++++++++++++++++++++++++++++++++-----
 4 files changed, 56 insertions(+), 20 deletions(-)

diff --git a/include/linux/btf.h b/include/linux/btf.h
index 48108471c5b1..ab1fe7c8df20 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -216,6 +216,7 @@ int btf_type_snprintf_show(const struct btf *btf, u32 type_id, void *obj,
 int btf_get_fd_by_id(u32 id);
 u32 btf_obj_id(const struct btf *btf);
 bool btf_is_kernel(const struct btf *btf);
+const void *btf_get_raw_data(const struct btf *btf, u32 *data_size);
 bool btf_is_module(const struct btf *btf);
 bool btf_is_vmlinux(const struct btf *btf);
 struct module *btf_try_get_module(const struct btf *btf);
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index a62d78581207..6f1d9b3ba6a3 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -8322,11 +8322,19 @@ u32 btf_obj_id(const struct btf *btf)
 	return READ_ONCE(btf->id);
 }
 
+const void *btf_get_raw_data(const struct btf *btf, u32 *data_size)
+{
+	if (data_size)
+		*data_size = btf->data_size;
+	return btf->data;
+}
+
 bool btf_is_kernel(const struct btf *btf)
 {
 	return btf->kernel_btf;
 }
 
+
 bool btf_is_module(const struct btf *btf)
 {
 	return btf->kernel_btf && strcmp(btf->name, "vmlinux") != 0;
diff --git a/kernel/bpf/check_btf.c b/kernel/bpf/check_btf.c
index 93bebe6fe12e..c52cc859abac 100644
--- a/kernel/bpf/check_btf.c
+++ b/kernel/bpf/check_btf.c
@@ -411,28 +411,16 @@ int bpf_check_btf_info_early(struct bpf_verifier_env *env,
 			     const union bpf_attr *attr,
 			     bpfptr_t uattr)
 {
-	struct btf *btf;
-	int err;
-
 	if (!attr->func_info_cnt && !attr->line_info_cnt) {
 		if (check_abnormal_return(env))
 			return -EINVAL;
 		return 0;
 	}
 
-	btf = btf_get_by_fd(attr->prog_btf_fd);
-	if (IS_ERR(btf))
-		return PTR_ERR(btf);
-	if (btf_is_kernel(btf)) {
-		btf_put(btf);
-		return -EACCES;
-	}
-	env->prog->aux->btf = btf;
+	if (env->prog->aux->btf)
+		return check_btf_func_early(env, attr, uattr);
 
-	err = check_btf_func_early(env, attr, uattr);
-	if (err)
-		return err;
-	return 0;
+	return -EINVAL;
 }
 
 int bpf_check_btf_info(struct bpf_verifier_env *env,
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 51fe8d77bb39..6d1db5eaad3c 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2816,9 +2816,11 @@ static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr
 				     bool is_kernel)
 {
 	bpfptr_t usig = make_bpfptr(attr->signature, is_kernel);
-	struct bpf_dynptr_kern sig_ptr, insns_ptr;
+	struct bpf_dynptr_kern sig_ptr, data_ptr;
 	struct bpf_key *key = NULL;
-	void *sig;
+	u32 insns_sz, btf_sz = 0;
+	const void *btf_data = NULL;
+	void *sig, *data = NULL;
 	int err = 0;
 
 	/*
@@ -2842,14 +2844,34 @@ static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr
 		return PTR_ERR(sig);
 	}
 
+	insns_sz = prog->len * sizeof(struct bpf_insn);
+
+	if (prog->aux->btf)
+		btf_data = btf_get_raw_data(prog->aux->btf, &btf_sz);
+
+	if (bpf_dynptr_check_size(insns_sz + btf_sz)) {
+		err = -E2BIG;
+		goto out;
+	}
+	data = kvmalloc(insns_sz + btf_sz, GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto out;
+	}
+	memcpy(data, prog->insnsi, insns_sz);
+	if (btf_sz)
+		memcpy(data + insns_sz, btf_data, btf_sz);
+
+	bpf_dynptr_init(&data_ptr, data, BPF_DYNPTR_TYPE_LOCAL, 0,
+			insns_sz + btf_sz);
 	bpf_dynptr_init(&sig_ptr, sig, BPF_DYNPTR_TYPE_LOCAL, 0,
 			attr->signature_size);
-	bpf_dynptr_init(&insns_ptr, prog->insnsi, BPF_DYNPTR_TYPE_LOCAL, 0,
-			prog->len * sizeof(struct bpf_insn));
 
-	err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&insns_ptr,
+	err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&data_ptr,
 					 (struct bpf_dynptr *)&sig_ptr, key);
 
+out:
+	kvfree(data);
 	bpf_key_put(key);
 	kvfree(sig);
 	return err;
@@ -3037,6 +3059,21 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
 	/* eBPF programs must be GPL compatible to use GPL-ed functions */
 	prog->gpl_compatible = license_is_gpl_compatible(license) ? 1 : 0;
 
+	if (attr->prog_btf_fd) {
+		struct btf *btf = btf_get_by_fd(attr->prog_btf_fd);
+
+		if (IS_ERR(btf)) {
+			err = PTR_ERR(btf);
+			goto free_prog;
+		}
+		if (btf_is_kernel(btf)) {
+			btf_put(btf);
+			err = -EACCES;
+			goto free_prog;
+		}
+		prog->aux->btf = btf;
+	}
+
 	if (attr->signature) {
 		err = bpf_prog_verify_signature(prog, attr, uattr.is_kernel);
 		if (err)
@@ -3148,6 +3185,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
 	free_uid(prog->aux->user);
 	if (prog->aux->attach_btf)
 		btf_put(prog->aux->attach_btf);
+	if (prog->aux->btf)
+		btf_put(prog->aux->btf);
 	bpf_prog_free(prog);
 put_token:
 	bpf_token_put(token);
-- 
2.53.0


^ permalink raw reply related

* [PATCH bpf-next 03/13] bpf, libbpf: load prog BTF in the skel_internal loader
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: ast, daniel, memxor, James.Bottomley, paul, KP Singh
In-Reply-To: <20260522023234.3778588-1-kpsingh@kernel.org>

bpf_load_and_run loads only the loader insns and the metadata map.
To match the kernel's extended signature scope (insns || btf), the
loader needs to BPF_BTF_LOAD the prog BTF before BPF_PROG_LOAD so
attr->prog_btf_fd can be filled in.

Add btf and btf_sz to bpf_load_and_run_opts; when set, do the
BPF_BTF_LOAD before BPF_PROG_LOAD and pass the resulting fd as
attr->prog_btf_fd.

Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 tools/lib/bpf/skel_internal.h | 44 ++++++++++++++++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.h
index 6a8f5c7a02eb..0b6b1ecedd45 100644
--- a/tools/lib/bpf/skel_internal.h
+++ b/tools/lib/bpf/skel_internal.h
@@ -11,6 +11,8 @@
 #include <linux/bpf.h>
 #else
 #include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
 #include <sys/syscall.h>
 #include <sys/mman.h>
 #include <linux/keyctl.h>
@@ -66,8 +68,10 @@ struct bpf_load_and_run_opts {
 	struct bpf_loader_ctx *ctx;
 	const void *data;
 	const void *insns;
+	const void *btf;
 	__u32 data_sz;
 	__u32 insns_sz;
+	__u32 btf_sz;
 	const char *errstr;
 	void *signature;
 	__u32 signature_sz;
@@ -88,6 +92,22 @@ static inline int skel_sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
 #endif
 }
 
+#ifndef __KERNEL__
+static inline int skel_sys_btf_load(union bpf_attr *attr, unsigned int size)
+{
+	int fd;
+
+	fd = skel_sys_bpf(BPF_BTF_LOAD, attr, size);
+	if (fd >= 0 && fd < 3) {
+		int new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+
+		close(fd);
+		fd = new_fd;
+	}
+	return fd;
+}
+#endif
+
 #ifdef __KERNEL__
 static inline int close(int fd)
 {
@@ -353,8 +373,9 @@ static inline int skel_map_freeze(int fd)
 static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
 {
 	const size_t prog_load_attr_sz = offsetofend(union bpf_attr, keyring_id);
+	const size_t btf_load_attr_sz = offsetofend(union bpf_attr, btf_token_fd);
 	const size_t test_run_attr_sz = offsetofend(union bpf_attr, test);
-	int map_fd = -1, prog_fd = -1, key = 0, err;
+	int map_fd = -1, prog_fd = -1, btf_fd = -1, key = 0, err;
 	union bpf_attr attr;
 
 	err = map_fd = skel_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4, opts->data_sz, 1,
@@ -387,11 +408,30 @@ static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
 	}
 #endif
 
+#ifndef __KERNEL__
+	if (opts->btf && opts->btf_sz) {
+		memset(&attr, 0, btf_load_attr_sz);
+		attr.btf = (long) opts->btf;
+		attr.btf_size = opts->btf_sz;
+		attr.btf_log_level = opts->ctx->log_level;
+		attr.btf_log_size = opts->ctx->log_size;
+		attr.btf_log_buf = opts->ctx->log_buf;
+		err = btf_fd = skel_sys_btf_load(&attr, btf_load_attr_sz);
+		if (btf_fd < 0) {
+			opts->errstr = "failed to load loader BTF";
+			set_err;
+			goto out;
+		}
+	}
+#endif
+
 	memset(&attr, 0, prog_load_attr_sz);
 	attr.prog_type = BPF_PROG_TYPE_SYSCALL;
 	attr.insns = (long) opts->insns;
 	attr.insn_cnt = opts->insns_sz / sizeof(struct bpf_insn);
 	attr.license = (long) "Dual BSD/GPL";
+	if (btf_fd >= 0)
+		attr.prog_btf_fd = btf_fd;
 #ifndef __KERNEL__
 	attr.signature = (long) opts->signature;
 	attr.signature_size = opts->signature_sz;
@@ -437,6 +477,8 @@ static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
 		close(map_fd);
 	if (prog_fd >= 0)
 		close(prog_fd);
+	if (btf_fd >= 0)
+		close(btf_fd);
 	return err;
 }
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH bpf-next 04/13] bpf: add bpf_loader_verify_metadata kfunc
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: ast, daniel, memxor, James.Bottomley, paul, KP Singh
In-Reply-To: <20260522023234.3778588-1-kpsingh@kernel.org>

A signed loader reaches sig.verdict = BPF_SIG_OK after PKCS#7
verification, but the metadata map content the loader writes has not
been checked against its expected SHA256. The map carries that SHA256
in the signed instruction stream, spilled to the loader's stack at
runtime as four ld_imm64 immediates.

Assert the map is exclusive, compare its frozen content hash against
the spilled SHA256, and promote sig.verdict to
BPF_SIG_METADATA_VERIFIED. The verifier injects the calling
prog->aux as an implicit argument via KF_IMPLICIT_ARGS so the kfunc
can read the prog digest and signature verdict directly.

Drop skel_obj_get_info_by_fd from bpf_load_and_run since the kfunc
computes the map hash itself via map_get_hash; the prior
BPF_OBJ_GET_INFO_BY_FD call to populate map->sha is not
needed.

Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 kernel/bpf/helpers.c          | 57 +++++++++++++++++++++++++++++++++++
 tools/bpf/bpftool/sign.c      | 17 ++++++++++-
 tools/lib/bpf/skel_internal.h | 25 ---------------
 3 files changed, 73 insertions(+), 26 deletions(-)

diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index b5314c9fed3c..9afa71fbcac3 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -4257,6 +4257,53 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_p,
 	return -EOPNOTSUPP;
 #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
 }
+
+/**
+ * bpf_loader_verify_metadata - perform the signed loader's metadata-map check
+ * in a single kernel-side step.
+ *
+ * Asserts the metadata map is exclusive, compares its frozen content hash
+ * against the expected SHA256 carried in the loader's signed instruction
+ * stream, and promotes sig.verdict from BPF_SIG_OK to
+ * BPF_SIG_METADATA_VERIFIED.
+ *
+ * @map:       metadata map bound to this loader via excl_prog_hash at sign time
+ * @hash:      pointer to the expected SHA256, spilled to the loader's BPF stack
+ *             from signed ld_imm64 immediates
+ * @hash__sz:  byte length of @hash (must equal SHA256_DIGEST_SIZE)
+ * @aux__ign:  verifier-supplied prog aux; BPF programs do not set it
+ *
+ * Return: 0 on success, -EPERM if not called from a signed loader,
+ * -EINVAL if the map is not exclusive or the hash buffer is the wrong size,
+ * -EBADMSG if the hash does not match.
+ */
+__bpf_kfunc int bpf_loader_verify_metadata(struct bpf_map *map,
+					   const u64 *hash, u32 hash__sz,
+					   struct bpf_prog_aux *aux__ign)
+{
+	u8 sha[SHA256_DIGEST_SIZE];
+	int err;
+
+	if (!aux__ign || aux__ign->sig.verdict != BPF_SIG_OK)
+		return -EPERM;
+	if (!map->excl_prog_sha || hash__sz != SHA256_DIGEST_SIZE)
+		return -EINVAL;
+	if (memcmp(map->excl_prog_sha, aux__ign->prog->digest, SHA256_DIGEST_SIZE))
+		return -EPERM;
+	if (!READ_ONCE(map->frozen))
+		return -EPERM;
+	if (!map->ops->map_get_hash)
+		return -EINVAL;
+	err = map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, sha);
+	if (err)
+		return err;
+	if (memcmp(sha, hash, SHA256_DIGEST_SIZE))
+		return -EBADMSG;
+
+	aux__ign->sig.verdict = BPF_SIG_METADATA_VERIFIED;
+	return 0;
+}
+
 #endif /* CONFIG_KEYS */
 
 typedef int (*bpf_task_work_callback_t)(struct bpf_map *map, void *key, void *value);
@@ -4764,6 +4811,15 @@ static const struct btf_kfunc_id_set generic_kfunc_set = {
 	.set   = &generic_btf_ids,
 };
 
+BTF_KFUNCS_START(syscall_btf_ids)
+BTF_ID_FLAGS(func, bpf_loader_verify_metadata, KF_SLEEPABLE | KF_IMPLICIT_ARGS)
+BTF_KFUNCS_END(syscall_btf_ids)
+
+static const struct btf_kfunc_id_set syscall_kfunc_set = {
+	.owner = THIS_MODULE,
+	.set   = &syscall_btf_ids,
+};
+
 
 BTF_ID_LIST(generic_dtor_ids)
 BTF_ID(struct, task_struct)
@@ -4893,6 +4949,7 @@ static int __init kfunc_init(void)
 	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &generic_kfunc_set);
 	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &generic_kfunc_set);
 	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_CGROUP_SKB, &generic_kfunc_set);
+	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &syscall_kfunc_set);
 	ret = ret ?: register_btf_id_dtor_kfuncs(generic_dtors,
 						  ARRAY_SIZE(generic_dtors),
 						  THIS_MODULE);
diff --git a/tools/bpf/bpftool/sign.c b/tools/bpf/bpftool/sign.c
index f9b742f4bb10..13f256546cf0 100644
--- a/tools/bpf/bpftool/sign.c
+++ b/tools/bpf/bpftool/sign.c
@@ -134,10 +134,24 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
 	EVP_PKEY *private_key = NULL;
 	CMS_ContentInfo *cms = NULL;
 	long actual_sig_len = 0;
+	void *signed_buf = NULL;
+	size_t signed_sz;
 	X509 *x509 = NULL;
 	int err = 0;
 
-	bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
+	signed_sz = opts->insns_sz + opts->btf_sz;
+	if (opts->btf_sz) {
+		signed_buf = malloc(signed_sz);
+		if (!signed_buf) {
+			err = -ENOMEM;
+			goto cleanup;
+		}
+		memcpy(signed_buf, opts->insns, opts->insns_sz);
+		memcpy(signed_buf + opts->insns_sz, opts->btf, opts->btf_sz);
+		bd_in = BIO_new_mem_buf(signed_buf, signed_sz);
+	} else {
+		bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
+	}
 	if (!bd_in) {
 		err = -ENOMEM;
 		goto cleanup;
@@ -212,6 +226,7 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
 	X509_free(x509);
 	EVP_PKEY_free(private_key);
 	BIO_free(bd_in);
+	free(signed_buf);
 	DISPLAY_OSSL_ERR(err < 0);
 	return err;
 }
diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.h
index 0b6b1ecedd45..d194f4e23d12 100644
--- a/tools/lib/bpf/skel_internal.h
+++ b/tools/lib/bpf/skel_internal.h
@@ -335,25 +335,6 @@ static inline int skel_link_create(int prog_fd, int target_fd,
 	return skel_sys_bpf(BPF_LINK_CREATE, &attr, attr_sz);
 }
 
-static inline int skel_obj_get_info_by_fd(int fd)
-{
-	const size_t attr_sz = offsetofend(union bpf_attr, info);
-	__u8 sha[SHA256_DIGEST_LENGTH];
-	struct bpf_map_info info;
-	__u32 info_len = sizeof(info);
-	union bpf_attr attr;
-
-	memset(&info, 0, sizeof(info));
-	info.hash = (long) &sha;
-	info.hash_size = SHA256_DIGEST_LENGTH;
-
-	memset(&attr, 0, attr_sz);
-	attr.info.bpf_fd = fd;
-	attr.info.info = (long) &info;
-	attr.info.info_len = info_len;
-	return skel_sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, attr_sz);
-}
-
 static inline int skel_map_freeze(int fd)
 {
 	const size_t attr_sz = offsetofend(union bpf_attr, map_fd);
@@ -400,12 +381,6 @@ static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
 		set_err;
 		goto out;
 	}
-	err = skel_obj_get_info_by_fd(map_fd);
-	if (err < 0) {
-		opts->errstr = "failed to fetch obj info";
-		set_err;
-		goto out;
-	}
 #endif
 
 #ifndef __KERNEL__
-- 
2.53.0


^ permalink raw reply related

* [PATCH bpf-next 05/13] bpf: compute prog->digest at BPF_PROG_LOAD entry
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: ast, daniel, memxor, James.Bottomley, paul, KP Singh
In-Reply-To: <20260522023234.3778588-1-kpsingh@kernel.org>

add_subprog_and_kfunc relocates kfunc CALLs by patching insn->imm
and src_reg, and bpf_prog_calc_tag has no rule to mask kfunc CALL
fields. The tag has to be computed over the unmodified user-supplied
insns to match the excl_prog_hash userspace signed, so move the call
to bpf_prog_load right after signature verification.

Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 kernel/bpf/syscall.c  | 7 +++++++
 kernel/bpf/verifier.c | 4 ----
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 6d1db5eaad3c..39ebd825c136 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -3086,6 +3086,13 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
 	}
 	prog->aux->is_kernel = uattr.is_kernel;
 
+	/* Hash insns now, before any verifier-side rewrite, so prog->digest
+	 * matches the excl_prog_hash userspace computed.
+	 */
+	err = bpf_prog_calc_tag(prog);
+	if (err)
+		goto free_prog;
+
 	prog->orig_prog = NULL;
 	prog->jited = 0;
 
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 7fb88e1cd7c4..f0e45cfa5b34 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -18434,10 +18434,6 @@ static int check_and_resolve_insns(struct bpf_verifier_env *env)
 	int insn_cnt = env->prog->len;
 	int i, err;
 
-	err = bpf_prog_calc_tag(env->prog);
-	if (err)
-		return err;
-
 	for (i = 0; i < insn_cnt; i++, insn++) {
 		if (insn->dst_reg >= MAX_BPF_REG) {
 			verbose(env, "R%d is invalid\n", insn->dst_reg);
-- 
2.53.0


^ permalink raw reply related

* [PATCH bpf-next 06/13] bpf: resolve loader-style kfunc CALLs against prog BTF
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: ast, daniel, memxor, James.Bottomley, paul, KP Singh
In-Reply-To: <20260522023234.3778588-1-kpsingh@kernel.org>

gen_loader-emitted signed loaders cannot bake vmlinux BTF ids into
kfunc CALL imm at sign time. Add a new pseudo
BPF_PSEUDO_KFUNC_CALL_PROG_BTF that gen_loader emits in src_reg, with
imm holding the FUNC type id in the loader's own prog BTF.

In add_subprog_and_kfunc, look the FUNC up by name in vmlinux BTF,
patch imm with the resolved id, and rewrite src_reg back to
BPF_PSEUDO_KFUNC_CALL so downstream passes see a normal kfunc CALL.
Leave standard src_reg calls unchanged.

Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 include/linux/bpf_verifier.h   |  6 ++++
 include/uapi/linux/bpf.h       |  5 ++++
 kernel/bpf/verifier.c          | 54 ++++++++++++++++++++++++++++++++--
 tools/include/uapi/linux/bpf.h |  5 ++++
 4 files changed, 67 insertions(+), 3 deletions(-)

diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 185b2aa43a42..396b85830996 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -959,6 +959,12 @@ static inline bool bpf_pseudo_kfunc_call(const struct bpf_insn *insn)
 	       insn->src_reg == BPF_PSEUDO_KFUNC_CALL;
 }
 
+static inline bool bpf_pseudo_kfunc_call_prog_btf(const struct bpf_insn *insn)
+{
+	return insn->code == (BPF_JMP | BPF_CALL) &&
+	       insn->src_reg == BPF_PSEUDO_KFUNC_CALL_PROG_BTF;
+}
+
 __printf(2, 0) void bpf_verifier_vlog(struct bpf_verifier_log *log,
 				      const char *fmt, va_list args);
 __printf(2, 3) void bpf_verifier_log_write(struct bpf_verifier_env *env,
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 552bc5d9afbd..06056e714e8e 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1382,6 +1382,11 @@ enum {
  * bpf_call->imm == btf_id of a BTF_KIND_FUNC in the running kernel
  */
 #define BPF_PSEUDO_KFUNC_CALL	2
+/* when bpf_call->src_reg == BPF_PSEUDO_KFUNC_CALL_PROG_BTF,
+ * bpf_call->imm == btf_id of a BTF_KIND_FUNC in the program's
+ * prog BTF. The verifier resolves it to a vmlinux btf_id by name.
+ */
+#define BPF_PSEUDO_KFUNC_CALL_PROG_BTF	3
 
 enum bpf_addr_space_cast {
 	BPF_ADDR_SPACE_CAST = 1,
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index f0e45cfa5b34..1b5d06b9d74a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -3088,6 +3088,47 @@ bool bpf_prog_has_kfunc_call(const struct bpf_prog *prog)
 	return !!prog->aux->kfunc_tab;
 }
 
+/*
+ * Resolve a gen_loader-emitted kfunc CALL by FUNC name in vmlinux BTF,
+ * then rewrite src_reg back to BPF_PSEUDO_KFUNC_CALL. Caller must have
+ * already filtered for BPF_PSEUDO_KFUNC_CALL_PROG_BTF.
+ */
+static int resolve_loader_kfunc(struct bpf_verifier_env *env,
+				struct bpf_insn *insn, int insn_idx)
+{
+	struct btf *prog_btf = env->prog->aux->btf;
+	const struct btf_type *t;
+	const char *name;
+	s32 vmlinux_id;
+
+	if (!prog_btf || !btf_vmlinux || insn->off) {
+		verbose(env, "kfunc call insn %d: PROG_BTF resolution requires prog BTF and insn->off == 0\n",
+			insn_idx);
+		return -EINVAL;
+	}
+	t = btf_type_by_id(prog_btf, insn->imm);
+	if (!t || !btf_type_is_func(t)) {
+		verbose(env, "kfunc call insn %d: imm %d is not a FUNC in prog BTF\n",
+			insn_idx, insn->imm);
+		return -EINVAL;
+	}
+	name = btf_name_by_offset(prog_btf, t->name_off);
+	if (!name || !name[0]) {
+		verbose(env, "kfunc call insn %d: prog-BTF FUNC has no name\n",
+			insn_idx);
+		return -EINVAL;
+	}
+	vmlinux_id = btf_find_by_name_kind(btf_vmlinux, name, BTF_KIND_FUNC);
+	if (vmlinux_id < 0) {
+		verbose(env, "kfunc call insn %d: %s not found in vmlinux BTF\n",
+			insn_idx, name);
+		return vmlinux_id;
+	}
+	insn->imm = vmlinux_id;
+	insn->src_reg = BPF_PSEUDO_KFUNC_CALL;
+	return 0;
+}
+
 static int add_subprog_and_kfunc(struct bpf_verifier_env *env)
 {
 	struct bpf_subprog_info *subprog = env->subprog_info;
@@ -3101,7 +3142,8 @@ static int add_subprog_and_kfunc(struct bpf_verifier_env *env)
 
 	for (i = 0; i < insn_cnt; i++, insn++) {
 		if (!bpf_pseudo_func(insn) && !bpf_pseudo_call(insn) &&
-		    !bpf_pseudo_kfunc_call(insn))
+		    !bpf_pseudo_kfunc_call(insn) &&
+		    !bpf_pseudo_kfunc_call_prog_btf(insn))
 			continue;
 
 		if (!env->bpf_capable) {
@@ -3109,10 +3151,16 @@ static int add_subprog_and_kfunc(struct bpf_verifier_env *env)
 			return -EPERM;
 		}
 
-		if (bpf_pseudo_func(insn) || bpf_pseudo_call(insn))
+		if (bpf_pseudo_func(insn) || bpf_pseudo_call(insn)) {
 			ret = add_subprog(env, i + insn->imm + 1);
-		else
+		} else {
+			if (bpf_pseudo_kfunc_call_prog_btf(insn)) {
+				ret = resolve_loader_kfunc(env, insn, i);
+				if (ret < 0)
+					return ret;
+			}
 			ret = bpf_add_kfunc_call(env, insn->imm, insn->off);
+		}
 
 		if (ret < 0)
 			return ret;
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 677be9a47347..d4f7f3e0aaa3 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1382,6 +1382,11 @@ enum {
  * bpf_call->imm == btf_id of a BTF_KIND_FUNC in the running kernel
  */
 #define BPF_PSEUDO_KFUNC_CALL	2
+/* when bpf_call->src_reg == BPF_PSEUDO_KFUNC_CALL_PROG_BTF,
+ * bpf_call->imm == btf_id of a BTF_KIND_FUNC in the program's
+ * prog BTF. The verifier resolves it to a vmlinux btf_id by name.
+ */
+#define BPF_PSEUDO_KFUNC_CALL_PROG_BTF	3
 
 enum bpf_addr_space_cast {
 	BPF_ADDR_SPACE_CAST = 1,
-- 
2.53.0


^ permalink raw reply related

* [PATCH bpf-next 07/13] libbpf: generate prog BTF for loader programs
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: ast, daniel, memxor, James.Bottomley, paul, KP Singh
In-Reply-To: <20260522023234.3778588-1-kpsingh@kernel.org>

For signed loaders, build a minimal prog BTF containing one FUNC entry
for bpf_loader_verify_metadata with a FUNC_PROTO of primitives (int,
void *) so the bytes are reproducible across build hosts.

In emit_verify_metadata, spill four signed ld_imm64 immediates into
a 32-byte stack buffer and invoke the kfunc once with (map, &buf, 32),
replacing ~24 inline dword comparisons. Expose the serialized BTF on
gen_loader_opts from bpf_gen__finish so bpftool gen can embed it in
the lskel.

Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 tools/lib/bpf/bpf_gen_internal.h |   2 +
 tools/lib/bpf/gen_loader.c       | 127 ++++++++++++++++++++++++-------
 tools/lib/bpf/libbpf.h           |   4 +-
 3 files changed, 105 insertions(+), 28 deletions(-)

diff --git a/tools/lib/bpf/bpf_gen_internal.h b/tools/lib/bpf/bpf_gen_internal.h
index 49af4260b8e6..e4aed9e99b56 100644
--- a/tools/lib/bpf/bpf_gen_internal.h
+++ b/tools/lib/bpf/bpf_gen_internal.h
@@ -52,6 +52,8 @@ struct bpf_gen {
 	int fd_array;
 	int nr_fd_array;
 	int hash_insn_offset[SHA256_DWORD_SIZE];
+	struct btf *loader_btf;
+	int loader_btf_func_id;
 };
 
 void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps);
diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
index fee35c26deb8..48ac25c058e3 100644
--- a/tools/lib/bpf/gen_loader.c
+++ b/tools/lib/bpf/gen_loader.c
@@ -35,6 +35,7 @@ struct loader_stack {
 	__u32 btf_fd;
 	__u32 inner_map_fd;
 	__u32 prog_fd[MAX_USED_PROGS];
+	__u64 metadata_hash[SHA256_DWORD_SIZE];
 };
 
 #define stack_off(field) \
@@ -109,7 +110,7 @@ static void emit2(struct bpf_gen *gen, struct bpf_insn insn1, struct bpf_insn in
 
 static int add_data(struct bpf_gen *gen, const void *data, __u32 size);
 static void emit_sys_close_blob(struct bpf_gen *gen, int blob_off);
-static void emit_signature_match(struct bpf_gen *gen);
+static void emit_verify_metadata(struct bpf_gen *gen);
 
 void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps)
 {
@@ -152,8 +153,68 @@ void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps
 	/* R7 contains the error code from sys_bpf. Copy it into R0 and exit. */
 	emit(gen, BPF_MOV64_REG(BPF_REG_0, BPF_REG_7));
 	emit(gen, BPF_EXIT_INSN());
-	if (OPTS_GET(gen->opts, gen_hash, false))
-		emit_signature_match(gen);
+	if (OPTS_GET(gen->opts, gen_hash, false)) {
+		int int_id, u32_id, ptr_id, proto_id, err;
+
+		/* Prog BTF built from primitives so the bytes are reproducible
+		 * across build hosts.
+		 */
+		gen->loader_btf = btf__new_empty();
+		if (libbpf_get_error(gen->loader_btf)) {
+			gen->error = -ENOMEM;
+			gen->loader_btf = NULL;
+			return;
+		}
+		if (gen->swapped_endian) {
+			enum btf_endianness target =
+				btf__endianness(gen->loader_btf) == BTF_LITTLE_ENDIAN
+				? BTF_BIG_ENDIAN : BTF_LITTLE_ENDIAN;
+
+			err = btf__set_endianness(gen->loader_btf, target);
+			if (err) {
+				gen->error = err;
+				return;
+			}
+		}
+		int_id = btf__add_int(gen->loader_btf, "int", 4, BTF_INT_SIGNED);
+		if (int_id < 0) {
+			gen->error = int_id;
+			return;
+		}
+		u32_id = btf__add_int(gen->loader_btf, "u32", 4, 0);
+		if (u32_id < 0) {
+			gen->error = u32_id;
+			return;
+		}
+		ptr_id = btf__add_ptr(gen->loader_btf, 0);
+		if (ptr_id < 0) {
+			gen->error = ptr_id;
+			return;
+		}
+		proto_id = btf__add_func_proto(gen->loader_btf, int_id);
+		if (proto_id < 0) {
+			gen->error = proto_id;
+			return;
+		}
+		err = btf__add_func_param(gen->loader_btf, "map", ptr_id);
+		if (!err)
+			err = btf__add_func_param(gen->loader_btf, "hash", ptr_id);
+		if (!err)
+			err = btf__add_func_param(gen->loader_btf, "hash__sz", u32_id);
+		if (err) {
+			gen->error = err;
+			return;
+		}
+		gen->loader_btf_func_id = btf__add_func(gen->loader_btf,
+							"bpf_loader_verify_metadata",
+							BTF_FUNC_GLOBAL, proto_id);
+		if (gen->loader_btf_func_id < 0) {
+			gen->error = gen->loader_btf_func_id;
+			gen->loader_btf_func_id = 0;
+			return;
+		}
+		emit_verify_metadata(gen);
+	}
 }
 
 static int add_data(struct bpf_gen *gen, const void *data, __u32 size)
@@ -398,7 +459,7 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps)
 			      blob_fd_array_off(gen, i));
 	emit(gen, BPF_MOV64_IMM(BPF_REG_0, 0));
 	emit(gen, BPF_EXIT_INSN());
-	if (OPTS_GET(gen->opts, gen_hash, false))
+	if (!gen->error && OPTS_GET(gen->opts, gen_hash, false))
 		compute_sha_update_offsets(gen);
 
 	pr_debug("gen: finish %s\n", errstr(gen->error));
@@ -410,6 +471,19 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps)
 		opts->data = gen->data_start;
 		opts->data_sz = gen->data_cur - gen->data_start;
 
+		if (gen->loader_btf) {
+			__u32 btf_sz = 0;
+			const void *btf_data;
+
+			btf_data = btf__raw_data(gen->loader_btf, &btf_sz);
+			if (!btf_data) {
+				gen->error = -ENOMEM;
+				return gen->error;
+			}
+			OPTS_SET(opts, btf, btf_data);
+			OPTS_SET(opts, btf_sz, btf_sz);
+		}
+
 		/* use target endianness for embedded loader */
 		if (gen->swapped_endian) {
 			struct bpf_insn *insn = (struct bpf_insn *)opts->insns;
@@ -426,6 +500,7 @@ void bpf_gen__free(struct bpf_gen *gen)
 {
 	if (!gen)
 		return;
+	btf__free(gen->loader_btf);
 	free(gen->data_start);
 	free(gen->insn_start);
 	free(gen);
@@ -580,39 +655,37 @@ void bpf_gen__map_create(struct bpf_gen *gen,
 		emit_sys_close_stack(gen, stack_off(inner_map_fd));
 }
 
-static void emit_signature_match(struct bpf_gen *gen)
+static void emit_verify_metadata(struct bpf_gen *gen)
 {
 	__s64 off;
 	int i;
 
+	/* arg1: metadata struct bpf_map */
+	emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX,
+					 0, 0, 0, 0));
+
+	/* arg2: hash buffer on our BPF stack, populated from ld_imm64
+	 * immediates patched in by compute_sha_update_offsets() before signing.
+	 */
+	emit(gen, BPF_MOV64_REG(BPF_REG_2, BPF_REG_10));
+	emit(gen, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, stack_off(metadata_hash)));
 	for (i = 0; i < SHA256_DWORD_SIZE; i++) {
-		emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX,
-						 0, 0, 0, 0));
-		emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, i * sizeof(__u64)));
 		gen->hash_insn_offset[i] = gen->insn_cur - gen->insn_start;
 		emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_3, 0, 0, 0, 0, 0));
-
-		off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2;
-		if (is_simm16(off)) {
-			emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL));
-			emit(gen, BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, off));
-		} else {
-			gen->error = -ERANGE;
-		}
+		emit(gen, BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3,
+				      i * sizeof(__u64)));
 	}
 
-	/* Reject if the metadata map is not exclusive. Without exclusivity
-	 * the cached map->sha[] verified above can be stale: another BPF
-	 * program with map access could have mutated the contents between
-	 * BPF_OBJ_GET_INFO_BY_FD and loader execution.
-	 */
-	emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX,
-					 0, 0, 0, 0));
-	emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, SHA256_DWORD_SIZE * sizeof(__u64)));
-	off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2;
+	/* arg3: hash length */
+	emit(gen, BPF_MOV64_IMM(BPF_REG_3, SHA256_DWORD_SIZE * sizeof(__u64)));
+
+	emit(gen, BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0,
+			       BPF_PSEUDO_KFUNC_CALL_PROG_BTF, 0,
+			       gen->loader_btf_func_id));
+	emit(gen, BPF_MOV64_REG(BPF_REG_7, BPF_REG_0));
+	off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 1;
 	if (is_simm16(off)) {
-		emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL));
-		emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_2, 0, off));
+		emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, off));
 	} else {
 		gen->error = -ERANGE;
 	}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index bba4e8464396..25906fb695f9 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -1899,9 +1899,11 @@ struct gen_loader_opts {
 	__u32 data_sz;
 	__u32 insns_sz;
 	bool gen_hash;
+	const void *btf;
+	__u32 btf_sz;
 };
 
-#define gen_loader_opts__last_field gen_hash
+#define gen_loader_opts__last_field btf_sz
 LIBBPF_API int bpf_object__gen_loader(struct bpf_object *obj,
 				      struct gen_loader_opts *opts);
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH bpf-next 08/13] bpftool gen: embed loader prog BTF in the lskel header
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: ast, daniel, memxor, James.Bottomley, paul, KP Singh
In-Reply-To: <20260522023234.3778588-1-kpsingh@kernel.org>

Extend the signed lskel codegen to include the loader prog BTF in
the generated header and the PKCS#7 signature scope. The loader
needs this BTF at load time so the kernel can resolve kfunc calls
by name.

Lskels generated without -S are unchanged; the new codegen is gated
on opts.btf_sz.

Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 tools/bpf/bpftool/gen.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
index 2f9e10752e28..95dba5c53ef4 100644
--- a/tools/bpf/bpftool/gen.c
+++ b/tools/bpf/bpftool/gen.c
@@ -793,6 +793,8 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
 	if (sign_progs) {
 		sopts.insns = opts.insns;
 		sopts.insns_sz = opts.insns_sz;
+		sopts.btf = opts.btf;
+		sopts.btf_sz = opts.btf_sz;
 		sopts.excl_prog_hash = prog_sha;
 		sopts.excl_prog_hash_sz = sizeof(prog_sha);
 		sopts.signature = sig_buf;
@@ -822,6 +824,17 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
 		\n\
 		\";\n");
 
+		if (opts.btf_sz) {
+			codegen("\
+			\n\
+				static const char opts_btf[] __attribute__((__aligned__(8))) = \"\\\n\
+			");
+			print_hex(opts.btf, opts.btf_sz);
+			codegen("\
+			\n\
+			\";\n");
+		}
+
 		codegen("\
 		\n\
 			opts.signature = (void *)opts_sig;			\n\
@@ -830,6 +843,14 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
 			opts.excl_prog_hash_sz = sizeof(opts_excl_hash) - 1;	\n\
 			opts.keyring_id = skel->keyring_id;			\n\
 		");
+
+		if (opts.btf_sz) {
+			codegen("\
+			\n\
+				opts.btf = (void *)opts_btf;			    \n\
+				opts.btf_sz = sizeof(opts_btf) - 1;		    \n\
+			");
+		}
 	}
 
 	codegen("\
-- 
2.53.0


^ permalink raw reply related

* [PATCH bpf-next 09/13] lsm: add bpf_prog_load_post_integrity hook
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: ast, daniel, memxor, James.Bottomley, paul, KP Singh
In-Reply-To: <20260522023234.3778588-1-kpsingh@kernel.org>

Add a companion to security_bpf_prog_load. The existing hook fires
at PROG_LOAD entry where the verdict is at most BPF_SIG_OK; the new
hook fires from bpf_loader_verify_metadata after the in-kernel
metadata check, just before sig.verdict is promoted to
BPF_SIG_METADATA_VERIFIED. Policy LSMs that want to gate on
metadata verification (not just signature presence) register here.

Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 include/linux/lsm_hook_defs.h |  1 +
 include/linux/security.h      |  6 ++++++
 security/security.c           | 17 +++++++++++++++++
 3 files changed, 24 insertions(+)

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 2b8dfb35caed..c0e7899756d4 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -446,6 +446,7 @@ LSM_HOOK(int, 0, bpf_map_create, struct bpf_map *map, union bpf_attr *attr,
 LSM_HOOK(void, LSM_RET_VOID, bpf_map_free, struct bpf_map *map)
 LSM_HOOK(int, 0, bpf_prog_load, struct bpf_prog *prog, union bpf_attr *attr,
 	 struct bpf_token *token, bool kernel)
+LSM_HOOK(int, 0, bpf_prog_load_post_integrity, struct bpf_prog *prog)
 LSM_HOOK(void, LSM_RET_VOID, bpf_prog_free, struct bpf_prog *prog)
 LSM_HOOK(int, 0, bpf_token_create, struct bpf_token *token, union bpf_attr *attr,
 	 const struct path *path)
diff --git a/include/linux/security.h b/include/linux/security.h
index 41d7367cf403..3a8f2c50f7be 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -2305,6 +2305,7 @@ extern int security_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
 extern void security_bpf_map_free(struct bpf_map *map);
 extern int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
 				  struct bpf_token *token, bool kernel);
+extern int security_bpf_prog_load_post_integrity(struct bpf_prog *prog);
 extern void security_bpf_prog_free(struct bpf_prog *prog);
 extern int security_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
 				     const struct path *path);
@@ -2343,6 +2344,11 @@ static inline int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *
 	return 0;
 }
 
+static inline int security_bpf_prog_load_post_integrity(struct bpf_prog *prog)
+{
+	return 0;
+}
+
 static inline void security_bpf_prog_free(struct bpf_prog *prog)
 { }
 
diff --git a/security/security.c b/security/security.c
index 4e999f023651..05153e8496c9 100644
--- a/security/security.c
+++ b/security/security.c
@@ -5383,6 +5383,23 @@ int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
 	return rc;
 }
 
+/**
+ * security_bpf_prog_load_post_integrity() - Notify LSMs that a signed loader
+ * has just verified its metadata map.
+ * @prog: the loader BPF program whose metadata check passed.
+ *
+ * Invoked by bpf_loader_verify_metadata() after the kernel-side hash check
+ * succeeds, before prog->aux->sig_verdict is promoted to
+ * BPF_SIG_METADATA_VERIFIED. A non-zero return aborts the kfunc and leaves
+ * the verdict at BPF_SIG_OK.
+ *
+ * Return: 0 on success, negative errno to deny.
+ */
+int security_bpf_prog_load_post_integrity(struct bpf_prog *prog)
+{
+	return call_int_hook(bpf_prog_load_post_integrity, prog);
+}
+
 /**
  * security_bpf_token_create() - Check if creating of BPF token is allowed
  * @token: BPF token object
-- 
2.53.0


^ permalink raw reply related

* [PATCH bpf-next 10/13] bpf: invoke security_bpf_prog_load_post_integrity from the metadata kfunc
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: ast, daniel, memxor, James.Bottomley, paul, KP Singh
In-Reply-To: <20260522023234.3778588-1-kpsingh@kernel.org>

Call security_bpf_prog_load_post_integrity from
bpf_loader_verify_metadata just before promoting
prog->aux->sig.verdict from BPF_SIG_OK to BPF_SIG_METADATA_VERIFIED.
This lets policy LSMs deny the metadata-verified transition.

A non-zero return aborts the kfunc and leaves the verdict at
BPF_SIG_OK; observers that key off METADATA_VERIFIED never see a
verdict the LSM denied.

Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 kernel/bpf/helpers.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 9afa71fbcac3..52e71fb6e200 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -4300,6 +4300,14 @@ __bpf_kfunc int bpf_loader_verify_metadata(struct bpf_map *map,
 	if (memcmp(sha, hash, SHA256_DIGEST_SIZE))
 		return -EBADMSG;
 
+	/* Metadata integrity is decided by the checks above; the LSM hook
+	 * is an observer of that verdict and may apply policy (e.g. deny),
+	 * but cannot vouch for integrity it did not verify itself.
+	 */
+	err = security_bpf_prog_load_post_integrity(aux__ign->prog);
+	if (err)
+		return err;
+
 	aux__ign->sig.verdict = BPF_SIG_METADATA_VERIFIED;
 	return 0;
 }
-- 
2.53.0


^ permalink raw reply related

* [PATCH bpf-next 11/13] ipe: add BPF program signature properties
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: ast, daniel, memxor, James.Bottomley, paul, KP Singh
In-Reply-To: <20260522023234.3778588-1-kpsingh@kernel.org>

Wire IPE to the BPF signing verdict stored in prog->aux->sig before
security_bpf_prog_load fires. Add three properties on a new IPE op
BPF_PROG_LOAD:

  bpf_signature= UNSIGNED | OK | METADATA_VERIFIED
  bpf_keyring=   BUILTIN | SECONDARY | PLATFORM | USER
  bpf_kernel=    TRUE | FALSE

Example policy:

  op=BPF_PROG_LOAD bpf_signature=UNSIGNED action=DENY
  op=BPF_PROG_LOAD bpf_signature=OK action=ALLOW

Gated by CONFIG_IPE_PROP_BPF_SIGNATURE (depends on BPF_SYSCALL).

Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 security/ipe/Kconfig         | 14 +++++++++
 security/ipe/audit.c         | 11 +++++++
 security/ipe/eval.c          | 57 ++++++++++++++++++++++++++++++++++++
 security/ipe/eval.h          |  5 ++++
 security/ipe/hooks.c         | 36 +++++++++++++++++++++++
 security/ipe/hooks.h         |  7 +++++
 security/ipe/ipe.c           |  3 ++
 security/ipe/policy.h        | 10 +++++++
 security/ipe/policy_parser.c | 19 ++++++++++++
 9 files changed, 162 insertions(+)

diff --git a/security/ipe/Kconfig b/security/ipe/Kconfig
index a110a6cd848b..204517c60a34 100644
--- a/security/ipe/Kconfig
+++ b/security/ipe/Kconfig
@@ -13,6 +13,7 @@ menuconfig SECURITY_IPE
 	select IPE_PROP_DM_VERITY_SIGNATURE if DM_VERITY && DM_VERITY_VERIFY_ROOTHASH_SIG
 	select IPE_PROP_FS_VERITY if FS_VERITY
 	select IPE_PROP_FS_VERITY_BUILTIN_SIG if FS_VERITY && FS_VERITY_BUILTIN_SIGNATURES
+	select IPE_PROP_BPF_SIGNATURE if BPF_SYSCALL
 	help
 	  This option enables the Integrity Policy Enforcement LSM
 	  allowing users to define a policy to enforce a trust-based access
@@ -95,6 +96,19 @@ config IPE_PROP_FS_VERITY_BUILTIN_SIG
 
 	  if unsure, answer Y.
 
+config IPE_PROP_BPF_SIGNATURE
+	bool "Enable support for BPF program signature verdicts"
+	depends on BPF_SYSCALL
+	help
+	  This option enables the 'bpf_signature', 'bpf_keyring' and
+	  'bpf_kernel' properties within IPE policies. The properties
+	  These allow
+	  policy rules to gate BPF program loads based on the loader's
+	  signature verdict, the keyring used for verification, and
+	  whether the load originated in kernel mode.
+
+	  if unsure, answer Y.
+
 endmenu
 
 config SECURITY_IPE_KUNIT_TEST
diff --git a/security/ipe/audit.c b/security/ipe/audit.c
index 93fb59fbddd6..fec98c396d49 100644
--- a/security/ipe/audit.c
+++ b/security/ipe/audit.c
@@ -41,6 +41,7 @@ static const char *const audit_op_names[__IPE_OP_MAX + 1] = {
 	"KEXEC_INITRAMFS",
 	"POLICY",
 	"X509_CERT",
+	"BPF_PROG_LOAD",
 	"UNKNOWN",
 };
 
@@ -51,6 +52,7 @@ static const char *const audit_hook_names[__IPE_HOOK_MAX] = {
 	"MPROTECT",
 	"KERNEL_READ",
 	"KERNEL_LOAD",
+	"BPF_PROG_LOAD",
 };
 
 static const char *const audit_prop_names[__IPE_PROP_MAX] = {
@@ -62,6 +64,15 @@ static const char *const audit_prop_names[__IPE_PROP_MAX] = {
 	"fsverity_digest=",
 	"fsverity_signature=FALSE",
 	"fsverity_signature=TRUE",
+	"bpf_signature=UNSIGNED",
+	"bpf_signature=OK",
+	"bpf_signature=METADATA_VERIFIED",
+	"bpf_keyring=BUILTIN",
+	"bpf_keyring=SECONDARY",
+	"bpf_keyring=PLATFORM",
+	"bpf_keyring=USER",
+	"bpf_kernel=FALSE",
+	"bpf_kernel=TRUE",
 };
 
 /**
diff --git a/security/ipe/eval.c b/security/ipe/eval.c
index 21439c5be336..e4b4e3723fc8 100644
--- a/security/ipe/eval.c
+++ b/security/ipe/eval.c
@@ -11,6 +11,7 @@
 #include <linux/rcupdate.h>
 #include <linux/moduleparam.h>
 #include <linux/fsverity.h>
+#include <linux/bpf.h>
 
 #include "ipe.h"
 #include "eval.h"
@@ -265,6 +266,44 @@ static bool evaluate_fsv_sig_true(const struct ipe_eval_ctx *const ctx)
 }
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
 
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+static bool evaluate_bpf_verdict(const struct ipe_eval_ctx *const ctx,
+				 enum bpf_sig_verdict expected)
+{
+	return ctx->bpf_verdict == expected;
+}
+
+static bool evaluate_bpf_keyring(const struct ipe_eval_ctx *const ctx,
+				 enum bpf_sig_keyring expected)
+{
+	return ctx->bpf_keyring == expected;
+}
+
+static bool evaluate_bpf_kernel(const struct ipe_eval_ctx *const ctx,
+				bool expected)
+{
+	return ctx->bpf_kernel == expected;
+}
+#else
+static bool evaluate_bpf_verdict(const struct ipe_eval_ctx *const ctx,
+				 enum bpf_sig_verdict expected)
+{
+	return false;
+}
+
+static bool evaluate_bpf_keyring(const struct ipe_eval_ctx *const ctx,
+				 enum bpf_sig_keyring expected)
+{
+	return false;
+}
+
+static bool evaluate_bpf_kernel(const struct ipe_eval_ctx *const ctx,
+				bool expected)
+{
+	return false;
+}
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
+
 /**
  * evaluate_property() - Analyze @ctx against a rule property.
  * @ctx: Supplies a pointer to the context to be evaluated.
@@ -297,6 +336,24 @@ static bool evaluate_property(const struct ipe_eval_ctx *const ctx,
 		return evaluate_fsv_sig_false(ctx);
 	case IPE_PROP_FSV_SIG_TRUE:
 		return evaluate_fsv_sig_true(ctx);
+	case IPE_PROP_BPF_SIG_UNSIGNED:
+		return evaluate_bpf_verdict(ctx, BPF_SIG_UNSIGNED);
+	case IPE_PROP_BPF_SIG_OK:
+		return evaluate_bpf_verdict(ctx, BPF_SIG_OK);
+	case IPE_PROP_BPF_SIG_METADATA_VERIFIED:
+		return evaluate_bpf_verdict(ctx, BPF_SIG_METADATA_VERIFIED);
+	case IPE_PROP_BPF_KEYRING_BUILTIN:
+		return evaluate_bpf_keyring(ctx, BPF_SIG_KEYRING_BUILTIN);
+	case IPE_PROP_BPF_KEYRING_SECONDARY:
+		return evaluate_bpf_keyring(ctx, BPF_SIG_KEYRING_SECONDARY);
+	case IPE_PROP_BPF_KEYRING_PLATFORM:
+		return evaluate_bpf_keyring(ctx, BPF_SIG_KEYRING_PLATFORM);
+	case IPE_PROP_BPF_KEYRING_USER:
+		return evaluate_bpf_keyring(ctx, BPF_SIG_KEYRING_USER);
+	case IPE_PROP_BPF_KERNEL_FALSE:
+		return evaluate_bpf_kernel(ctx, false);
+	case IPE_PROP_BPF_KERNEL_TRUE:
+		return evaluate_bpf_kernel(ctx, true);
 	default:
 		return false;
 	}
diff --git a/security/ipe/eval.h b/security/ipe/eval.h
index fef65a36468c..2ddf89695818 100644
--- a/security/ipe/eval.h
+++ b/security/ipe/eval.h
@@ -52,6 +52,11 @@ struct ipe_eval_ctx {
 #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
 	const struct ipe_inode *ipe_inode;
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+	u8 bpf_verdict;		/* enum bpf_sig_verdict */
+	u8 bpf_keyring;		/* enum bpf_sig_keyring */
+	bool bpf_kernel;
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
 };
 
 enum ipe_match {
diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c
index 0ae54a880405..bdc1b634bb08 100644
--- a/security/ipe/hooks.c
+++ b/security/ipe/hooks.c
@@ -312,6 +312,42 @@ int ipe_bdev_setintegrity(struct block_device *bdev, enum lsm_integrity_type typ
 }
 #endif /* CONFIG_IPE_PROP_DM_VERITY */
 
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+/**
+ * ipe_bpf_prog_load() - IPE hook for BPF program load.
+ * @prog: The BPF program being loaded.
+ * @attr: BPF syscall attributes.
+ * @token: BPF token.
+ * @kernel: Whether the load originated in kernel mode.
+ *
+ * Return: %0 on success, %-EACCES if denied by policy.
+ */
+int ipe_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
+		      struct bpf_token *token, bool kernel)
+{
+	struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
+
+	ipe_build_eval_ctx(&ctx, NULL, IPE_OP_BPF_PROG_LOAD,
+			   IPE_HOOK_BPF_PROG_LOAD);
+	ctx.bpf_verdict = prog->aux->sig.verdict;
+	ctx.bpf_keyring = prog->aux->sig.keyring;
+	ctx.bpf_kernel = kernel;
+	return ipe_evaluate_event(&ctx);
+}
+
+int ipe_bpf_prog_load_post_integrity(struct bpf_prog *prog)
+{
+	struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
+
+	ipe_build_eval_ctx(&ctx, NULL, IPE_OP_BPF_PROG_LOAD_POST_INTEGRITY,
+			   IPE_HOOK_BPF_PROG_LOAD_POST_INTEGRITY);
+	ctx.bpf_verdict = BPF_SIG_METADATA_VERIFIED;
+	ctx.bpf_keyring = prog->aux->sig.keyring;
+	ctx.bpf_kernel = prog->aux->is_kernel;
+	return ipe_evaluate_event(&ctx);
+}
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
+
 #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
 /**
  * ipe_inode_setintegrity() - save integrity data from a inode to IPE's LSM blob.
diff --git a/security/ipe/hooks.h b/security/ipe/hooks.h
index 07db37332740..abdedd436aa8 100644
--- a/security/ipe/hooks.h
+++ b/security/ipe/hooks.h
@@ -10,6 +10,7 @@
 #include <linux/security.h>
 #include <linux/blk_types.h>
 #include <linux/fsverity.h>
+#include <linux/bpf.h>
 
 enum ipe_hook_type {
 	IPE_HOOK_BPRM_CHECK = 0,
@@ -18,6 +19,7 @@ enum ipe_hook_type {
 	IPE_HOOK_MPROTECT,
 	IPE_HOOK_KERNEL_READ,
 	IPE_HOOK_KERNEL_LOAD,
+	IPE_HOOK_BPF_PROG_LOAD,
 	__IPE_HOOK_MAX
 };
 
@@ -52,4 +54,9 @@ int ipe_inode_setintegrity(const struct inode *inode, enum lsm_integrity_type ty
 			   const void *value, size_t size);
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
 
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+int ipe_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
+		      struct bpf_token *token, bool kernel);
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
+
 #endif /* _IPE_HOOKS_H */
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
index 495bb765de1b..17ace9236253 100644
--- a/security/ipe/ipe.c
+++ b/security/ipe/ipe.c
@@ -60,6 +60,9 @@ static struct security_hook_list ipe_hooks[] __ro_after_init = {
 #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
 	LSM_HOOK_INIT(inode_setintegrity, ipe_inode_setintegrity),
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
+	LSM_HOOK_INIT(bpf_prog_load, ipe_bpf_prog_load),
+#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
 };
 
 /**
diff --git a/security/ipe/policy.h b/security/ipe/policy.h
index 5bfbdbddeef8..eb066750a48b 100644
--- a/security/ipe/policy.h
+++ b/security/ipe/policy.h
@@ -17,6 +17,7 @@ enum ipe_op_type {
 	IPE_OP_KEXEC_INITRAMFS,
 	IPE_OP_POLICY,
 	IPE_OP_X509,
+	IPE_OP_BPF_PROG_LOAD,
 	__IPE_OP_MAX,
 };
 
@@ -39,6 +40,15 @@ enum ipe_prop_type {
 	IPE_PROP_FSV_DIGEST,
 	IPE_PROP_FSV_SIG_FALSE,
 	IPE_PROP_FSV_SIG_TRUE,
+	IPE_PROP_BPF_SIG_UNSIGNED,
+	IPE_PROP_BPF_SIG_OK,
+	IPE_PROP_BPF_SIG_METADATA_VERIFIED,
+	IPE_PROP_BPF_KEYRING_BUILTIN,
+	IPE_PROP_BPF_KEYRING_SECONDARY,
+	IPE_PROP_BPF_KEYRING_PLATFORM,
+	IPE_PROP_BPF_KEYRING_USER,
+	IPE_PROP_BPF_KERNEL_FALSE,
+	IPE_PROP_BPF_KERNEL_TRUE,
 	__IPE_PROP_MAX
 };
 
diff --git a/security/ipe/policy_parser.c b/security/ipe/policy_parser.c
index 6fa5bebf8471..c1e374d2ec34 100644
--- a/security/ipe/policy_parser.c
+++ b/security/ipe/policy_parser.c
@@ -237,6 +237,7 @@ static const match_table_t operation_tokens = {
 	{IPE_OP_KEXEC_INITRAMFS,	"op=KEXEC_INITRAMFS"},
 	{IPE_OP_POLICY,			"op=POLICY"},
 	{IPE_OP_X509,			"op=X509_CERT"},
+	{IPE_OP_BPF_PROG_LOAD,		"op=BPF_PROG_LOAD"},
 	{IPE_OP_INVALID,		NULL}
 };
 
@@ -281,6 +282,15 @@ static const match_table_t property_tokens = {
 	{IPE_PROP_FSV_DIGEST,		"fsverity_digest=%s"},
 	{IPE_PROP_FSV_SIG_FALSE,	"fsverity_signature=FALSE"},
 	{IPE_PROP_FSV_SIG_TRUE,		"fsverity_signature=TRUE"},
+	{IPE_PROP_BPF_SIG_UNSIGNED,	"bpf_signature=UNSIGNED"},
+	{IPE_PROP_BPF_SIG_OK,		"bpf_signature=OK"},
+	{IPE_PROP_BPF_SIG_METADATA_VERIFIED, "bpf_signature=METADATA_VERIFIED"},
+	{IPE_PROP_BPF_KEYRING_BUILTIN,	"bpf_keyring=BUILTIN"},
+	{IPE_PROP_BPF_KEYRING_SECONDARY, "bpf_keyring=SECONDARY"},
+	{IPE_PROP_BPF_KEYRING_PLATFORM,	"bpf_keyring=PLATFORM"},
+	{IPE_PROP_BPF_KEYRING_USER,	"bpf_keyring=USER"},
+	{IPE_PROP_BPF_KERNEL_FALSE,	"bpf_kernel=FALSE"},
+	{IPE_PROP_BPF_KERNEL_TRUE,	"bpf_kernel=TRUE"},
 	{IPE_PROP_INVALID,		NULL}
 };
 
@@ -331,6 +341,15 @@ static int parse_property(char *t, struct ipe_rule *r)
 	case IPE_PROP_DMV_SIG_TRUE:
 	case IPE_PROP_FSV_SIG_FALSE:
 	case IPE_PROP_FSV_SIG_TRUE:
+	case IPE_PROP_BPF_SIG_UNSIGNED:
+	case IPE_PROP_BPF_SIG_OK:
+	case IPE_PROP_BPF_SIG_METADATA_VERIFIED:
+	case IPE_PROP_BPF_KEYRING_BUILTIN:
+	case IPE_PROP_BPF_KEYRING_SECONDARY:
+	case IPE_PROP_BPF_KEYRING_PLATFORM:
+	case IPE_PROP_BPF_KEYRING_USER:
+	case IPE_PROP_BPF_KERNEL_FALSE:
+	case IPE_PROP_BPF_KERNEL_TRUE:
 		p->type = token;
 		break;
 	default:
-- 
2.53.0


^ permalink raw reply related

* [PATCH bpf-next 12/13] ipe: gate post-integrity BPF program loads
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: ast, daniel, memxor, James.Bottomley, paul, KP Singh
In-Reply-To: <20260522023234.3778588-1-kpsingh@kernel.org>

Register on security_bpf_prog_load_post_integrity and expose a new
IPE op BPF_PROG_LOAD_POST_INTEGRITY. Kept distinct from
BPF_PROG_LOAD so policies need not reason about the same rule
firing at two timings with different verdict states.

Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 security/ipe/audit.c         | 2 ++
 security/ipe/hooks.c         | 6 ++++++
 security/ipe/hooks.h         | 2 ++
 security/ipe/ipe.c           | 1 +
 security/ipe/policy.h        | 1 +
 security/ipe/policy_parser.c | 1 +
 6 files changed, 13 insertions(+)

diff --git a/security/ipe/audit.c b/security/ipe/audit.c
index fec98c396d49..bcb3e6c0a310 100644
--- a/security/ipe/audit.c
+++ b/security/ipe/audit.c
@@ -42,6 +42,7 @@ static const char *const audit_op_names[__IPE_OP_MAX + 1] = {
 	"POLICY",
 	"X509_CERT",
 	"BPF_PROG_LOAD",
+	"BPF_PROG_LOAD_POST_INTEGRITY",
 	"UNKNOWN",
 };
 
@@ -53,6 +54,7 @@ static const char *const audit_hook_names[__IPE_HOOK_MAX] = {
 	"KERNEL_READ",
 	"KERNEL_LOAD",
 	"BPF_PROG_LOAD",
+	"BPF_PROG_LOAD_POST_INTEGRITY",
 };
 
 static const char *const audit_prop_names[__IPE_PROP_MAX] = {
diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c
index bdc1b634bb08..3f6e260a8787 100644
--- a/security/ipe/hooks.c
+++ b/security/ipe/hooks.c
@@ -335,6 +335,12 @@ int ipe_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
 	return ipe_evaluate_event(&ctx);
 }
 
+/**
+ * ipe_bpf_prog_load_post_integrity() - IPE hook for post-integrity verdict.
+ * @prog: The loader BPF program.
+ *
+ * Return: %0 on success, %-EACCES if denied by policy.
+ */
 int ipe_bpf_prog_load_post_integrity(struct bpf_prog *prog)
 {
 	struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
diff --git a/security/ipe/hooks.h b/security/ipe/hooks.h
index abdedd436aa8..bd24067705ea 100644
--- a/security/ipe/hooks.h
+++ b/security/ipe/hooks.h
@@ -20,6 +20,7 @@ enum ipe_hook_type {
 	IPE_HOOK_KERNEL_READ,
 	IPE_HOOK_KERNEL_LOAD,
 	IPE_HOOK_BPF_PROG_LOAD,
+	IPE_HOOK_BPF_PROG_LOAD_POST_INTEGRITY,
 	__IPE_HOOK_MAX
 };
 
@@ -57,6 +58,7 @@ int ipe_inode_setintegrity(const struct inode *inode, enum lsm_integrity_type ty
 #ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
 int ipe_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
 		      struct bpf_token *token, bool kernel);
+int ipe_bpf_prog_load_post_integrity(struct bpf_prog *prog);
 #endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
 
 #endif /* _IPE_HOOKS_H */
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
index 17ace9236253..d5e6f339639a 100644
--- a/security/ipe/ipe.c
+++ b/security/ipe/ipe.c
@@ -62,6 +62,7 @@ static struct security_hook_list ipe_hooks[] __ro_after_init = {
 #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
 #ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
 	LSM_HOOK_INIT(bpf_prog_load, ipe_bpf_prog_load),
+	LSM_HOOK_INIT(bpf_prog_load_post_integrity, ipe_bpf_prog_load_post_integrity),
 #endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
 };
 
diff --git a/security/ipe/policy.h b/security/ipe/policy.h
index eb066750a48b..84b3e69e618d 100644
--- a/security/ipe/policy.h
+++ b/security/ipe/policy.h
@@ -18,6 +18,7 @@ enum ipe_op_type {
 	IPE_OP_POLICY,
 	IPE_OP_X509,
 	IPE_OP_BPF_PROG_LOAD,
+	IPE_OP_BPF_PROG_LOAD_POST_INTEGRITY,
 	__IPE_OP_MAX,
 };
 
diff --git a/security/ipe/policy_parser.c b/security/ipe/policy_parser.c
index c1e374d2ec34..350cc93a1af1 100644
--- a/security/ipe/policy_parser.c
+++ b/security/ipe/policy_parser.c
@@ -238,6 +238,7 @@ static const match_table_t operation_tokens = {
 	{IPE_OP_POLICY,			"op=POLICY"},
 	{IPE_OP_X509,			"op=X509_CERT"},
 	{IPE_OP_BPF_PROG_LOAD,		"op=BPF_PROG_LOAD"},
+	{IPE_OP_BPF_PROG_LOAD_POST_INTEGRITY, "op=BPF_PROG_LOAD_POST_INTEGRITY"},
 	{IPE_OP_INVALID,		NULL}
 };
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH bpf-next 13/13] selftests/bpf: add IPE BPF policy integration tests
From: KP Singh @ 2026-05-22  2:32 UTC (permalink / raw)
  To: linux-security-module, bpf
  Cc: ast, daniel, memxor, James.Bottomley, paul, KP Singh
In-Reply-To: <20260522023234.3778588-1-kpsingh@kernel.org>

Smoke test for the IPE BPF signing properties and ops added in the
preceding patches. The test primes the kernel config with IPE
knobs, writes a boot policy, and boots vmtest.sh to verify that
signed lskels load, unsigned loads are denied, and the policy
audit lines match.

Manual only for now, not wired into the selftests Makefile.

Signed-off-by: KP Singh <kpsingh@kernel.org>
---
 .../selftests/bpf/test_signed_bpf_ipe.sh      | 156 ++++++++++++++++++
 tools/testing/selftests/bpf/vmtest.sh         |   4 +-
 2 files changed, 158 insertions(+), 2 deletions(-)
 create mode 100755 tools/testing/selftests/bpf/test_signed_bpf_ipe.sh

diff --git a/tools/testing/selftests/bpf/test_signed_bpf_ipe.sh b/tools/testing/selftests/bpf/test_signed_bpf_ipe.sh
new file mode 100755
index 000000000000..aaa259ddb917
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_signed_bpf_ipe.sh
@@ -0,0 +1,156 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# IPE BPF policy integration tests.
+
+if [ "${1:-}" = "--in-vm" ]; then
+	set -u
+	fail=0
+	record() {
+		id=$1; verdict=$2; shift 2
+		printf '%s %s %s\n' "$verdict" "$id" "$*"
+		[ "$verdict" = FAIL ] && fail=$((fail + 1))
+		return 0
+	}
+
+	mountpoint -q /sys/kernel/security 2>/dev/null \
+		|| mount -t securityfs none /sys/kernel/security 2>/dev/null || true
+	if [ -d /sys/kernel/security/ipe ]; then
+		record T0 PASS "securityfs/ipe present"
+	elif grep -qw ipe /sys/kernel/security/lsm 2>/dev/null; then
+		record T0 PASS "ipe in /sys/kernel/security/lsm"
+	elif [ -r /sys/module/ipe/parameters/enforce ]; then
+		record T0 PASS "ipe module parameters present"
+	else
+		record T0 FAIL "no sign that IPE LSM is loaded"
+	fi
+
+	if ./test_progs -t atomics 2>&1 | grep -qE '^#[0-9]+[[:space:]]+atomics:OK$'; then
+		record T1 PASS "atomics signed lskel loaded"
+	else
+		record T1 FAIL "atomics signed lskel did not load"
+	fi
+
+	if dmesg | grep -q 'ipe_op=BPF_PROG_LOAD ipe_hook=BPF_PROG_LOAD'; then
+		record T2 PASS "ipe_op=BPF_PROG_LOAD audit line found"
+	else
+		record T2 FAIL "no ipe_op=BPF_PROG_LOAD audit lines in dmesg"
+	fi
+
+	if dmesg | grep 'ipe_op=BPF_PROG_LOAD' \
+		| grep -q 'bpf_signature=UNSIGNED action=DENY'; then
+		record T3 PASS "deny-UNSIGNED rule matched"
+	else
+		record T3 FAIL "deny-UNSIGNED rule never matched"
+	fi
+
+	mountpoint -q /sys/kernel/security 2>/dev/null \
+		|| mount -t securityfs none /sys/kernel/security 2>/dev/null || true
+	content=
+	for d in /sys/kernel/security/ipe/policies/*/; do
+		[ -d "$d" ] || continue
+		if [ -r "$d/policy" ]; then
+			content=$(cat "$d/policy" 2>/dev/null || true)
+			break
+		fi
+	done
+	if [ -z "$content" ]; then
+		record T5 SKIP "could not read /sys/kernel/security/ipe/policies/*/policy"
+	elif echo "$content" | grep -q '^op=BPF_PROG_LOAD_POST_INTEGRITY'; then
+		record T5 PASS "post-integrity rule present in active policy"
+	else
+		record T5 FAIL "active policy does not contain a BPF_PROG_LOAD_POST_INTEGRITY rule"
+	fi
+
+	ENFORCE=/sys/kernel/security/ipe/enforce
+	mountpoint -q /sys/kernel/security 2>/dev/null \
+		|| mount -t securityfs none /sys/kernel/security 2>/dev/null || true
+
+	if [ ! -w "$ENFORCE" ]; then
+		record T4 SKIP "$ENFORCE not writable"
+	elif ! echo 1 > "$ENFORCE" 2>/dev/null; then
+		record T4 SKIP "could not toggle IPE to enforce=1"
+	else
+		out=$(./test_progs -t bind_perm 2>&1 || true)
+		if echo "$out" | grep -q -- '-EACCES\|errno 13'; then
+			record T4 PASS "unsigned load denied with -EACCES under enforce=1"
+		else
+			record T4 FAIL "no -EACCES seen from unsigned load under enforce=1"
+			echo "$out" | tail -10 | sed 's/^/  T4: /'
+		fi
+		echo 0 > "$ENFORCE" 2>/dev/null || true
+	fi
+
+	if [ "$fail" -eq 0 ]; then
+		echo ALL_TESTS_PASSED
+	else
+		echo FAIL_COUNT "$fail"
+	fi
+	exit 0
+fi
+
+set -euo pipefail
+
+SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+KERNEL_DIR=$(cd "$SCRIPT_DIR/../../../.." && pwd)
+VMTEST="$SCRIPT_DIR/vmtest.sh"
+
+POLICY_PATH=${IPE_TEST_POLICY:-/tmp/ipe_bpf_test_policy}
+
+red()   { printf '\033[31m%s\033[0m\n' "$*"; }
+green() { printf '\033[32m%s\033[0m\n' "$*"; }
+bold()  { printf '\033[1m%s\033[0m\n' "$*"; }
+
+bold "=== IPE BPF policy integration tests ==="
+
+tmpfile=$(mktemp -p "$(dirname "$POLICY_PATH")" \
+		  ".$(basename "$POLICY_PATH").XXXXXX")
+cat > "$tmpfile" <<EOF
+policy_name="ipe_bpf_test" policy_version=0.0.1
+op=BPF_PROG_LOAD bpf_signature=OK                action=ALLOW
+op=BPF_PROG_LOAD bpf_signature=UNSIGNED          action=DENY
+op=BPF_PROG_LOAD_POST_INTEGRITY                  action=ALLOW
+DEFAULT op=BPF_PROG_LOAD                         action=ALLOW
+DEFAULT op=BPF_PROG_LOAD_POST_INTEGRITY          action=ALLOW
+DEFAULT action=ALLOW
+EOF
+mv "$tmpfile" "$POLICY_PATH"
+echo "policy written to $POLICY_PATH"
+
+export OUTPUT_DIR=${OUTPUT_DIR:-$HOME/.bpf_selftests_ipe}
+CFG="$OUTPUT_DIR/latest.config"
+mkdir -p "$OUTPUT_DIR"
+if [ ! -f "$CFG" ]; then
+	PLATFORM=$(uname -m)
+	for src in "$KERNEL_DIR/tools/testing/selftests/bpf/config" \
+		   "$KERNEL_DIR/tools/testing/selftests/bpf/config.vm" \
+		   "$KERNEL_DIR/tools/testing/selftests/bpf/config.$PLATFORM"; do
+		[ -f "$src" ] && cat "$src" >> "$CFG"
+	done
+fi
+"$KERNEL_DIR/scripts/config" --file "$CFG" \
+	--enable SECURITY_IPE \
+	--enable IPE_PROP_BPF_SIGNATURE \
+	--set-str IPE_BOOT_POLICY "$POLICY_PATH"
+lsm=$("$KERNEL_DIR/scripts/config" --file "$CFG" --state LSM 2>/dev/null)
+if [ -z "$lsm" ] || [ "$lsm" = "undef" ]; then
+	"$KERNEL_DIR/scripts/config" --file "$CFG" --set-str LSM "bpf,ipe"
+elif ! echo ",${lsm}," | grep -q ',ipe,'; then
+	"$KERNEL_DIR/scripts/config" --file "$CFG" --set-str LSM "${lsm},ipe"
+fi
+touch "$CFG"
+
+RUN_OUT=$(mktemp -t ipe_test_run.XXXXXX)
+trap 'rm -f "$RUN_OUT"' EXIT
+export VMTEST_EXTRA_CMDLINE="ipe.enforce=0 ipe.success_audit=1"
+"$VMTEST" -- ./test_signed_bpf_ipe.sh --in-vm 2>&1 | tee "$RUN_OUT"
+
+if grep -q 'ALL_TESTS_PASSED' "$RUN_OUT"; then
+	green "=== all IPE policy integration tests PASSED ==="
+	grep -E '^(PASS|FAIL|SKIP) T[0-9]+' "$RUN_OUT" || true
+	exit 0
+else
+	red "=== IPE policy integration tests FAILED ==="
+	grep -E '^(PASS|FAIL|SKIP) T[0-9]+' "$RUN_OUT" || true
+	exit 1
+fi
diff --git a/tools/testing/selftests/bpf/vmtest.sh b/tools/testing/selftests/bpf/vmtest.sh
index 2f869daf8a06..b30e2b359413 100755
--- a/tools/testing/selftests/bpf/vmtest.sh
+++ b/tools/testing/selftests/bpf/vmtest.sh
@@ -61,7 +61,7 @@ DEFAULT_COMMAND="./test_progs"
 MOUNT_DIR="mnt"
 LOCAL_ROOTFS_IMAGE=""
 ROOTFS_IMAGE="root.img"
-OUTPUT_DIR="$HOME/.bpf_selftests"
+OUTPUT_DIR="${OUTPUT_DIR:-$HOME/.bpf_selftests}"
 KCONFIG_REL_PATHS=("tools/testing/selftests/bpf/config"
 	"tools/testing/selftests/bpf/config.vm"
 	"tools/testing/selftests/bpf/config.${PLATFORM}")
@@ -294,7 +294,7 @@ EOF
 		-m 4G \
 		-drive file="${rootfs_img}",format=raw,index=1,media=disk,if=virtio,cache=none \
 		-kernel "${kernel_bzimage}" \
-		-append "root=/dev/vda rw console=${QEMU_CONSOLE}"
+		-append "root=/dev/vda rw console=${QEMU_CONSOLE} ${VMTEST_EXTRA_CMDLINE:-}"
 }
 
 copy_logs()
-- 
2.53.0


^ permalink raw reply related

* Re: [net-next] netlabel: fix IPv6 unlabeled address add error handling
From: Paul Moore @ 2026-05-22  3:50 UTC (permalink / raw)
  To: Chenguang Zhao
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, netdev, linux-security-module
In-Reply-To: <20260522022910.398416-1-zhaochenguang@kylinos.cn>

On Thu, May 21, 2026 at 10:30 PM Chenguang Zhao
<zhaochenguang@kylinos.cn> wrote:
>
> netlbl_unlhsh_add_addr6() always returned zero after
> netlbl_af6list_add(), masking failures such as duplicate
> IPv6 static label entries.
>
> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
> ---
>  net/netlabel/netlabel_unlabeled.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Nice catch, thanks!

Acked-by: Paul Moore <paul@paul-moore.com>

-- 
paul-moore.com

^ permalink raw reply

* [net-next] netlabel: validate unlabeled mask attribute length
From: Chenguang Zhao @ 2026-05-22  5:45 UTC (permalink / raw)
  To: Paul Moore, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman
  Cc: Chenguang Zhao, netdev, linux-security-module

netlbl_unlabel_addrinfo_get() checked the address length
but allowed shorter mask attributes to pass through to
fixed-size address reads.

Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
 netlbl_unlabel_addrinfo_get() only rejected a mask
 length mismatch when the address attribute length
 was also invalid.  A crafted Generic Netlink request
 could therefore provide a valid IPv4/IPv6 address
 attribute with a shorter mask attribute.
 
 NLA_BINARY policy lengths are maximum lengths,
 not exact lengths, so the short mask can pass
 policy validation.  The mask is later read as
 a full struct in_addr or struct in6_addr.
 Require both address and mask attributes to
 have the exact expected size.
---
 net/netlabel/netlabel_unlabeled.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c
index ca7a9e2a3de7..c1b7e0061886 100644
--- a/net/netlabel/netlabel_unlabeled.c
+++ b/net/netlabel/netlabel_unlabeled.c
@@ -762,8 +762,9 @@ static int netlbl_unlabel_addrinfo_get(struct genl_info *info,
 	if (info->attrs[NLBL_UNLABEL_A_IPV4ADDR] &&
 	    info->attrs[NLBL_UNLABEL_A_IPV4MASK]) {
 		addr_len = nla_len(info->attrs[NLBL_UNLABEL_A_IPV4ADDR]);
-		if (addr_len != sizeof(struct in_addr) &&
-		    addr_len != nla_len(info->attrs[NLBL_UNLABEL_A_IPV4MASK]))
+		if (addr_len != sizeof(struct in_addr) ||
+		    nla_len(info->attrs[NLBL_UNLABEL_A_IPV4MASK]) !=
+		    sizeof(struct in_addr))
 			return -EINVAL;
 		*len = addr_len;
 		*addr = nla_data(info->attrs[NLBL_UNLABEL_A_IPV4ADDR]);
@@ -771,8 +772,9 @@ static int netlbl_unlabel_addrinfo_get(struct genl_info *info,
 		return 0;
 	} else if (info->attrs[NLBL_UNLABEL_A_IPV6ADDR]) {
 		addr_len = nla_len(info->attrs[NLBL_UNLABEL_A_IPV6ADDR]);
-		if (addr_len != sizeof(struct in6_addr) &&
-		    addr_len != nla_len(info->attrs[NLBL_UNLABEL_A_IPV6MASK]))
+		if (addr_len != sizeof(struct in6_addr) ||
+		    nla_len(info->attrs[NLBL_UNLABEL_A_IPV6MASK]) !=
+		    sizeof(struct in6_addr))
 			return -EINVAL;
 		*len = addr_len;
 		*addr = nla_data(info->attrs[NLBL_UNLABEL_A_IPV6ADDR]);
-- 
2.25.1


^ permalink raw reply related

* [ANN] Linux Security Summit Europe 2026 CfP
From: Reshetova, Elena @ 2026-05-22  5:56 UTC (permalink / raw)
  To: linux-security-module@vger.kernel.org
  Cc: Linux Security Summit Program Committee, Friend,
	linux-integrity@vger.kernel.org, lwn@lwn.net,
	linux-crypto@vger.kernel.org, keyrings@vger.kernel.org,
	linux-coco@lists.linux.dev, kernel-hardening@lists.openwall.com

====================================================================
             ANNOUNCEMENT AND CALL FOR PARTICIPATION

                   LINUX SECURITY SUMMIT EUROPE 2026

                           Thursday, 8 October
                             Prague, Czechia
====================================================================

DESCRIPTION

Linux Security Summit Europe (LSS-EU) 2026 is a technical forum for
collaboration between Linux developers, researchers, and end-users.

Its primary aim is to foster community efforts in deeply analyzing and
solving Linux operating system security challenges, including those in the
Linux kernel.

This year LSS-EU is a single day event happening right after Linux Plumbers 2026
https://lpc.events/ 

Proposals to LSS-EU should be submitted via:
    https://events.linuxfoundation.org/linux-security-summit-europe/program/cfp/

SUGGESTED TOPICS

    * Access Control
    * Case Studies
    * Cryptography and Key Management
    * Emerging Technologies, Threats & Techniques
    * Hardware Security
    * IoT and Embedded Security
    * Integrity Policy and Enforcement
    * Open Source Supply Chain for the Linux OS
    * Security Tools
    * Security UX
    * Linux OS Hardening
    * Virtualization and Containers

DATES TO REMEMBER:

    * CFP Close: Sunday, 28 June at 11:59 PM CEST (UTC +2)  / 2:59 PM PDT (UTC -7)
    * CFP Notifications: Tuesday, 14 July
    * Schedule Announced: Wednesday, 15 July
    * Event Date: Thursday, 8 October

WHO SHOULD ATTEND

We're seeking a diverse range of attendees and welcome participation by
people involved in Linux security development, operations, and research.

LSS is a unique global event that provides the opportunity to present and
discuss your work or research with key Linux security community members and
maintainers.  It's also useful for those who wish to keep up with the latest
in Linux security development and to provide input to the development
process.

MASTODON

  For event updates and announcements, follow:

    https://social.kernel.org/LinuxSecSummit

  #linuxsecuritysummit

PROGRAM COMMITTEE

  The program committee for LSS 2026 is:

    * James Morris, Microsoft
    * Serge Hallyn, Geico
    * Paul Moore, Microsoft
    * Stephen Smalley, NSA
    * Elena Reshetova, Intel
    * John Johansen, Canonical
    * Kees Cook, Google
    * Casey Schaufler
    * Mimi Zohar, IBM
    * David A. Wheeler, Linux Foundation

  The program committee may be contacted as a group via email:
    lss-pc () lists.linuxfoundation.org



^ permalink raw reply

* [PATCH v3] keys/trusted_keys: move TPM-specific fields into trusted_tpm_options
From: Srish Srinivasan @ 2026-05-22  8:16 UTC (permalink / raw)
  To: linux-integrity, keyrings
  Cc: James.Bottomley, stefanb, jarkko, zohar, nayna, rnsastry,
	linux-kernel, linux-security-module, ssrish

The trusted_key_options struct contains TPM-specific fields (keyhandle,
keyauth, blobauth_len, blobauth, pcrinfo_len, pcrinfo, pcrlock, hash,
policydigest_len, policydigest, and policyhandle). This leads to the
accumulation of backend-specific fields in the generic options structure.

Define a trusted_tpm_options structure and move the TPM-specific fields
there. Store a pointer to trusted_tpm_options in trusted_key_options's
private.

No functional change intended.

Signed-off-by: Srish Srinivasan <ssrish@linux.ibm.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
---
This patch depends on 9ec4175a30eb ("KEYS: trusted: Debugging as a
feature"), which is in linux-tpmdd/master but not yet in mainline.

Please apply on top of linux-tpmdd/master.

base-commit: 67657fb65aa9f2d7dd46235246b1677792bd103e

Changelog:
 v3:
  - Exclude the preparatory clean up patch as the problem has been addressed
    in commit 9ec4175a30eb ("KEYS: trusted: Debugging as a feature")

 v2:
  - Exclude the bug-fix patch as it has already been applied to 6.19-rc7
  - Rename instances of trusted_tpm_options from tpm_opts to private
  - Use pr_debug and KERN_DEBUG for logging debug messages (preparatory clean
    up patch)
  - Address other minor comments from Jarkko

 include/keys/trusted-type.h               | 11 ---
 include/keys/trusted_tpm.h                | 14 ++++
 security/keys/trusted-keys/trusted_tpm1.c | 95 ++++++++++++++---------
 security/keys/trusted-keys/trusted_tpm2.c | 51 ++++++------
 4 files changed, 102 insertions(+), 69 deletions(-)

diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
index 9f9940482da4..3db61b57cf73 100644
--- a/include/keys/trusted-type.h
+++ b/include/keys/trusted-type.h
@@ -39,17 +39,6 @@ struct trusted_key_payload {
 
 struct trusted_key_options {
 	uint16_t keytype;
-	uint32_t keyhandle;
-	unsigned char keyauth[TPM_DIGEST_SIZE];
-	uint32_t blobauth_len;
-	unsigned char blobauth[TPM_DIGEST_SIZE];
-	uint32_t pcrinfo_len;
-	unsigned char pcrinfo[MAX_PCRINFO_SIZE];
-	int pcrlock;
-	uint32_t hash;
-	uint32_t policydigest_len;
-	unsigned char policydigest[MAX_DIGEST_SIZE];
-	uint32_t policyhandle;
 	void *private;
 };
 
diff --git a/include/keys/trusted_tpm.h b/include/keys/trusted_tpm.h
index 0fadc6a4f166..355ebd36cbfd 100644
--- a/include/keys/trusted_tpm.h
+++ b/include/keys/trusted_tpm.h
@@ -7,6 +7,20 @@
 
 extern struct trusted_key_ops trusted_key_tpm_ops;
 
+struct trusted_tpm_options {
+	uint32_t keyhandle;
+	unsigned char keyauth[TPM_DIGEST_SIZE];
+	uint32_t blobauth_len;
+	unsigned char blobauth[TPM_DIGEST_SIZE];
+	uint32_t pcrinfo_len;
+	unsigned char pcrinfo[MAX_PCRINFO_SIZE];
+	int pcrlock;
+	uint32_t hash;
+	uint32_t policydigest_len;
+	unsigned char policydigest[MAX_DIGEST_SIZE];
+	uint32_t policyhandle;
+};
+
 int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_payload *payload,
 		      struct trusted_key_options *options);
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 13513819991e..21360a41d290 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -49,15 +49,17 @@ enum {
 #ifdef CONFIG_TRUSTED_KEYS_DEBUG
 static inline void dump_options(struct trusted_key_options *o)
 {
+	struct trusted_tpm_options *private = o->private;
+
 	if (!trusted_debug)
 		return;
 
 	pr_debug("sealing key type %d\n", o->keytype);
-	pr_debug("sealing key handle %0X\n", o->keyhandle);
-	pr_debug("pcrlock %d\n", o->pcrlock);
-	pr_debug("pcrinfo %d\n", o->pcrinfo_len);
+	pr_debug("sealing key handle %0X\n", private->keyhandle);
+	pr_debug("pcrlock %d\n", private->pcrlock);
+	pr_debug("pcrinfo %d\n", private->pcrinfo_len);
 	print_hex_dump_debug("pcrinfo ", DUMP_PREFIX_NONE,
-			     16, 1, o->pcrinfo, o->pcrinfo_len, 0);
+			     16, 1, private->pcrinfo, private->pcrinfo_len, 0);
 }
 
 static inline void dump_sess(struct osapsess *s)
@@ -631,6 +633,7 @@ static int tpm_unseal(struct tpm_buf *tb,
 static int key_seal(struct trusted_key_payload *p,
 		    struct trusted_key_options *o)
 {
+	struct trusted_tpm_options *private = o->private;
 	struct tpm_buf tb;
 	int ret;
 
@@ -641,9 +644,10 @@ static int key_seal(struct trusted_key_payload *p,
 	/* include migratable flag at end of sealed key */
 	p->key[p->key_len] = p->migratable;
 
-	ret = tpm_seal(&tb, o->keytype, o->keyhandle, o->keyauth,
+	ret = tpm_seal(&tb, o->keytype, private->keyhandle, private->keyauth,
 		       p->key, p->key_len + 1, p->blob, &p->blob_len,
-		       o->blobauth, o->pcrinfo, o->pcrinfo_len);
+		       private->blobauth, private->pcrinfo,
+		       private->pcrinfo_len);
 	if (ret < 0)
 		pr_info("srkseal failed (%d)\n", ret);
 
@@ -657,6 +661,7 @@ static int key_seal(struct trusted_key_payload *p,
 static int key_unseal(struct trusted_key_payload *p,
 		      struct trusted_key_options *o)
 {
+	struct trusted_tpm_options *private = o->private;
 	struct tpm_buf tb;
 	int ret;
 
@@ -664,8 +669,8 @@ static int key_unseal(struct trusted_key_payload *p,
 	if (ret)
 		return ret;
 
-	ret = tpm_unseal(&tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
-			 o->blobauth, p->key, &p->key_len);
+	ret = tpm_unseal(&tb, private->keyhandle, private->keyauth, p->blob,
+			 p->blob_len, private->blobauth, p->key, &p->key_len);
 	if (ret < 0)
 		pr_info("srkunseal failed (%d)\n", ret);
 	else
@@ -702,6 +707,7 @@ static const match_table_t key_tokens = {
 static int getoptions(char *c, struct trusted_key_payload *pay,
 		      struct trusted_key_options *opt)
 {
+	struct trusted_tpm_options *private = opt->private;
 	substring_t args[MAX_OPT_ARGS];
 	char *p = c;
 	int token;
@@ -717,7 +723,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 	if (tpm2 < 0)
 		return tpm2;
 
-	opt->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1;
+	private->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1;
 
 	if (!c)
 		return 0;
@@ -731,11 +737,11 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 
 		switch (token) {
 		case Opt_pcrinfo:
-			opt->pcrinfo_len = strlen(args[0].from) / 2;
-			if (opt->pcrinfo_len > MAX_PCRINFO_SIZE)
+			private->pcrinfo_len = strlen(args[0].from) / 2;
+			if (private->pcrinfo_len > MAX_PCRINFO_SIZE)
 				return -EINVAL;
-			res = hex2bin(opt->pcrinfo, args[0].from,
-				      opt->pcrinfo_len);
+			res = hex2bin(private->pcrinfo, args[0].from,
+				      private->pcrinfo_len);
 			if (res < 0)
 				return -EINVAL;
 			break;
@@ -744,12 +750,12 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 			if (res < 0)
 				return -EINVAL;
 			opt->keytype = SEAL_keytype;
-			opt->keyhandle = handle;
+			private->keyhandle = handle;
 			break;
 		case Opt_keyauth:
 			if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
 				return -EINVAL;
-			res = hex2bin(opt->keyauth, args[0].from,
+			res = hex2bin(private->keyauth, args[0].from,
 				      SHA1_DIGEST_SIZE);
 			if (res < 0)
 				return -EINVAL;
@@ -760,21 +766,23 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 			 * hex strings.  TPM 2.0 authorizations are simple
 			 * passwords (although it can take a hash as well)
 			 */
-			opt->blobauth_len = strlen(args[0].from);
+			private->blobauth_len = strlen(args[0].from);
 
-			if (opt->blobauth_len == 2 * TPM_DIGEST_SIZE) {
-				res = hex2bin(opt->blobauth, args[0].from,
+			if (private->blobauth_len == 2 * TPM_DIGEST_SIZE) {
+				res = hex2bin(private->blobauth, args[0].from,
 					      TPM_DIGEST_SIZE);
 				if (res < 0)
 					return -EINVAL;
 
-				opt->blobauth_len = TPM_DIGEST_SIZE;
+				private->blobauth_len = TPM_DIGEST_SIZE;
 				break;
 			}
 
-			if (tpm2 && opt->blobauth_len <= sizeof(opt->blobauth)) {
-				memcpy(opt->blobauth, args[0].from,
-				       opt->blobauth_len);
+			if (tpm2 &&
+			    private->blobauth_len <=
+			    sizeof(private->blobauth)) {
+				memcpy(private->blobauth, args[0].from,
+				       private->blobauth_len);
 				break;
 			}
 
@@ -792,14 +800,14 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 			res = kstrtoul(args[0].from, 10, &lock);
 			if (res < 0)
 				return -EINVAL;
-			opt->pcrlock = lock;
+			private->pcrlock = lock;
 			break;
 		case Opt_hash:
 			if (test_bit(Opt_policydigest, &token_mask))
 				return -EINVAL;
 			for (i = 0; i < HASH_ALGO__LAST; i++) {
 				if (!strcmp(args[0].from, hash_algo_name[i])) {
-					opt->hash = i;
+					private->hash = i;
 					break;
 				}
 			}
@@ -811,14 +819,14 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 			}
 			break;
 		case Opt_policydigest:
-			digest_len = hash_digest_size[opt->hash];
+			digest_len = hash_digest_size[private->hash];
 			if (!tpm2 || strlen(args[0].from) != (2 * digest_len))
 				return -EINVAL;
-			res = hex2bin(opt->policydigest, args[0].from,
+			res = hex2bin(private->policydigest, args[0].from,
 				      digest_len);
 			if (res < 0)
 				return -EINVAL;
-			opt->policydigest_len = digest_len;
+			private->policydigest_len = digest_len;
 			break;
 		case Opt_policyhandle:
 			if (!tpm2)
@@ -826,7 +834,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 			res = kstrtoul(args[0].from, 16, &handle);
 			if (res < 0)
 				return -EINVAL;
-			opt->policyhandle = handle;
+			private->policyhandle = handle;
 			break;
 		default:
 			return -EINVAL;
@@ -837,6 +845,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 
 static struct trusted_key_options *trusted_options_alloc(void)
 {
+	struct trusted_tpm_options *private;
 	struct trusted_key_options *options;
 	int tpm2;
 
@@ -849,14 +858,23 @@ static struct trusted_key_options *trusted_options_alloc(void)
 		/* set any non-zero defaults */
 		options->keytype = SRK_keytype;
 
-		if (!tpm2)
-			options->keyhandle = SRKHANDLE;
+		private = kzalloc_obj(*private);
+		if (!private) {
+			kfree_sensitive(options);
+			options = NULL;
+		} else {
+			if (!tpm2)
+				private->keyhandle = SRKHANDLE;
+
+			options->private = private;
+		}
 	}
 	return options;
 }
 
 static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
 {
+	struct trusted_tpm_options *private = NULL;
 	struct trusted_key_options *options = NULL;
 	int ret = 0;
 	int tpm2;
@@ -874,7 +892,8 @@ static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
 		goto out;
 	dump_options(options);
 
-	if (!options->keyhandle && !tpm2) {
+	private = options->private;
+	if (!private->keyhandle && !tpm2) {
 		ret = -EINVAL;
 		goto out;
 	}
@@ -888,20 +907,22 @@ static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
 		goto out;
 	}
 
-	if (options->pcrlock) {
-		ret = pcrlock(options->pcrlock);
+	if (private->pcrlock) {
+		ret = pcrlock(private->pcrlock);
 		if (ret < 0) {
 			pr_info("pcrlock failed (%d)\n", ret);
 			goto out;
 		}
 	}
 out:
+	kfree_sensitive(options->private);
 	kfree_sensitive(options);
 	return ret;
 }
 
 static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
 {
+	struct trusted_tpm_options *private = NULL;
 	struct trusted_key_options *options = NULL;
 	int ret = 0;
 	int tpm2;
@@ -919,7 +940,8 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
 		goto out;
 	dump_options(options);
 
-	if (!options->keyhandle && !tpm2) {
+	private = options->private;
+	if (!private->keyhandle && !tpm2) {
 		ret = -EINVAL;
 		goto out;
 	}
@@ -931,14 +953,15 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
 	if (ret < 0)
 		pr_info("key_unseal failed (%d)\n", ret);
 
-	if (options->pcrlock) {
-		ret = pcrlock(options->pcrlock);
+	if (private->pcrlock) {
+		ret = pcrlock(private->pcrlock);
 		if (ret < 0) {
 			pr_info("pcrlock failed (%d)\n", ret);
 			goto out;
 		}
 	}
 out:
+	kfree_sensitive(options->private);
 	kfree_sensitive(options);
 	return ret;
 }
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 6340823f8b53..94e01249b921 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -24,6 +24,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
 			   u8 *src, u32 len)
 {
+	struct trusted_tpm_options *private = options->private;
 	const int SCRATCH_SIZE = PAGE_SIZE;
 	u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
 	u8 *work = scratch, *work1;
@@ -46,7 +47,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	work = asn1_encode_oid(work, end_work, tpm2key_oid,
 			       asn1_oid_len(tpm2key_oid));
 
-	if (options->blobauth_len == 0) {
+	if (private->blobauth_len == 0) {
 		unsigned char bool[3], *w = bool;
 		/* tag 0 is emptyAuth */
 		w = asn1_encode_boolean(w, w + sizeof(bool), true);
@@ -69,7 +70,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 		goto err;
 	}
 
-	work = asn1_encode_integer(work, end_work, options->keyhandle);
+	work = asn1_encode_integer(work, end_work, private->keyhandle);
 	work = asn1_encode_octet_string(work, end_work, pub, pub_len);
 	work = asn1_encode_octet_string(work, end_work, priv, priv_len);
 
@@ -102,6 +103,7 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
 			   u8 **buf)
 {
+	struct trusted_tpm_options *private = options->private;
 	int ret;
 	struct tpm2_key_context ctx;
 	u8 *blob;
@@ -121,7 +123,7 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
 		return -ENOMEM;
 
 	*buf = blob;
-	options->keyhandle = ctx.parent;
+	private->keyhandle = ctx.parent;
 
 	memcpy(blob, ctx.priv, ctx.priv_len);
 	blob += ctx.priv_len;
@@ -233,6 +235,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_payload *payload,
 		      struct trusted_key_options *options)
 {
+	struct trusted_tpm_options *private = options->private;
 	off_t offset = TPM_HEADER_SIZE;
 	struct tpm_buf buf, sized;
 	int blob_len = 0;
@@ -240,11 +243,11 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	u32 flags;
 	int rc;
 
-	hash = tpm2_find_hash_alg(options->hash);
+	hash = tpm2_find_hash_alg(private->hash);
 	if (hash < 0)
 		return hash;
 
-	if (!options->keyhandle)
+	if (!private->keyhandle)
 		return -EINVAL;
 
 	rc = tpm_try_get_ops(chip);
@@ -268,18 +271,19 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		goto out_put;
 	}
 
-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, &buf, private->keyhandle, NULL);
 	if (rc)
 		goto out;
 
 	tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
-				    options->keyauth, TPM_DIGEST_SIZE);
+				    private->keyauth, TPM_DIGEST_SIZE);
 
 	/* sensitive */
-	tpm_buf_append_u16(&sized, options->blobauth_len);
+	tpm_buf_append_u16(&sized, private->blobauth_len);
 
-	if (options->blobauth_len)
-		tpm_buf_append(&sized, options->blobauth, options->blobauth_len);
+	if (private->blobauth_len)
+		tpm_buf_append(&sized, private->blobauth,
+			       private->blobauth_len);
 
 	tpm_buf_append_u16(&sized, payload->key_len);
 	tpm_buf_append(&sized, payload->key, payload->key_len);
@@ -292,14 +296,15 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 
 	/* key properties */
 	flags = 0;
-	flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
+	flags |= private->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
 	flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT);
 	tpm_buf_append_u32(&sized, flags);
 
 	/* policy */
-	tpm_buf_append_u16(&sized, options->policydigest_len);
-	if (options->policydigest_len)
-		tpm_buf_append(&sized, options->policydigest, options->policydigest_len);
+	tpm_buf_append_u16(&sized, private->policydigest_len);
+	if (private->policydigest_len)
+		tpm_buf_append(&sized, private->policydigest,
+			       private->policydigest_len);
 
 	/* public parameters */
 	tpm_buf_append_u16(&sized, TPM_ALG_NULL);
@@ -373,6 +378,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 			 u32 *blob_handle)
 {
 	u8 *blob_ref __free(kfree) = NULL;
+	struct trusted_tpm_options *private = options->private;
 	struct tpm_buf buf;
 	unsigned int private_len;
 	unsigned int public_len;
@@ -392,7 +398,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 	}
 
 	/* new format carries keyhandle but old format doesn't */
-	if (!options->keyhandle)
+	if (!private->keyhandle)
 		return -EINVAL;
 
 	/* must be big enough for at least the two be16 size counts */
@@ -433,11 +439,11 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 		return rc;
 	}
 
-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, &buf, private->keyhandle, NULL);
 	if (rc)
 		goto out;
 
-	tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
+	tpm_buf_append_hmac_session(chip, &buf, 0, private->keyauth,
 				    TPM_DIGEST_SIZE);
 
 	tpm_buf_append(&buf, blob, blob_len);
@@ -481,6 +487,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 			   struct trusted_key_options *options,
 			   u32 blob_handle)
 {
+	struct trusted_tpm_options *private = options->private;
 	struct tpm_header *head;
 	struct tpm_buf buf;
 	u16 data_len;
@@ -502,10 +509,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 	if (rc)
 		goto out;
 
-	if (!options->policyhandle) {
+	if (!private->policyhandle) {
 		tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
-					    options->blobauth,
-					    options->blobauth_len);
+					    private->blobauth,
+					    private->blobauth_len);
 	} else {
 		/*
 		 * FIXME: The policy session was generated outside the
@@ -518,9 +525,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 		 * could repeat our actions with the exfiltrated
 		 * password.
 		 */
-		tpm2_buf_append_auth(&buf, options->policyhandle,
+		tpm2_buf_append_auth(&buf, private->policyhandle,
 				     NULL /* nonce */, 0, 0,
-				     options->blobauth, options->blobauth_len);
+				     private->blobauth, private->blobauth_len);
 		if (tpm2_chip_auth(chip)) {
 			tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0);
 		} else  {

-- 
2.51.0


^ permalink raw reply related

* Re: [PATCH v4 1/7] lsm: Add granular mount hooks to replace security_sb_mount
From: Christian Brauner @ 2026-05-22  9:15 UTC (permalink / raw)
  To: Song Liu
  Cc: linux-security-module, linux-fsdevel, selinux, apparmor, paul,
	jmorris, serge, viro, brauner, jack, john.johansen,
	stephen.smalley.work, omosnace, mic, gnoack, takedakn,
	penguin-kernel, herton, kernel-team
In-Reply-To: <20260515200158.4081915-2-song@kernel.org>

On Fri, 15 May 2026 13:01:52 -0700, Song Liu <song@kernel.org> wrote:
> [...]
> 
> Reviewed-by: Stephen Smalley <stephen.smalley.work@gmail.com>
> Tested-by: Stephen Smalley <stephen.smalley.work@gmail.com> # for selinux only
> Signed-off-by: Song Liu <song@kernel.org>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>

Please cleanly separate the preparatory work for introducing the new
hooks from any changes to fs/namespace.c

Once you have all of the new machinery in place, switch fs/namespace.c
over to the new hooks.

This will make it way easier to review and easier to distribute the

-- 
Christian Brauner <brauner@kernel.org>

^ permalink raw reply

* Re: [PATCH v3] keys/trusted_keys: move TPM-specific fields into trusted_tpm_options
From: Jarkko Sakkinen @ 2026-05-22 12:41 UTC (permalink / raw)
  To: Srish Srinivasan
  Cc: linux-integrity, keyrings, James.Bottomley, stefanb, zohar, nayna,
	rnsastry, linux-kernel, linux-security-module
In-Reply-To: <20260522081637.189546-1-ssrish@linux.ibm.com>

On Fri, May 22, 2026 at 01:46:37PM +0530, Srish Srinivasan wrote:
> The trusted_key_options struct contains TPM-specific fields (keyhandle,
> keyauth, blobauth_len, blobauth, pcrinfo_len, pcrinfo, pcrlock, hash,
> policydigest_len, policydigest, and policyhandle). This leads to the
> accumulation of backend-specific fields in the generic options structure.
> 
> Define a trusted_tpm_options structure and move the TPM-specific fields
> there. Store a pointer to trusted_tpm_options in trusted_key_options's
> private.
> 
> No functional change intended.
> 
> Signed-off-by: Srish Srinivasan <ssrish@linux.ibm.com>
> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
> ---

I'm supporting this change yes but before spreading tpm_buf all over the
place I'd like to get this merged:

https://lore.kernel.org/linux-integrity/20260522013555.1063716-1-jarkko@kernel.org/T/#u

It'd need tested-by's most importantly. There's another patch shoe-horning
buffers a bit upgrading tpm_buf so I thought now would be good tiem to
get this finally applied.

Possible merge conflicts for +1 change of this should be fairly trivial.

BR, Jarkko

> This patch depends on 9ec4175a30eb ("KEYS: trusted: Debugging as a
> feature"), which is in linux-tpmdd/master but not yet in mainline.
> 
> Please apply on top of linux-tpmdd/master.
> 
> base-commit: 67657fb65aa9f2d7dd46235246b1677792bd103e
> 
> Changelog:
>  v3:
>   - Exclude the preparatory clean up patch as the problem has been addressed
>     in commit 9ec4175a30eb ("KEYS: trusted: Debugging as a feature")
> 
>  v2:
>   - Exclude the bug-fix patch as it has already been applied to 6.19-rc7
>   - Rename instances of trusted_tpm_options from tpm_opts to private
>   - Use pr_debug and KERN_DEBUG for logging debug messages (preparatory clean
>     up patch)
>   - Address other minor comments from Jarkko
> 
>  include/keys/trusted-type.h               | 11 ---
>  include/keys/trusted_tpm.h                | 14 ++++
>  security/keys/trusted-keys/trusted_tpm1.c | 95 ++++++++++++++---------
>  security/keys/trusted-keys/trusted_tpm2.c | 51 ++++++------
>  4 files changed, 102 insertions(+), 69 deletions(-)
> 
> diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
> index 9f9940482da4..3db61b57cf73 100644
> --- a/include/keys/trusted-type.h
> +++ b/include/keys/trusted-type.h
> @@ -39,17 +39,6 @@ struct trusted_key_payload {
>  
>  struct trusted_key_options {
>  	uint16_t keytype;
> -	uint32_t keyhandle;
> -	unsigned char keyauth[TPM_DIGEST_SIZE];
> -	uint32_t blobauth_len;
> -	unsigned char blobauth[TPM_DIGEST_SIZE];
> -	uint32_t pcrinfo_len;
> -	unsigned char pcrinfo[MAX_PCRINFO_SIZE];
> -	int pcrlock;
> -	uint32_t hash;
> -	uint32_t policydigest_len;
> -	unsigned char policydigest[MAX_DIGEST_SIZE];
> -	uint32_t policyhandle;
>  	void *private;
>  };
>  
> diff --git a/include/keys/trusted_tpm.h b/include/keys/trusted_tpm.h
> index 0fadc6a4f166..355ebd36cbfd 100644
> --- a/include/keys/trusted_tpm.h
> +++ b/include/keys/trusted_tpm.h
> @@ -7,6 +7,20 @@
>  
>  extern struct trusted_key_ops trusted_key_tpm_ops;
>  
> +struct trusted_tpm_options {
> +	uint32_t keyhandle;
> +	unsigned char keyauth[TPM_DIGEST_SIZE];
> +	uint32_t blobauth_len;
> +	unsigned char blobauth[TPM_DIGEST_SIZE];
> +	uint32_t pcrinfo_len;
> +	unsigned char pcrinfo[MAX_PCRINFO_SIZE];
> +	int pcrlock;
> +	uint32_t hash;
> +	uint32_t policydigest_len;
> +	unsigned char policydigest[MAX_DIGEST_SIZE];
> +	uint32_t policyhandle;
> +};
> +
>  int tpm2_seal_trusted(struct tpm_chip *chip,
>  		      struct trusted_key_payload *payload,
>  		      struct trusted_key_options *options);
> diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
> index 13513819991e..21360a41d290 100644
> --- a/security/keys/trusted-keys/trusted_tpm1.c
> +++ b/security/keys/trusted-keys/trusted_tpm1.c
> @@ -49,15 +49,17 @@ enum {
>  #ifdef CONFIG_TRUSTED_KEYS_DEBUG
>  static inline void dump_options(struct trusted_key_options *o)
>  {
> +	struct trusted_tpm_options *private = o->private;
> +
>  	if (!trusted_debug)
>  		return;
>  
>  	pr_debug("sealing key type %d\n", o->keytype);
> -	pr_debug("sealing key handle %0X\n", o->keyhandle);
> -	pr_debug("pcrlock %d\n", o->pcrlock);
> -	pr_debug("pcrinfo %d\n", o->pcrinfo_len);
> +	pr_debug("sealing key handle %0X\n", private->keyhandle);
> +	pr_debug("pcrlock %d\n", private->pcrlock);
> +	pr_debug("pcrinfo %d\n", private->pcrinfo_len);
>  	print_hex_dump_debug("pcrinfo ", DUMP_PREFIX_NONE,
> -			     16, 1, o->pcrinfo, o->pcrinfo_len, 0);
> +			     16, 1, private->pcrinfo, private->pcrinfo_len, 0);
>  }
>  
>  static inline void dump_sess(struct osapsess *s)
> @@ -631,6 +633,7 @@ static int tpm_unseal(struct tpm_buf *tb,
>  static int key_seal(struct trusted_key_payload *p,
>  		    struct trusted_key_options *o)
>  {
> +	struct trusted_tpm_options *private = o->private;
>  	struct tpm_buf tb;
>  	int ret;
>  
> @@ -641,9 +644,10 @@ static int key_seal(struct trusted_key_payload *p,
>  	/* include migratable flag at end of sealed key */
>  	p->key[p->key_len] = p->migratable;
>  
> -	ret = tpm_seal(&tb, o->keytype, o->keyhandle, o->keyauth,
> +	ret = tpm_seal(&tb, o->keytype, private->keyhandle, private->keyauth,
>  		       p->key, p->key_len + 1, p->blob, &p->blob_len,
> -		       o->blobauth, o->pcrinfo, o->pcrinfo_len);
> +		       private->blobauth, private->pcrinfo,
> +		       private->pcrinfo_len);
>  	if (ret < 0)
>  		pr_info("srkseal failed (%d)\n", ret);
>  
> @@ -657,6 +661,7 @@ static int key_seal(struct trusted_key_payload *p,
>  static int key_unseal(struct trusted_key_payload *p,
>  		      struct trusted_key_options *o)
>  {
> +	struct trusted_tpm_options *private = o->private;
>  	struct tpm_buf tb;
>  	int ret;
>  
> @@ -664,8 +669,8 @@ static int key_unseal(struct trusted_key_payload *p,
>  	if (ret)
>  		return ret;
>  
> -	ret = tpm_unseal(&tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
> -			 o->blobauth, p->key, &p->key_len);
> +	ret = tpm_unseal(&tb, private->keyhandle, private->keyauth, p->blob,
> +			 p->blob_len, private->blobauth, p->key, &p->key_len);
>  	if (ret < 0)
>  		pr_info("srkunseal failed (%d)\n", ret);
>  	else
> @@ -702,6 +707,7 @@ static const match_table_t key_tokens = {
>  static int getoptions(char *c, struct trusted_key_payload *pay,
>  		      struct trusted_key_options *opt)
>  {
> +	struct trusted_tpm_options *private = opt->private;
>  	substring_t args[MAX_OPT_ARGS];
>  	char *p = c;
>  	int token;
> @@ -717,7 +723,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
>  	if (tpm2 < 0)
>  		return tpm2;
>  
> -	opt->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1;
> +	private->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1;
>  
>  	if (!c)
>  		return 0;
> @@ -731,11 +737,11 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
>  
>  		switch (token) {
>  		case Opt_pcrinfo:
> -			opt->pcrinfo_len = strlen(args[0].from) / 2;
> -			if (opt->pcrinfo_len > MAX_PCRINFO_SIZE)
> +			private->pcrinfo_len = strlen(args[0].from) / 2;
> +			if (private->pcrinfo_len > MAX_PCRINFO_SIZE)
>  				return -EINVAL;
> -			res = hex2bin(opt->pcrinfo, args[0].from,
> -				      opt->pcrinfo_len);
> +			res = hex2bin(private->pcrinfo, args[0].from,
> +				      private->pcrinfo_len);
>  			if (res < 0)
>  				return -EINVAL;
>  			break;
> @@ -744,12 +750,12 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
>  			if (res < 0)
>  				return -EINVAL;
>  			opt->keytype = SEAL_keytype;
> -			opt->keyhandle = handle;
> +			private->keyhandle = handle;
>  			break;
>  		case Opt_keyauth:
>  			if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
>  				return -EINVAL;
> -			res = hex2bin(opt->keyauth, args[0].from,
> +			res = hex2bin(private->keyauth, args[0].from,
>  				      SHA1_DIGEST_SIZE);
>  			if (res < 0)
>  				return -EINVAL;
> @@ -760,21 +766,23 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
>  			 * hex strings.  TPM 2.0 authorizations are simple
>  			 * passwords (although it can take a hash as well)
>  			 */
> -			opt->blobauth_len = strlen(args[0].from);
> +			private->blobauth_len = strlen(args[0].from);
>  
> -			if (opt->blobauth_len == 2 * TPM_DIGEST_SIZE) {
> -				res = hex2bin(opt->blobauth, args[0].from,
> +			if (private->blobauth_len == 2 * TPM_DIGEST_SIZE) {
> +				res = hex2bin(private->blobauth, args[0].from,
>  					      TPM_DIGEST_SIZE);
>  				if (res < 0)
>  					return -EINVAL;
>  
> -				opt->blobauth_len = TPM_DIGEST_SIZE;
> +				private->blobauth_len = TPM_DIGEST_SIZE;
>  				break;
>  			}
>  
> -			if (tpm2 && opt->blobauth_len <= sizeof(opt->blobauth)) {
> -				memcpy(opt->blobauth, args[0].from,
> -				       opt->blobauth_len);
> +			if (tpm2 &&
> +			    private->blobauth_len <=
> +			    sizeof(private->blobauth)) {
> +				memcpy(private->blobauth, args[0].from,
> +				       private->blobauth_len);
>  				break;
>  			}
>  
> @@ -792,14 +800,14 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
>  			res = kstrtoul(args[0].from, 10, &lock);
>  			if (res < 0)
>  				return -EINVAL;
> -			opt->pcrlock = lock;
> +			private->pcrlock = lock;
>  			break;
>  		case Opt_hash:
>  			if (test_bit(Opt_policydigest, &token_mask))
>  				return -EINVAL;
>  			for (i = 0; i < HASH_ALGO__LAST; i++) {
>  				if (!strcmp(args[0].from, hash_algo_name[i])) {
> -					opt->hash = i;
> +					private->hash = i;
>  					break;
>  				}
>  			}
> @@ -811,14 +819,14 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
>  			}
>  			break;
>  		case Opt_policydigest:
> -			digest_len = hash_digest_size[opt->hash];
> +			digest_len = hash_digest_size[private->hash];
>  			if (!tpm2 || strlen(args[0].from) != (2 * digest_len))
>  				return -EINVAL;
> -			res = hex2bin(opt->policydigest, args[0].from,
> +			res = hex2bin(private->policydigest, args[0].from,
>  				      digest_len);
>  			if (res < 0)
>  				return -EINVAL;
> -			opt->policydigest_len = digest_len;
> +			private->policydigest_len = digest_len;
>  			break;
>  		case Opt_policyhandle:
>  			if (!tpm2)
> @@ -826,7 +834,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
>  			res = kstrtoul(args[0].from, 16, &handle);
>  			if (res < 0)
>  				return -EINVAL;
> -			opt->policyhandle = handle;
> +			private->policyhandle = handle;
>  			break;
>  		default:
>  			return -EINVAL;
> @@ -837,6 +845,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
>  
>  static struct trusted_key_options *trusted_options_alloc(void)
>  {
> +	struct trusted_tpm_options *private;
>  	struct trusted_key_options *options;
>  	int tpm2;
>  
> @@ -849,14 +858,23 @@ static struct trusted_key_options *trusted_options_alloc(void)
>  		/* set any non-zero defaults */
>  		options->keytype = SRK_keytype;
>  
> -		if (!tpm2)
> -			options->keyhandle = SRKHANDLE;
> +		private = kzalloc_obj(*private);
> +		if (!private) {
> +			kfree_sensitive(options);
> +			options = NULL;
> +		} else {
> +			if (!tpm2)
> +				private->keyhandle = SRKHANDLE;
> +
> +			options->private = private;
> +		}
>  	}
>  	return options;
>  }
>  
>  static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
>  {
> +	struct trusted_tpm_options *private = NULL;
>  	struct trusted_key_options *options = NULL;
>  	int ret = 0;
>  	int tpm2;
> @@ -874,7 +892,8 @@ static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
>  		goto out;
>  	dump_options(options);
>  
> -	if (!options->keyhandle && !tpm2) {
> +	private = options->private;
> +	if (!private->keyhandle && !tpm2) {
>  		ret = -EINVAL;
>  		goto out;
>  	}
> @@ -888,20 +907,22 @@ static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
>  		goto out;
>  	}
>  
> -	if (options->pcrlock) {
> -		ret = pcrlock(options->pcrlock);
> +	if (private->pcrlock) {
> +		ret = pcrlock(private->pcrlock);
>  		if (ret < 0) {
>  			pr_info("pcrlock failed (%d)\n", ret);
>  			goto out;
>  		}
>  	}
>  out:
> +	kfree_sensitive(options->private);
>  	kfree_sensitive(options);
>  	return ret;
>  }
>  
>  static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
>  {
> +	struct trusted_tpm_options *private = NULL;
>  	struct trusted_key_options *options = NULL;
>  	int ret = 0;
>  	int tpm2;
> @@ -919,7 +940,8 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
>  		goto out;
>  	dump_options(options);
>  
> -	if (!options->keyhandle && !tpm2) {
> +	private = options->private;
> +	if (!private->keyhandle && !tpm2) {
>  		ret = -EINVAL;
>  		goto out;
>  	}
> @@ -931,14 +953,15 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
>  	if (ret < 0)
>  		pr_info("key_unseal failed (%d)\n", ret);
>  
> -	if (options->pcrlock) {
> -		ret = pcrlock(options->pcrlock);
> +	if (private->pcrlock) {
> +		ret = pcrlock(private->pcrlock);
>  		if (ret < 0) {
>  			pr_info("pcrlock failed (%d)\n", ret);
>  			goto out;
>  		}
>  	}
>  out:
> +	kfree_sensitive(options->private);
>  	kfree_sensitive(options);
>  	return ret;
>  }
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> index 6340823f8b53..94e01249b921 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -24,6 +24,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
>  			   struct trusted_key_options *options,
>  			   u8 *src, u32 len)
>  {
> +	struct trusted_tpm_options *private = options->private;
>  	const int SCRATCH_SIZE = PAGE_SIZE;
>  	u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
>  	u8 *work = scratch, *work1;
> @@ -46,7 +47,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
>  	work = asn1_encode_oid(work, end_work, tpm2key_oid,
>  			       asn1_oid_len(tpm2key_oid));
>  
> -	if (options->blobauth_len == 0) {
> +	if (private->blobauth_len == 0) {
>  		unsigned char bool[3], *w = bool;
>  		/* tag 0 is emptyAuth */
>  		w = asn1_encode_boolean(w, w + sizeof(bool), true);
> @@ -69,7 +70,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
>  		goto err;
>  	}
>  
> -	work = asn1_encode_integer(work, end_work, options->keyhandle);
> +	work = asn1_encode_integer(work, end_work, private->keyhandle);
>  	work = asn1_encode_octet_string(work, end_work, pub, pub_len);
>  	work = asn1_encode_octet_string(work, end_work, priv, priv_len);
>  
> @@ -102,6 +103,7 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
>  			   struct trusted_key_options *options,
>  			   u8 **buf)
>  {
> +	struct trusted_tpm_options *private = options->private;
>  	int ret;
>  	struct tpm2_key_context ctx;
>  	u8 *blob;
> @@ -121,7 +123,7 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
>  		return -ENOMEM;
>  
>  	*buf = blob;
> -	options->keyhandle = ctx.parent;
> +	private->keyhandle = ctx.parent;
>  
>  	memcpy(blob, ctx.priv, ctx.priv_len);
>  	blob += ctx.priv_len;
> @@ -233,6 +235,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>  		      struct trusted_key_payload *payload,
>  		      struct trusted_key_options *options)
>  {
> +	struct trusted_tpm_options *private = options->private;
>  	off_t offset = TPM_HEADER_SIZE;
>  	struct tpm_buf buf, sized;
>  	int blob_len = 0;
> @@ -240,11 +243,11 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>  	u32 flags;
>  	int rc;
>  
> -	hash = tpm2_find_hash_alg(options->hash);
> +	hash = tpm2_find_hash_alg(private->hash);
>  	if (hash < 0)
>  		return hash;
>  
> -	if (!options->keyhandle)
> +	if (!private->keyhandle)
>  		return -EINVAL;
>  
>  	rc = tpm_try_get_ops(chip);
> @@ -268,18 +271,19 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>  		goto out_put;
>  	}
>  
> -	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> +	rc = tpm_buf_append_name(chip, &buf, private->keyhandle, NULL);
>  	if (rc)
>  		goto out;
>  
>  	tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
> -				    options->keyauth, TPM_DIGEST_SIZE);
> +				    private->keyauth, TPM_DIGEST_SIZE);
>  
>  	/* sensitive */
> -	tpm_buf_append_u16(&sized, options->blobauth_len);
> +	tpm_buf_append_u16(&sized, private->blobauth_len);
>  
> -	if (options->blobauth_len)
> -		tpm_buf_append(&sized, options->blobauth, options->blobauth_len);
> +	if (private->blobauth_len)
> +		tpm_buf_append(&sized, private->blobauth,
> +			       private->blobauth_len);
>  
>  	tpm_buf_append_u16(&sized, payload->key_len);
>  	tpm_buf_append(&sized, payload->key, payload->key_len);
> @@ -292,14 +296,15 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>  
>  	/* key properties */
>  	flags = 0;
> -	flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
> +	flags |= private->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
>  	flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT);
>  	tpm_buf_append_u32(&sized, flags);
>  
>  	/* policy */
> -	tpm_buf_append_u16(&sized, options->policydigest_len);
> -	if (options->policydigest_len)
> -		tpm_buf_append(&sized, options->policydigest, options->policydigest_len);
> +	tpm_buf_append_u16(&sized, private->policydigest_len);
> +	if (private->policydigest_len)
> +		tpm_buf_append(&sized, private->policydigest,
> +			       private->policydigest_len);
>  
>  	/* public parameters */
>  	tpm_buf_append_u16(&sized, TPM_ALG_NULL);
> @@ -373,6 +378,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
>  			 u32 *blob_handle)
>  {
>  	u8 *blob_ref __free(kfree) = NULL;
> +	struct trusted_tpm_options *private = options->private;
>  	struct tpm_buf buf;
>  	unsigned int private_len;
>  	unsigned int public_len;
> @@ -392,7 +398,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
>  	}
>  
>  	/* new format carries keyhandle but old format doesn't */
> -	if (!options->keyhandle)
> +	if (!private->keyhandle)
>  		return -EINVAL;
>  
>  	/* must be big enough for at least the two be16 size counts */
> @@ -433,11 +439,11 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
>  		return rc;
>  	}
>  
> -	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> +	rc = tpm_buf_append_name(chip, &buf, private->keyhandle, NULL);
>  	if (rc)
>  		goto out;
>  
> -	tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
> +	tpm_buf_append_hmac_session(chip, &buf, 0, private->keyauth,
>  				    TPM_DIGEST_SIZE);
>  
>  	tpm_buf_append(&buf, blob, blob_len);
> @@ -481,6 +487,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
>  			   struct trusted_key_options *options,
>  			   u32 blob_handle)
>  {
> +	struct trusted_tpm_options *private = options->private;
>  	struct tpm_header *head;
>  	struct tpm_buf buf;
>  	u16 data_len;
> @@ -502,10 +509,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
>  	if (rc)
>  		goto out;
>  
> -	if (!options->policyhandle) {
> +	if (!private->policyhandle) {
>  		tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
> -					    options->blobauth,
> -					    options->blobauth_len);
> +					    private->blobauth,
> +					    private->blobauth_len);
>  	} else {
>  		/*
>  		 * FIXME: The policy session was generated outside the
> @@ -518,9 +525,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
>  		 * could repeat our actions with the exfiltrated
>  		 * password.
>  		 */
> -		tpm2_buf_append_auth(&buf, options->policyhandle,
> +		tpm2_buf_append_auth(&buf, private->policyhandle,
>  				     NULL /* nonce */, 0, 0,
> -				     options->blobauth, options->blobauth_len);
> +				     private->blobauth, private->blobauth_len);
>  		if (tpm2_chip_auth(chip)) {
>  			tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0);
>  		} else  {
> 
> -- 
> 2.51.0
> 

^ permalink raw reply

* Re: [RFC PATCH v4 01/19] landlock: Support socket access-control
From: Günther Noack @ 2026-05-22 15:42 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Mikhail Ivanov, gnoack, willemdebruijn.kernel, matthieu,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze
In-Reply-To: <20260508.aeJoht7aepho@digikod.net>

On Fri, May 08, 2026 at 03:29:21PM +0200, Mickaël Salaün wrote:
> On Sat, Apr 18, 2026 at 02:29:04PM +0300, Mikhail Ivanov wrote:
> > On 11/22/2025 2:13 PM, Mikhail Ivanov wrote:
> > > On 11/22/2025 1:49 PM, Günther Noack wrote:
> > > > On Tue, Nov 18, 2025 at 09:46:21PM +0800, Mikhail Ivanov wrote:
> > > > > +/**
> > > > > + * struct landlock_socket_attr - Socket protocol definition
> > > > > + *
> > > > > + * Argument of sys_landlock_add_rule().
> > > > > + */
> > > > > +struct landlock_socket_attr {
> > > > > +    /**
> > > > > +     * @allowed_access: Bitmask of allowed access for a socket protocol
> > > > > +     * (cf. `Socket flags`_).
> > > > > +     */
> > > > > +    __u64 allowed_access;
> > > > > +    /**
> > > > > +     * @family: Protocol family used for communication
> > > > > +     * (cf. include/linux/socket.h).
> > > > > +     */
> > > > > +    __s32 family;
> > > > > +    /**
> > > > > +     * @type: Socket type (cf. include/linux/net.h)
> > > > > +     */
> > > > > +    __s32 type;
> > > > > +    /**
> > > > > +     * @protocol: Communication protocol specific to protocol
> > > > > family set in
> > > > > +     * @family field.
> > > > 
> > > > This is specific to both the @family and the @type, not just the @family.
> > > > 
> > > > > From socket(2):
> > > > 
> > > >    Normally only a single protocol exists to support a particular
> > > >    socket type within a given protocol family.
> > > > 
> > > > For instance, in your commit message above the protocol in the example
> > > > is IPPROTO_TCP, which would imply the type SOCK_STREAM, but not work
> > > > with SOCK_DGRAM.
> > > 
> > > You're right.
> > > 
> > 
> > I revised the socket(2) semantics and this part is about that kernel
> > maps (family, type, 0) to the default protocol of given family and type.
> > Eg. (AF_INET, SOCK_STREAM, 0) is mapped to (AF_INET, SOCK_STREAM,
> > IPPROTO_TCP). I would like to clarify that such mapping is taking place
> > in landlock_socket_attr.protocol field doc.
> > 
> > There should be list of protocols defined per protocol family. From
> > socket(2):
> > 	The domain argument specifies a communication domain.
> > 	...
> > 	The protocol number to use is specific to the “communication
> > 	domain” in which communication is to take place.
> > 
> > Such mapping allows to define strange socket rules if setting @type=-1.
> > For example:
> > 	struct landlock_socket_attr attr = {
> > 		.family = AF_INET,
> > 		.type = -1,
> > 		.protocol = 0,
> > 	};
> 
> Looking again at this API, I think we should not have a special handling
> of the "-1" values but instead change the struct landlock_socket_attr to
> start with a "wildcards" field to properly identify which socket
> property should be "any" value (according to a dedicated flag):
> 
> struct landlock_socket_attr {
> 	__u64 allowed_perm; /* see the ns/cap patch series */
> 	__u32 wildcards; /* LANDLOCK_SOCKET_ANY_PROTOCOL */
> 	__u32 family;
> 	__u32 type;
> 	__u32 protocol;
> };
> 
> In fact, I though a lot about the two potential wildcards (type and
> family we previously discussed), and my conclusion is that we should
> only handle "any protocol" (instead of any type too). This makes the
> UAPI simpler and less dangerous, especially wrt families that have very
> specific and sometime privileged types (e.g. SOCK_RAW).

I'm on board with that, API-wise.  This seems reasonable.


> Another
> important point is that it would allow to do only one rbtree lookup
> (tweaking a bit the rbtree walk) instead of four lookup like with the
> current implementation.  The idea is to generate an rbtree key with:
> 
>   family | type | !any-protocol-boolean | protocol
> 
> This key format allows a one-descent walk lookup.  We'll have to replace
> the use of landlock_find_rule() with a custom walk that first look for
> the any-protocol-boolean (which should probably be represented by 0 for
> "any protocol" and by 1 for "specific protocol"), and if no "any
> protocol" key is found, to continue the walk to match the full protocol
> value.

Seems reasonable as well.


> > This definition corresponds to (AF_INET, SOCK_STREAM, 0->IPPROTO_TCP)
> > and to (AF_INET, SOCK_DGRAM, 0->IPPROTO_UDP).
> > 
> > I don't see this as a bad thing as far as there is proper documentation
> > for landlock_socket_attr.
> 
> Thinking more about the asymmetry between UAPI and kernel state, I think
> the best approach is to canonicalize the rules' values to make them
> match the kernel equivalent.  This behavior would be much less
> surprising to users (this is mostly an UX improvement, but also a way to
> deduplicate some rules).  Indeed, users would be able to use the default
> value (e.g. protocol 0 for INET/STREAM) *and* the canonicalized value
> (e.g. protocol IPPROTO_TCP).  Here is a patch to implement this approach
> and (most importantly) with the related kernel tests to make sure the
> canonicalizations are correct:

Impressive reverse engineering of that mapping. o_O

The approach makes sense to me in general, but I left some more
specific comments below.


> [PATCH] landlock: Canonicalize socket rules and add drift detection
> 
> The kernel socket stack performs family-specific rewrites between the
> (family, type, protocol) triple passed to socket(2) and the resulting
> socket object.  Rewrites currently mirrored:
> 
> - __sock_create rewrites AF_INET + SOCK_PACKET to AF_PACKET for pre-2.2
>   compatibility.
> - unix_create rewrites AF_UNIX + SOCK_RAW to AF_UNIX + SOCK_DGRAM as a
>   BSD leftover, and ignores the user protocol (sk_protocol stays 0 for
>   every AF_UNIX socket).
> - inet_create and inet6_create resolve protocol=0 to a type-specific
>   default (IPPROTO_TCP for SOCK_STREAM, IPPROTO_UDP for SOCK_DGRAM,
>   IPPROTO_SCTP for SOCK_SEQPACKET) via the inetsw walk.
> - ax25_create rewrites protocol=0 and protocol=PF_AX25 to AX25_P_TEXT
>   for SOCK_DGRAM and SOCK_SEQPACKET.
> - pn_socket_create rewrites protocol=0 to PN_PROTO_PHONET (SOCK_DGRAM)
>   or PN_PROTO_PIPE (SOCK_SEQPACKET).
> - vsock_create accepts protocol=0 or PF_VSOCK and stores sk_protocol=0
>   for both, so PF_VSOCK aliases protocol=0.
> - Several families (AF_PACKET, AF_KEY, AF_APPLETALK, AF_ATMPVC,
>   AF_ATMSVC, AF_LLC, AF_CAN raw/bcm, AF_RXRPC, AF_IEEE802154,
>   AF_QIPCRTR) accept the user protocol but never write sk_protocol, so
>   the kernel stores 0 regardless of input.
> - AF_CAN + SOCK_DGRAM is asymmetric: bcm and raw leave sk_protocol=0,
>   but j1939_sk_init writes sk_protocol = CAN_J1939.

Relevant remark on the side:

With the socket options SO_DOMAIN, SO_TYPE and SO_PROTOCOL, the
canonicalized sockets have become exposed to userspace.  So these are
not purely kernel-private mappings, but this canonicalization is
exposed to userspace (which should presumably make the mappings
stable).

I hope that the assumption is correct that the mappings for the
existing (f, t, p) combinations *are* stable?  If these mappings were
permitted to change for existing (f, t, p) combinations, I suspect it
might be possible to construct a scenario where a landlocked program
that used to work stops working after a kernel update.  That would be
a worse backwards compatibility issue than when getsockopt() returns a
new value for one of SO_{DOMAIN,TYPE,PROTOCOL}.


> Without Landlock-side canonicalization, a rule inserted with the user-
> facing form (e.g., AF_INET + SOCK_STREAM + 0) silently misses sockets
> the user wants to match: a socket created with protocol=0 reaches
> sk_protocol=IPPROTO_TCP, so a rule keyed on 0 does not apply.  Each
> socket call form would require the user to write a separate rule.
> 
> Store rules in the canonical form at insertion.  The new
> landlock_canon_map[AF_MAX][SOCK_MAX] table with flag-driven entries
> (_LANDLOCK_CANON_REWRITE_FAMILY, _LANDLOCK_CANON_REWRITE_TYPE,
> _LANDLOCK_CANON_PROTOCOL_ZERO, _LANDLOCK_CANON_PROTOCOL_FAMILY_ID,
> _LANDLOCK_CANON_PROTOCOL_ALWAYS, _LANDLOCK_CANON_PROTOCOL_PRESERVE)
> encodes the rewrites.  landlock_canonicalize_socket_key applies them
> idempotently; wildcards (TYPE_ALL, PROTOCOL_ALL) are preserved so
> wildcard rules remain first-class.  Per-protocol overrides (the
> preserve_protocol field) handle asymmetries like AF_CAN + SOCK_DGRAM
> where one sub-protocol writes sk_protocol differently from the others.
> Lookup remains O(1): the override is part of the same cell the array
> index returns.
> 
> Keep enforcement at security_socket_create (pre-create) so Landlock
> denies unauthorized triples before the kernel loads any family-specific
> module or allocates a socket.  This preserves the EACCES error path for
> triples the kernel itself would reject (AF_UNSPEC, invalid family and
> type pairs) rather than leaking EAFNOSUPPORT, ESOCKTNOSUPPORT, or
> EPROTONOSUPPORT as a sandbox bypass signal.  The pre-create hook
> canonicalizes the caller input through the same map before the rule
> lookup so that a rule keyed on the user form matches at the hook.
> 
> Add security_socket_post_create purely for runtime drift detection. At
> post_create the family .create() has completed, so sk_family,
> sock->type, and sk_protocol are authoritative.  Per-field WARN_ONCE
> fires when landlock_canonicalize_socket_key disagrees with these values,
> identifying which axis (family, type, or protocol) drifted and the
> user-supplied triple.  This hook only runs for tasks sandboxed by a
> domain that handles LANDLOCK_ACCESS_SOCKET_CREATE, so non-sandboxed
> tasks pay no overhead.

Shouldn't this only be enabled under a suitable debug build config?

This is all supposed to be captured by the KUnit test already, right?
Or do you expect that there are differences that the KUnit test
doesn't cover?

> 
> Four layers guard against landlock_canon_map drift:
> 
> - static_assert(AF_MAX == N) and static_assert(SOCK_MAX == N) anchor the
>   dimensions at the current kernel ABI; a new AF_* or SOCK_* value
>   breaks the build and forces a map audit.
> - A new KUnit suite (landlock_socket) iterates every (family, type,
>   protocol) triple over a probe range that covers all family IDs
>   (0..NPROTO) plus IPPROTO_SCTP, 255, and 0xFFFF.  For triples the
>   kernel accepts, sock_create_kern is the oracle; canonicalization is
>   validated against the resulting sk_family, sock->type, and
>   sk_protocol.  Families known to be unsupported by the default
>   configuration are marked, and -EAFNOSUPPORT on those families is
>   ignored so additional sub-protocol configs can extend coverage without
>   per-arch .kunitconfig fragmentation.  A separate map-entry test covers
>   cells unreachable through sock_create_kern (currently AF_VSOCK +
>   SOCK_DGRAM, which requires CONFIG_VIRTIO_VSOCKETS).  A
>   wildcard-preservation test pins the TYPE_ALL and PROTOCOL_ALL
>   invariants.
> - The per-field runtime WARN_ONCE described above.
> - security/landlock/.kunitconfig enumerates the CONFIG entries required
>   to exercise every audited family so a family disabled by kernel config
>   causes a build-visible coverage loss rather than a silent KUnit skip.
> 
> The four-way wildcard grid lookup in hook_socket_create gates every
> pack_socket_key call on a successful return.  This incidentally fixes a
> latent bug in the original hook where the first pack failure was
> detected with == -EACCES (the helper only ever returns 0 or -EINVAL), so
> an out-of-range triple fed three subsequent check_socket_access calls
> with an uninitialized key.
> 
> The tcp_protocol.variant2 selftest previously asserted that a rule for
> IPPROTO_TCP did NOT match socket(AF_INET, SOCK_STREAM, 0).  Under
> canonicalization both forms alias by design, so the test is renamed to
> alias_equivalence and its body is updated to assert the aliasing (both
> call forms match irrespective of which form the rule was inserted with).
> Unrelated families remain restricted.
> 
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> ---
>  security/landlock/.kunitconfig                |  40 +
>  security/landlock/socket.c                    | 871 +++++++++++++++++-
>  .../testing/selftests/landlock/socket_test.c  |  38 +-
>  3 files changed, 910 insertions(+), 39 deletions(-)
> 
> diff --git a/security/landlock/.kunitconfig b/security/landlock/.kunitconfig
> index f9423f01ac5b..5aafd56e8ebd 100644
> --- a/security/landlock/.kunitconfig
> +++ b/security/landlock/.kunitconfig
> @@ -1,6 +1,46 @@
> +CONFIG_AF_RXRPC=y
> +CONFIG_ATALK=y
> +CONFIG_ATM=y
>  CONFIG_AUDIT=y
> +CONFIG_AX25=y
> +CONFIG_BT=y
> +CONFIG_CAIF=y
> +CONFIG_CAN=y
> +CONFIG_CAN_BCM=y
> +CONFIG_CRYPTO=y
> +CONFIG_CRYPTO_USER_API_AEAD=y
> +CONFIG_HAMRADIO=y
> +CONFIG_IEEE802154=y
> +CONFIG_IEEE802154_SOCKET=y
> +CONFIG_INET=y
> +CONFIG_INFINIBAND=y
> +CONFIG_IP_SCTP=y
> +CONFIG_IPV6=y
> +CONFIG_ISDN=y
>  CONFIG_KUNIT=y
> +CONFIG_LLC=y
> +CONFIG_LLC2=y
> +CONFIG_MCTP=y
> +CONFIG_MISDN=y
> +CONFIG_MPTCP=y
> +CONFIG_MPTCP_IPV6=y
>  CONFIG_NET=y
> +CONFIG_NET_KEY=y
> +CONFIG_NETDEVICES=y
> +CONFIG_NETROM=y
> +CONFIG_NFC=y
> +CONFIG_PACKET=y
> +CONFIG_PHONET=y
> +CONFIG_PPP=y
> +CONFIG_PPPOE=y
> +CONFIG_QRTR=y
> +CONFIG_RDS=y
> +CONFIG_ROSE=y
>  CONFIG_SECURITY=y
>  CONFIG_SECURITY_LANDLOCK=y
>  CONFIG_SECURITY_LANDLOCK_KUNIT_TEST=y
> +CONFIG_SMC=y
> +CONFIG_TIPC=y
> +CONFIG_UNIX=y
> +CONFIG_VSOCKETS=y
> +CONFIG_X25=y

Are there possible kernel configurations where the mapping changes in
incompatible ways?

Specifically, are there kernel configurations where there is a family,
type and protocol such that:

(a) the call to socket(f, t, p) yields a valid socket FD
(b) and the socket's mapping is still different to what Landlock says

It seems that for the CONFIG_* options which are enabling address
families, this should stay compatible because without these, step (a)
presumably yields an error.  Are there other CONFIG_* options which
are not about address families where this is not the case?


> diff --git a/security/landlock/socket.c b/security/landlock/socket.c
> index 6afd5a0ac6d7..ef48949fa7d3 100644
> --- a/security/landlock/socket.c
> +++ b/security/landlock/socket.c
> @@ -5,20 +5,385 @@
>   * Copyright © 2025 Huawei Tech. Co., Ltd.
>   */
>  
> +#include <linux/in.h>
>  #include <linux/net.h>
>  #include <linux/socket.h>
>  #include <linux/stddef.h>
>  #include <net/ipv6.h>
> +#include <net/sock.h>
>  
>  #include "audit.h"
> +#include "cred.h"
>  #include "limits.h"
>  #include "ruleset.h"
>  #include "socket.h"
> -#include "cred.h"
>  
>  #define TYPE_ALL (-1)
>  #define PROTOCOL_ALL (-1)
>  
> +/*
> + * Compensation for kernel-internal socket rewrites.
> + *
> + * The kernel maps the user-visible (family, type, protocol) triple into
> + * (sk->sk_family, sock->type, sk->sk_protocol) following per-family rules.
> + * Landlock mirrors those rules at rule insertion and at hook time so that rules
> + * inserted with a user-facing form match the canonical triple seen after
> + * socket(2) completes.  The known patterns are:
> + *
> + *   - __sock_create rewrites AF_INET + SOCK_PACKET to AF_PACKET for
> + *     pre-2.2 compatibility (net/socket.c).
> + *   - unix_create rewrites AF_UNIX + SOCK_RAW to AF_UNIX + SOCK_DGRAM and
> + *     ignores the user protocol (sk_protocol stays 0).
> + *   - inet_create and inet6_create resolve protocol=0 to a type-specific
> + *     default (IPPROTO_TCP for SOCK_STREAM, IPPROTO_UDP for SOCK_DGRAM,
> + *     IPPROTO_SCTP for SOCK_SEQPACKET) via the inetsw walk.
> + *   - ax25_create rewrites protocol=0 or PF_AX25 to AX25_P_TEXT for
> + *     SOCK_DGRAM and SOCK_SEQPACKET.
> + *   - pn_socket_create rewrites protocol=0 to PN_PROTO_PHONET for
> + *     SOCK_DGRAM and PN_PROTO_PIPE for SOCK_SEQPACKET.
> + *   - vsock_create accepts either 0 or PF_VSOCK and stores sk_protocol=0
> + *     for both; Landlock canonicalizes the rule to protocol=0.
> + *
> + * Enforcement happens at security_socket_create (pre-create) so Landlock denies
> + * unauthorized triples before any family-specific module is loaded or socket
> + * allocated, preserving the EACCES error path for triples the kernel itself
> + * would reject.
> + *
> + * Drift between landlock_canon_map and kernel behavior is detected in four
> + * layers: static_asserts on AF_MAX and SOCK_MAX (new AF or SOCK values break
> + * the build), the exhaustive KUnit suite in this file using sock_create_kern as
> + * the oracle, a runtime WARN_ON_ONCE in the post_create hook, and an explicit
> + * CONFIG list in .kunitconfig so a missing family fails the build instead of
> + * silently skipping.
> + */
> +
> +#define _LANDLOCK_CANON_REWRITE_FAMILY BIT(0)
> +#define _LANDLOCK_CANON_REWRITE_TYPE BIT(1)
> +#define _LANDLOCK_CANON_PROTOCOL_ZERO BIT(2)
> +#define _LANDLOCK_CANON_PROTOCOL_FAMILY_ID BIT(3)
> +#define _LANDLOCK_CANON_PROTOCOL_ALWAYS BIT(4)
> +#define _LANDLOCK_CANON_PROTOCOL_PRESERVE BIT(5)

Naming nits:
 * _LANDLOCK_CANON_PROTOCOL_ALWAYS could maybe be
   _LANDLOCK_CANON_REWRITE_PROTOCOL, for symmetry with *REWRITE_FAMILY
   and *REWRITE_TYPE.
 * similarly, _LANDLOCK_CANON_PROTOCOL_ZERO =>
   _LANDLOCK_CANON_REWRITE_PROTOCOL_IF_ZERO?

At the higher abstraction level in this implementation:

These enum values, the struct landlock_canon_entry and its
interpretation in landlock_canonicalize_socket_key() feels a bit heavy
handed for the mapping, and when reading it, I had to jump between the
table, the struct, the enums and the canonicalization function.

I assume that you have attempted to express that table as a big switch
statement with normal `if` conditions where these preconditions are
expressed more directly?  Was that code worse?


> +
> +/*
> + * All fields fit in u8 today: ops uses 6 bits; SOCK_MAX-1 and AF_MAX-1 are both
> + * small; the largest canonicalization target is AX25_P_TEXT (0xF0), well within
> + * u8.  -Woverflow catches a too-wide initializer at build time if a future
> + * entry exceeds the field width.
> + */
> +struct landlock_canon_entry {
> +	u8 ops;
> +	u8 new_type;
> +	u8 new_family;
> +	u8 new_protocol;
> +	/*
> +	 * When PROTOCOL_PRESERVE is set and the user protocol matches this
> +	 * value, the protocol field is left unchanged regardless of the other
> +	 * PROTOCOL_* flags.  Captures sub-protocols whose .create() writes
> +	 * sk_protocol explicitly (e.g. CAN_J1939) when other sub-protocols of
> +	 * the same (family, type) do not.  Lookup stays O(1) because the
> +	 * override is part of the same cell.
> +	 */
> +	u8 preserve_protocol;
> +};
> +
> +/*
> + * Shared initializer for Case 4 families (AF_UNIX, AF_PACKET, AF_VSOCK, AF_KEY,
> + * ...) whose .create() ignores the user protocol and leaves sk_protocol at 0.
> + * The canonical form is any type, protocol=0.
> + */
> +#define _LANDLOCK_CANON_ALWAYS_ZERO                                        \
> +	{                                                                  \
> +		.ops = _LANDLOCK_CANON_PROTOCOL_ALWAYS, .new_protocol = 0, \
> +	}
> +
> +/*
> + * A change to AF_MAX or SOCK_MAX implies a new protocol family or socket type
> + * reached upstream.  The assertion fires a build error that forces an audit:
> + * does the new family or type introduce a kernel-internal rewrite that Landlock
> + * must mirror?  If not, bump the expected value below.
> + */
> +static_assert(AF_MAX == 46,
> +	      "AF_MAX changed; audit landlock_canon_map for new families.");
> +static_assert(SOCK_MAX == 11,
> +	      "SOCK_MAX changed; audit landlock_canon_map for new types.");
> +
> +static const struct landlock_canon_entry
> +	landlock_canon_map[AF_MAX][SOCK_MAX] = {
> +		/*
> +		 * unix_create rewrites SOCK_RAW to SOCK_DGRAM and ignores the
> +		 * user protocol; normalize every AF_UNIX socket to protocol=0.
> +		 */
> +		[AF_UNIX] = {
> +			[SOCK_STREAM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_DGRAM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_SEQPACKET] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_RAW] = {
> +				.ops = _LANDLOCK_CANON_REWRITE_TYPE |
> +				       _LANDLOCK_CANON_PROTOCOL_ALWAYS,
> +				.new_type = SOCK_DGRAM,
> +				.new_protocol = 0,
> +			},
> +		},
> +		[AF_INET] = {
> +			[SOCK_STREAM] = {
> +				.ops = _LANDLOCK_CANON_PROTOCOL_ZERO,
> +				.new_protocol = IPPROTO_TCP,
> +			},
> +			[SOCK_DGRAM] = {
> +				.ops = _LANDLOCK_CANON_PROTOCOL_ZERO,
> +				.new_protocol = IPPROTO_UDP,
> +			},
> +			[SOCK_SEQPACKET] = {
> +				.ops = _LANDLOCK_CANON_PROTOCOL_ZERO,
> +				.new_protocol = IPPROTO_SCTP,
> +			},
> +			/*
> +			 * __sock_create rewrites AF_INET + SOCK_PACKET to
> +			 * AF_PACKET; packet_create then stores sk_protocol=0
> +			 * regardless of the user protocol (see AF_PACKET rows
> +			 * below), so the canonical triple is (AF_PACKET,
> +			 * SOCK_PACKET, 0).
> +			 */
> +			[SOCK_PACKET] = {
> +				.ops = _LANDLOCK_CANON_REWRITE_FAMILY |
> +				       _LANDLOCK_CANON_PROTOCOL_ALWAYS,
> +				.new_family = AF_PACKET,
> +				.new_protocol = 0,
> +			},
> +		},
> +		/*
> +		 * packet_create stores the user-provided protocol in po->num
> +		 * (for filtering) but never writes sk_protocol, so sk_protocol
> +		 * stays at 0 for every AF_PACKET socket.  Canonicalize rules to
> +		 * protocol=0 to match.
> +		 */
> +		[AF_PACKET] = {
> +			[SOCK_DGRAM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_RAW] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_PACKET] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +		},
> +		/*
> +		 * AX.25 rewrites protocol=0 AND PF_AX25 to AX25_P_TEXT (0xF0)
> +		 * for SOCK_DGRAM and SOCK_SEQPACKET; see ax25_create in
> +		 * net/ax25/af_ax25.c.  SOCK_RAW preserves user input.
> +		 */
> +		[AF_AX25] = {
> +			[SOCK_DGRAM] = {
> +				.ops = _LANDLOCK_CANON_PROTOCOL_ZERO |
> +				       _LANDLOCK_CANON_PROTOCOL_FAMILY_ID,
> +				.new_protocol = 0xF0, /* AX25_P_TEXT */
> +			},
> +			[SOCK_SEQPACKET] = {
> +				.ops = _LANDLOCK_CANON_PROTOCOL_ZERO |
> +				       _LANDLOCK_CANON_PROTOCOL_FAMILY_ID,
> +				.new_protocol = 0xF0, /* AX25_P_TEXT */
> +			},
> +		},
> +		[AF_INET6] = {
> +			[SOCK_STREAM] = {
> +				.ops = _LANDLOCK_CANON_PROTOCOL_ZERO,
> +				.new_protocol = IPPROTO_TCP,
> +			},
> +			[SOCK_DGRAM] = {
> +				.ops = _LANDLOCK_CANON_PROTOCOL_ZERO,
> +				.new_protocol = IPPROTO_UDP,
> +			},
> +			[SOCK_SEQPACKET] = {
> +				.ops = _LANDLOCK_CANON_PROTOCOL_ZERO,
> +				.new_protocol = IPPROTO_SCTP,
> +			},
> +		},
> +		/*
> +		 * Phonet rewrites protocol=0 to PN_PROTO_PHONET for SOCK_DGRAM
> +		 * and PN_PROTO_PIPE for SOCK_SEQPACKET; see pn_socket_create in
> +		 * net/phonet/af_phonet.c.
> +		 */
> +		[AF_PHONET] = {
> +			[SOCK_DGRAM] = {
> +				.ops = _LANDLOCK_CANON_PROTOCOL_ZERO,
> +				.new_protocol = 1, /* PN_PROTO_PHONET */
> +			},
> +			[SOCK_SEQPACKET] = {
> +				.ops = _LANDLOCK_CANON_PROTOCOL_ZERO,
> +				.new_protocol = 2, /* PN_PROTO_PIPE */
> +			},
> +		},
> +		/*
> +		 * atalk_create accepts SOCK_DGRAM and SOCK_RAW but never writes
> +		 * sk_protocol.
> +		 */
> +		[AF_APPLETALK] = {
> +			[SOCK_DGRAM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_RAW] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +		},
> +		/*
> +		 * ATM PVC and SVC go through vcc_create, which rejects
> +		 * SOCK_STREAM and accepts every other type in the
> +		 * 0..SOCK_PACKET range without writing sk_protocol.
> +		 */
> +		[AF_ATMPVC] = {
> +			[0] = _LANDLOCK_CANON_ALWAYS_ZERO, /* reserved hole */
> +			[SOCK_DGRAM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_RAW] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_RDM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_SEQPACKET] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_DCCP] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[7] = _LANDLOCK_CANON_ALWAYS_ZERO, /* reserved hole */
> +			[8] = _LANDLOCK_CANON_ALWAYS_ZERO, /* reserved hole */
> +			[9] = _LANDLOCK_CANON_ALWAYS_ZERO, /* reserved hole */
> +			[SOCK_PACKET] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +		},
> +		[AF_ATMSVC] = {
> +			[0] = _LANDLOCK_CANON_ALWAYS_ZERO, /* reserved hole */
> +			[SOCK_DGRAM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_RAW] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_RDM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_SEQPACKET] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_DCCP] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[7] = _LANDLOCK_CANON_ALWAYS_ZERO, /* reserved hole */
> +			[8] = _LANDLOCK_CANON_ALWAYS_ZERO, /* reserved hole */
> +			[9] = _LANDLOCK_CANON_ALWAYS_ZERO, /* reserved hole */
> +			[SOCK_PACKET] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +		},
> +		/*
> +		 * LLC, CAN, RxRPC, IEEE 802.15.4, and QRTR all rely on
> +		 * sock_init_data to zero sk_protocol and never override it, so
> +		 * any reachable triple is canonicalized to protocol=0.
> +		 */
> +		[AF_LLC] = {
> +			[SOCK_STREAM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_DGRAM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +		},
> +		/*
> +		 * AF_CAN dispatches by protocol: can_raw and can_bcm leave
> +		 * sk_protocol=0, but j1939_sk_init writes sk_protocol =
> +		 * CAN_J1939.  Use PROTOCOL_PRESERVE on SOCK_DGRAM so the J1939
> +		 * value survives the canonicalization while all other
> +		 * sub-protocols get normalized to 0.  SOCK_SEQPACKET has no
> +		 * registered CAN sub-protocol so no entry is needed (the cell
> +		 * is unreachable at the kernel).
> +		 */
> +		[AF_CAN] = {
> +			[SOCK_DGRAM] = {
> +				.ops = _LANDLOCK_CANON_PROTOCOL_ALWAYS |
> +				       _LANDLOCK_CANON_PROTOCOL_PRESERVE,
> +				.new_protocol = 0,
> +				.preserve_protocol =
> +					7 /* CAN_J1939 */,
> +			},
> +			[SOCK_RAW] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +		},
> +		[AF_RXRPC] = {
> +			[SOCK_DGRAM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +		},
> +		[AF_IEEE802154] = {
> +			[SOCK_RAW] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_DGRAM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +		},
> +		[AF_QIPCRTR] = {
> +			[SOCK_DGRAM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +		},
> +		/*
> +		 * pfkey_create accepts only PF_KEY_V2 for SOCK_RAW but never
> +		 * writes sk_protocol, so the kernel stores sk_protocol=0.
> +		 */
> +		[AF_KEY] = {
> +			[SOCK_RAW] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +		},
> +		/*
> +		 * vsock accepts protocol=0 or PF_VSOCK and leaves sk_protocol
> +		 * at 0 in either case, for every transport (loopback, VMCI,
> +		 * virtio, hyperv) because vsock_create never writes
> +		 * sk_protocol.  Canonicalize every AF_VSOCK rule to protocol=0
> +		 * so both call forms match the same rule.
> +		 *
> +		 * The vsock loopback transport (the only one enabled by the
> +		 * default .kunitconfig) does not advertise DGRAM capability, so
> +		 * the SOCK_DGRAM cell is not reachable through sock_create_kern
> +		 * at KUnit time; DGRAM reachability requires
> +		 * CONFIG_VIRTIO_VSOCKETS=y.  The canonicalization is
> +		 * nonetheless correct; a dedicated map-entry test case
> +		 * validates it independently of any kernel configuration.
> +		 */
> +		[AF_VSOCK] = {
> +			[SOCK_STREAM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_DGRAM] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +			[SOCK_SEQPACKET] = _LANDLOCK_CANON_ALWAYS_ZERO,
> +		},
> +	};

Some impressive code archeology here! :)


> +
> +/*
> + * landlock_canonicalize_socket_key - Apply kernel-equivalent rewrites
> + *
> + * @family: in/out protocol family.
> + * @type: in/out socket type.
> + * @protocol: in/out protocol.
> + *
> + * Transforms a (@family, @type, @protocol) triple into the form the kernel
> + * stores after the family .create() completes.  Wildcards (TYPE_ALL,
> + * PROTOCOL_ALL) are preserved unchanged since they do not pin a specific
> + * triple.  Out-of-range values are left for the caller (pack_socket_key) to
> + * reject.
> + */
> +static void landlock_canonicalize_socket_key(s32 *family, s32 *type,
> +					     s32 *protocol)
> +{
> +	const struct landlock_canon_entry *entry;
> +	s32 input_family;
> +
> +	/*
> +	 * Type is the map second index, so it must be concrete.  Family is the
> +	 * map first index; checked below via bounds.
> +	 */
> +	if (*type == TYPE_ALL)
> +		return;
> +
> +	if (*family < 0 || *family >= AF_MAX || *type < 0 || *type >= SOCK_MAX)
> +		return;
> +
> +	entry = &landlock_canon_map[*family][*type];
> +	input_family = *family;
> +
> +	/*
> +	 * Family and type rewrites apply regardless of protocol value; a
> +	 * PROTOCOL_ALL rule still benefits from an alias family or type
> +	 * rewrite.
> +	 */

If I understand correctly, this remark is crucial for the correctness;
if the API allows PROTOCOL_ALL, then the rewrite results must be
independent of the protocol, so that we can still rewrite the family
and type correctly in these cases.

But if I understand correctly, if the API allows TYPE_ALL, a rule with
TYPE_ALL can *not* be canonicalized correctly in all cases, and that
would be another reason why the "type" wildcard is problematic?

It's an obscure example, but a rule could then allow (AF_INET,
TYPE_ALL, PROTOCOL_ALL).  When a process now invokes socket(AF_INET,
SOCK_PACKET, 0), that invocation *looks like it should match the
rule*, but the existing socket(2) logic rewrites that socket to a
(AF_PACKET, SOCK_PACKET, 0), which does not match the rule.

Generalized to the KUnit test, I think a very general test that would
catch this would need to look a bit like the
test_family_canonicalization test, but it would also have to try the
canonicalization with the permitted wildcard combinations and see that
the resulting rule still *matches* the socket that was created.

> +	if (entry->ops & _LANDLOCK_CANON_REWRITE_FAMILY)
> +		*family = entry->new_family;
> +	if (entry->ops & _LANDLOCK_CANON_REWRITE_TYPE)
> +		*type = entry->new_type;
> +
> +	/*
> +	 * Protocol rewrites are skipped for wildcard protocols so that a
> +	 * PROTOCOL_ALL rule stays wildcard even for families the kernel
> +	 * canonicalizes.
> +	 */
> +	if (*protocol == PROTOCOL_ALL)
> +		return;
> +
> +	/*
> +	 * Per-protocol preservation: when the map declares that one specific
> +	 * user protocol survives unchanged in sk_protocol (e.g., AF_CAN +
> +	 * SOCK_DGRAM + CAN_J1939: j1939_sk_init writes sk_protocol = CAN_J1939,
> +	 * while raw_init and bcm_init leave sk_protocol = 0), skip all protocol
> +	 * rewrites when the user input matches that value.  Keeps the runtime
> +	 * drift WARN silent for the exception while PROTOCOL_ALWAYS still
> +	 * normalizes the rest of the cell to new_protocol.
> +	 */
> +	if ((entry->ops & _LANDLOCK_CANON_PROTOCOL_PRESERVE) &&
> +	    *protocol == entry->preserve_protocol)
> +		return;
> +
> +	if (entry->ops & _LANDLOCK_CANON_PROTOCOL_ALWAYS)
> +		*protocol = entry->new_protocol;
> +	else if (((entry->ops & _LANDLOCK_CANON_PROTOCOL_ZERO) &&
> +		  *protocol == 0) ||
> +		 ((entry->ops & _LANDLOCK_CANON_PROTOCOL_FAMILY_ID) &&
> +		  *protocol == input_family))
> +		*protocol = entry->new_protocol;
> +}
> +
>  static int pack_socket_key(const s32 family, const s32 type, const s32 protocol,
>  			   uintptr_t *val)
>  {
> @@ -78,12 +443,12 @@ int landlock_append_socket_rule(struct landlock_ruleset *const ruleset,
>  {
>  	int err;
>  	uintptr_t key;
> +
>  	/*
> -	 * (AF_INET, SOCK_PACKET) is an alias for (AF_PACKET, SOCK_PACKET)
> -	 * (cf. __sock_create).
> +	 * Apply the kernel rewrites so that this rule matches the triple seen
> +	 * at hook time.  See landlock_canon_map.
>  	 */
> -	if (family == AF_INET && type == SOCK_PACKET)
> -		family = AF_PACKET;
> +	landlock_canonicalize_socket_key(&family, &type, &protocol);
>  
>  	err = pack_socket_key(family, type, protocol, &key);
>  	if (err)
> @@ -123,6 +488,12 @@ static int check_socket_access(const struct landlock_ruleset *dom,
>  	return -EACCES;
>  }
>  
> +/*
> + * Enforcement happens at security_socket_create (pre-create) so Landlock denies
> + * unauthorized triples before the kernel loads any family-specific module or
> + * allocates a socket.  This preserves the EACCES error path even for triples
> + * the kernel itself would reject (AF_UNSPEC, invalid family and type pairs).
> + */
>  static int hook_socket_create(int family, int type, int protocol, int kern)
>  {
>  	layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_SOCKET] = {};
> @@ -133,45 +504,52 @@ static int hook_socket_create(int family, int type, int protocol, int kern)
>  	const struct landlock_cred_security *const subject =
>  		landlock_get_applicable_subject(current_cred(), masks, NULL);
>  	uintptr_t key;
> +	s32 canon_family = family, canon_type = type, canon_protocol = protocol;
>  	struct lsm_socket_audit audit_socket = {
>  		.family = family,
>  		.type = type,
>  		.protocol = protocol,
>  	};
>  
> -	if (!subject)
> -		return 0;
> -	/* Checks only user space sockets. */
> +	/* Kernel-internal sockets bypass (accept, sock_create_lite, ...). */
>  	if (kern)
>  		return 0;
> +	if (!subject)
> +		return 0;
> +
> +	/* Canonicalize the user-facing triple to match the rule storage. */
> +	landlock_canonicalize_socket_key(&canon_family, &canon_type,
> +					 &canon_protocol);
>  
>  	handled_access = landlock_init_layer_masks(
>  		subject->domain, LANDLOCK_ACCESS_SOCKET_CREATE, &layer_masks,
>  		LANDLOCK_KEY_SOCKET);
> +
>  	/*
> -	 * Error could happen due to parameters are outside of the allowed range,
> -	 * so this combination couldn't be added in ruleset previously.
> -	 * Therefore, it's not permitted.
> +	 * Four-way wildcard grid lookup.  Each pack_socket_key call is gated on
> +	 * its return value so a rejected triple does not feed an uninitialized
> +	 * key into check_socket_access.
>  	 */
> -	if (pack_socket_key(family, type, protocol, &key) == -EACCES)
> -		return -EACCES;
> -	if (check_socket_access(subject->domain, key, &layer_masks,
> +	if (pack_socket_key(canon_family, canon_type, canon_protocol, &key) ==
> +		    0 &&
> +	    check_socket_access(subject->domain, key, &layer_masks,
>  				handled_access) == 0)
>  		return 0;
>  
> -	/* Ranges were already checked. */
> -	(void)pack_socket_key(family, TYPE_ALL, protocol, &key);
> -	if (check_socket_access(subject->domain, key, &layer_masks,
> +	if (pack_socket_key(canon_family, TYPE_ALL, canon_protocol, &key) ==
> +		    0 &&
> +	    check_socket_access(subject->domain, key, &layer_masks,
>  				handled_access) == 0)
>  		return 0;
>  
> -	(void)pack_socket_key(family, type, PROTOCOL_ALL, &key);
> -	if (check_socket_access(subject->domain, key, &layer_masks,
> +	if (pack_socket_key(canon_family, canon_type, PROTOCOL_ALL, &key) ==
> +		    0 &&
> +	    check_socket_access(subject->domain, key, &layer_masks,
>  				handled_access) == 0)
>  		return 0;
>  
> -	(void)pack_socket_key(family, TYPE_ALL, PROTOCOL_ALL, &key);
> -	if (check_socket_access(subject->domain, key, &layer_masks,
> +	if (pack_socket_key(canon_family, TYPE_ALL, PROTOCOL_ALL, &key) == 0 &&
> +	    check_socket_access(subject->domain, key, &layer_masks,
>  				handled_access) == 0)
>  		return 0;
>  
> @@ -187,8 +565,64 @@ static int hook_socket_create(int family, int type, int protocol, int kern)
>  	return -EACCES;
>  }
>  
> +/*
> + * Runtime drift detection: at post_create the family .create() has completed,
> + * so sk_family, sock->type and sk_protocol are authoritative.  Compare them
> + * against what landlock_canonicalize_socket_key produces from the user input; a
> + * mismatch means a kernel canonicalization is not mirrored in
> + * landlock_canon_map.  Enforcement already happened at socket_create, so this
> + * hook always returns 0.
> + *
> + * This hook only runs for tasks sandboxed by a Landlock domain that handles
> + * LANDLOCK_ACCESS_SOCKET_CREATE, so non-sandboxed tasks pay no overhead.
> + * Sandboxed tasks still cover enough of the reachable (family, type, protocol)
> + * space over the lifetime of a typical workload to surface drift in the field.
> + */
> +static int hook_socket_post_create(struct socket *sock, int family, int type,
> +				   int protocol, int kern)
> +{
> +	const struct access_masks masks = {
> +		.socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> +	};
> +	s32 canon_family = family, canon_type = type, canon_protocol = protocol;
> +
> +	if (kern)
> +		return 0;
> +	if (!landlock_get_applicable_subject(current_cred(), masks, NULL))
> +		return 0;
> +
> +	/*
> +	 * Kernel-resolved values are authoritative; no fallback to the user
> +	 * protocol argument.  Families that ignore the user protocol (e.g.,
> +	 * AF_UNIX, AF_VSOCK) are represented in landlock_canon_map with
> +	 * PROTOCOL_ALWAYS so the canonicalization below yields the same value
> +	 * for both rule insertion and this comparison.
> +	 *
> +	 * Drift is reported per field so a reviewer knows which axis disagrees
> +	 * with the kernel without having to diff three values.  WARN_ONCE
> +	 * rather than WARN_ON_ONCE keeps the stack trace but swallows
> +	 * subsequent identical drifts in the same boot.
> +	 */
> +	landlock_canonicalize_socket_key(&canon_family, &canon_type,
> +					 &canon_protocol);
> +	WARN_ONCE(
> +		canon_family != sock->sk->sk_family,
> +		"Landlock canon family drift: canon=%d kernel=%d (user family=%d type=%d protocol=%d)\n",
> +		canon_family, sock->sk->sk_family, family, type, protocol);
> +	WARN_ONCE(
> +		canon_type != sock->type,
> +		"Landlock canon type drift: canon=%d kernel=%d (user family=%d type=%d protocol=%d)\n",
> +		canon_type, sock->type, family, type, protocol);
> +	WARN_ONCE(
> +		canon_protocol != sock->sk->sk_protocol,
> +		"Landlock canon protocol drift: canon=%d kernel=%d (user family=%d type=%d protocol=%d)\n",
> +		canon_protocol, sock->sk->sk_protocol, family, type, protocol);
> +	return 0;
> +}
> +
>  static struct security_hook_list landlock_hooks[] __ro_after_init = {
>  	LSM_HOOK_INIT(socket_create, hook_socket_create),
> +	LSM_HOOK_INIT(socket_post_create, hook_socket_post_create),
>  };
>  
>  __init void landlock_add_socket_hooks(void)
> @@ -196,3 +630,398 @@ __init void landlock_add_socket_hooks(void)
>  	security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
>  			   &landlock_lsmid);
>  }
> +
> +#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
> +
> +#include <kunit/test.h>
> +#include <net/net_namespace.h>
> +
> +/*
> + * Per-family parameterized coverage test.  One subtest per AF_*; each subtest
> + * iterates every (type, protocol) probe through sock_create_kern and checks
> + * either that landlock_canonicalize_socket_key matches the kernel-resolved
> + * triple (for reachable families) or that the call fails with the expected
> + * errno (for unsupported families).  No subtest is skipped: every family
> + * produces PASS or FAIL, so an unsupported entry that quietly becomes stale is
> + * caught immediately.
> + *
> + * Every unsupported family entry expects sock_create_kern to fail with
> + * -EAFNOSUPPORT because no handler is registered for that family in the KUnit
> + * build environment (either the family was never implemented, was removed from
> + * Linux, is a reserved pseudo family, or requires an arch or CONFIG not
> + * satisfied by .kunitconfig).  The family name in the TAP subtest header,
> + * combined with this errno, identifies the case unambiguously.
> + */
> +struct landlock_canon_family_case {
> +	int af;
> +	const char *name;
> +	/*
> +	 * True means the family must be supported in the KUnit environment and
> +	 * canonicalization is exercised against the kernel.  False means every
> +	 * probe must fail with -EAFNOSUPPORT.
> +	 */
> +	bool supported;
> +};
> +
> +#define _LANDLOCK_AF_SUPPORTED(f)                         \
> +	{                                                 \
> +		.af = (f), .name = #f, .supported = true, \
> +	}
> +
> +#define _LANDLOCK_AF_UNSUPPORTED(f)                        \
> +	{                                                  \
> +		.af = (f), .name = #f, .supported = false, \
> +	}
> +
> +static const struct landlock_canon_family_case landlock_canon_families[] = {
> +	/* kernel rejects socket(AF_UNSPEC) */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_UNSPEC),
> +	_LANDLOCK_AF_SUPPORTED(AF_UNIX),
> +	_LANDLOCK_AF_SUPPORTED(AF_INET),
> +	_LANDLOCK_AF_SUPPORTED(AF_AX25),
> +	/* AF_IPX removed from Linux */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_IPX),
> +	_LANDLOCK_AF_SUPPORTED(AF_APPLETALK),
> +	_LANDLOCK_AF_SUPPORTED(AF_NETROM),
> +	/* AF_BRIDGE cannot be used to create sockets */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_BRIDGE),
> +	_LANDLOCK_AF_SUPPORTED(AF_ATMPVC),
> +	_LANDLOCK_AF_SUPPORTED(AF_X25),
> +	_LANDLOCK_AF_SUPPORTED(AF_INET6),
> +	_LANDLOCK_AF_SUPPORTED(AF_ROSE),
> +	/* AF_DECnet removed from Linux */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_DECnet),
> +	/* AF_NETBEUI not implemented */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_NETBEUI),
> +	/* AF_SECURITY is a pseudo family */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_SECURITY),
> +	_LANDLOCK_AF_SUPPORTED(AF_KEY),
> +	_LANDLOCK_AF_SUPPORTED(AF_NETLINK),
> +	_LANDLOCK_AF_SUPPORTED(AF_PACKET),
> +	/* AF_ASH not implemented */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_ASH),
> +	/* AF_ECONET removed from Linux */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_ECONET),
> +	_LANDLOCK_AF_SUPPORTED(AF_ATMSVC),
> +	_LANDLOCK_AF_SUPPORTED(AF_RDS),
> +	/* AF_SNA not implemented */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_SNA),
> +	/* AF_IRDA removed from Linux */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_IRDA),
> +	_LANDLOCK_AF_SUPPORTED(AF_PPPOX),
> +	/* AF_WANPIPE not implemented */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_WANPIPE),
> +	_LANDLOCK_AF_SUPPORTED(AF_LLC),
> +	/* AF_IB reserved by infiniband */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_IB),
> +	/* AF_MPLS cannot be used to create sockets */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_MPLS),
> +	_LANDLOCK_AF_SUPPORTED(AF_CAN),
> +	_LANDLOCK_AF_SUPPORTED(AF_TIPC),
> +	_LANDLOCK_AF_SUPPORTED(AF_BLUETOOTH),
> +	/* AF_IUCV only on s390 */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_IUCV),
> +	_LANDLOCK_AF_SUPPORTED(AF_RXRPC),
> +	_LANDLOCK_AF_SUPPORTED(AF_ISDN),
> +	_LANDLOCK_AF_SUPPORTED(AF_PHONET),
> +	_LANDLOCK_AF_SUPPORTED(AF_IEEE802154),
> +	_LANDLOCK_AF_SUPPORTED(AF_CAIF),
> +	_LANDLOCK_AF_SUPPORTED(AF_ALG),
> +	_LANDLOCK_AF_SUPPORTED(AF_NFC),
> +	_LANDLOCK_AF_SUPPORTED(AF_VSOCK),
> +	/* AF_KCM requires a kcm multiplexer setup */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_KCM),
> +	_LANDLOCK_AF_SUPPORTED(AF_QIPCRTR),
> +	_LANDLOCK_AF_SUPPORTED(AF_SMC),
> +	/* AF_XDP requires a netdev with XDP support */
> +	_LANDLOCK_AF_UNSUPPORTED(AF_XDP),
> +	_LANDLOCK_AF_SUPPORTED(AF_MCTP),
> +};
> +
> +static_assert(ARRAY_SIZE(landlock_canon_families) == AF_MAX,
> +	      "landlock_canon_families size must track AF_MAX.");
> +
> +static void
> +landlock_canon_family_to_desc(const struct landlock_canon_family_case *c,
> +			      char *desc)
> +{
> +	strscpy(desc, c->name, KUNIT_PARAM_DESC_SIZE);
> +}
> +
> +KUNIT_ARRAY_PARAM(landlock_canon_family, landlock_canon_families,
> +		  landlock_canon_family_to_desc);
> +
> +/*
> + * Edge protocol sentinels beyond the 0..NPROTO range exercised by the main
> + * sweep.  IPPROTO_SCTP is the only value in landlock_canon_map above NPROTO
> + * today; 255 = IPPROTO_RAW covers the top of the IP protocol byte; 0xFFFF = u16
> + * upper bound covers the ethertype-wide range accepted by AF_PACKET and
> + * saturates pack_socket_key's u16 field.
> + */
> +static const int landlock_edge_protocols[] = {
> +	IPPROTO_SCTP,
> +	255 /* IPPROTO_RAW */,
> +	0xFFFF /* u16 max */,
> +};
> +
> +/*
> + * Return the protocol value for probe index @p: indices 0..NPROTO cover every
> + * family-ID range (and NPROTO itself, one past the last valid PF_*, exercises
> + * an out-of-range probe); subsequent indices draw from landlock_edge_protocols.
> + * Returns -1 when the iteration is exhausted.
> + */
> +static int landlock_probe_protocol(size_t p)
> +{
> +	if (p <= NPROTO)
> +		return (int)p;
> +	if (p - (NPROTO + 1) < ARRAY_SIZE(landlock_edge_protocols))
> +		return landlock_edge_protocols[p - (NPROTO + 1)];
> +	return -1;
> +}
> +
> +static void test_family_canonicalization(struct kunit *const test)
> +{
> +	const struct landlock_canon_family_case *const c = test->param_value;
> +	unsigned int tested = 0, canonicalized = 0, confirmed_unsupported = 0;
> +	int type;
> +	size_t probe;
> +
> +	for (type = 0; type < SOCK_MAX; type++) {
> +		for (probe = 0;; probe++) {
> +			const int protocol = landlock_probe_protocol(probe);
> +			struct socket *sock = NULL;
> +			int ret, kernel_family, kernel_type, kernel_protocol;
> +			s32 landlock_family, landlock_type, landlock_protocol;
> +
> +			if (protocol < 0)
> +				break;
> +
> +			ret = sock_create_kern(&init_net, c->af, type, protocol,
> +					       &sock);
> +
> +			if (ret == 0) {
> +				/*
> +				 * Reached the kernel: fully verify Landlock
> +				 * canonicalization against the resolved
> +				 * sk_family / sock->type / sk_protocol triple.
> +				 * This path runs whether or not the family is
> +				 * listed as unsupported: if an extra CONFIG_*
> +				 * turns an unsupported family entry reachable,
> +				 * the test picks it up naturally.
> +				 */
> +				kernel_family = sock->sk->sk_family;
> +				kernel_type = sock->type;
> +				kernel_protocol = sock->sk->sk_protocol;
> +				sock_release(sock);
> +
> +				landlock_family = c->af;
> +				landlock_type = type;
> +				landlock_protocol = protocol;
> +				landlock_canonicalize_socket_key(
> +					&landlock_family, &landlock_type,
> +					&landlock_protocol);
> +
> +				/* Drift axis identified by the failing macro
> +				 * line; KUNIT_EXPECT_EQ_MSG already prints
> +				 * expected vs actual values.  The format
> +				 * "(F,T,P)" carries the user input that
> +				 * triggered the mismatch.
> +				 */
> +				KUNIT_EXPECT_EQ_MSG(test, landlock_family,
> +						    kernel_family,
> +						    "family (%d,%d,%d)", c->af,
> +						    type, protocol);
> +				KUNIT_EXPECT_EQ_MSG(test, landlock_type,
> +						    kernel_type,
> +						    "type (%d,%d,%d)", c->af,
> +						    type, protocol);
> +				KUNIT_EXPECT_EQ_MSG(test, landlock_protocol,
> +						    kernel_protocol,
> +						    "protocol (%d,%d,%d)",
> +						    c->af, type, protocol);
> +
> +				tested++;
> +				if (landlock_family != c->af ||
> +				    landlock_type != type ||
> +				    landlock_protocol != protocol)
> +					canonicalized++;
> +			} else if (ret == -EAFNOSUPPORT && !c->supported) {
> +				/*
> +				 * Ignore this specific combination when the
> +				 * kernel says the family is not registered AND
> +				 * the family is on the unsupported list.
> +				 * Rationale: the unsupported flag declares "we
> +				 * expect no kernel handler for this family in
> +				 * the KUnit build".  If extra CONFIG_* entries
> +				 * are added later (e.g. via --kconfig_add or a
> +				 * per-arch config layer), an unsupported family
> +				 * entry may start returning ret == 0 for some
> +				 * probes; those fall into the branch above and
> +				 * are fully tested.  No per-arch .kunitconfig
> +				 * fragmentation is required.
> +				 *
> +				 * Other negative errno values (e.g.,
> +				 * -EPROTONOSUPPORT, -ESOCKTNOSUPPORT,
> +				 * -EPROTOTYPE) indicate that this specific
> +				 * (type, protocol) triple is not supported by
> +				 * an otherwise-loaded family; silently skip the
> +				 * probe in that case too.
> +				 */
> +				confirmed_unsupported++;
> +			}
> +			/*
> +			 * All other non-zero returns are silently skipped: the
> +			 * kernel rejected the specific triple; nothing to
> +			 * canonicalize.
> +			 */
> +		}
> +	}
> +
> +	if (!c->supported) {
> +		kunit_info(test,
> +			   "%u probes confirmed %s unsupported (errno %d)",
> +			   confirmed_unsupported, c->name, -EAFNOSUPPORT);
> +		return;
> +	}
> +	if (tested == 0) {
> +		/*
> +		 * Resolve by either (a) adding a sub-protocol CONFIG to
> +		 * .kunitconfig so sock_create_kern can reach the family, or (b)
> +		 * flipping the family entry's .supported field to false to
> +		 * declare it expected-unreachable.
> +		 */
> +		KUNIT_FAIL(test, "%s: no reachable triple", c->name);
> +		return;
> +	}
> +	kunit_info(test, "%u reachable triples (%u canonicalized)", tested,
> +		   canonicalized);
> +}
> +
> +/*
> + * Wildcards bypass canonicalization: the input triple is returned unchanged for
> + * TYPE_ALL, and protocol rewrites are skipped for PROTOCOL_ALL while family and
> + * type rewrites still apply.
> + */
> +static void test_canonicalization_preserves_wildcards(struct kunit *const test)
> +{
> +	s32 family, type, protocol;
> +
> +	/* Type wildcard: no map lookup. */
> +	family = AF_INET;
> +	type = TYPE_ALL;
> +	protocol = 0;
> +	landlock_canonicalize_socket_key(&family, &type, &protocol);
> +	KUNIT_EXPECT_EQ(test, family, AF_INET);
> +	KUNIT_EXPECT_EQ(test, type, TYPE_ALL);
> +	KUNIT_EXPECT_EQ(test, protocol, 0);

This is btw the same example as the one I mentioned above; When you
call socket(AF_INET, SOCK_PACKET, 0), the resulting socket turns into
a (AF_PACKET, SOCK_PACKET, 0) and it does not match the canonicalized
(AF_INET, TYPE_ALL, 0) rule any more.


> +
> +	/* Protocol wildcard with REWRITE_FAMILY row: family still rewrites. */
> +	family = AF_INET;
> +	type = SOCK_PACKET;
> +	protocol = PROTOCOL_ALL;
> +	landlock_canonicalize_socket_key(&family, &type, &protocol);
> +	KUNIT_EXPECT_EQ(test, family, AF_PACKET);
> +	KUNIT_EXPECT_EQ(test, type, SOCK_PACKET);
> +	KUNIT_EXPECT_EQ(test, protocol, PROTOCOL_ALL);
> +
> +	/* Protocol wildcard with PROTOCOL_ZERO row: protocol stays wildcard. */
> +	family = AF_INET;
> +	type = SOCK_STREAM;
> +	protocol = PROTOCOL_ALL;
> +	landlock_canonicalize_socket_key(&family, &type, &protocol);
> +	KUNIT_EXPECT_EQ(test, family, AF_INET);
> +	KUNIT_EXPECT_EQ(test, type, SOCK_STREAM);
> +	KUNIT_EXPECT_EQ(test, protocol, PROTOCOL_ALL);
> +
> +	/*
> +	 * Protocol wildcard with PROTOCOL_ALWAYS row: protocol stays wildcard
> +	 * even though the entry would unconditionally rewrite concrete protocol
> +	 * values.
> +	 */
> +	family = AF_UNIX;
> +	type = SOCK_STREAM;
> +	protocol = PROTOCOL_ALL;
> +	landlock_canonicalize_socket_key(&family, &type, &protocol);
> +	KUNIT_EXPECT_EQ(test, family, AF_UNIX);
> +	KUNIT_EXPECT_EQ(test, type, SOCK_STREAM);
> +	KUNIT_EXPECT_EQ(test, protocol, PROTOCOL_ALL);
> +
> +	/*
> +	 * REWRITE_TYPE with PROTOCOL_ALL still folds the type while leaving the
> +	 * protocol wildcard intact.
> +	 */
> +	family = AF_UNIX;
> +	type = SOCK_RAW;
> +	protocol = PROTOCOL_ALL;
> +	landlock_canonicalize_socket_key(&family, &type, &protocol);
> +	KUNIT_EXPECT_EQ(test, family, AF_UNIX);
> +	KUNIT_EXPECT_EQ(test, type, SOCK_DGRAM);
> +	KUNIT_EXPECT_EQ(test, protocol, PROTOCOL_ALL);
> +}
> +
> +/*
> + * Each map entry produces the expected rewrite for a concrete triple.
> + * Parameterized for per-entry pass/fail visibility; semantic descriptors name
> + * what each entry is supposed to do.
> + */
> +struct landlock_canon_map_case {
> +	const char *name;
> +	s32 in_family, in_type, in_protocol;
> +	s32 out_family, out_type, out_protocol;
> +};
> +
> +/*
> + * test_family_canonicalization covers every (family, type, protocol) cell that
> + * sock_create_kern can reach.  The cases listed here are the kernel-unreachable
> + * cells whose canonicalization still matters in production and therefore needs
> + * config-independent validation.
> + *
> + * AF_VSOCK SOCK_DGRAM is the only such cell today: its reachability requires
> + * CONFIG_VIRTIO_VSOCKETS=y, which the default .kunitconfig does not enable.
> + * The two entries below cover both user call forms (protocol=0 and
> + * protocol=PF_VSOCK) that vsock_create accepts and canonicalize to 0.
> + */
> +static const struct landlock_canon_map_case landlock_canon_map_cases[] = {
> +	{ "vsock_dgram_zero_stays_zero", AF_VSOCK, SOCK_DGRAM, 0, AF_VSOCK,
> +	  SOCK_DGRAM, 0 },
> +	{ "vsock_dgram_family_id_forced_to_zero", AF_VSOCK, SOCK_DGRAM,
> +	  AF_VSOCK, AF_VSOCK, SOCK_DGRAM, 0 },
> +};
> +
> +static void landlock_canon_map_to_desc(const struct landlock_canon_map_case *c,
> +				       char *desc)
> +{
> +	strscpy(desc, c->name, KUNIT_PARAM_DESC_SIZE);
> +}
> +
> +KUNIT_ARRAY_PARAM(landlock_canon_map, landlock_canon_map_cases,
> +		  landlock_canon_map_to_desc);
> +
> +static void test_canonicalization_map_entry(struct kunit *const test)
> +{
> +	const struct landlock_canon_map_case *const c = test->param_value;
> +	s32 family = c->in_family, type = c->in_type, protocol = c->in_protocol;
> +
> +	landlock_canonicalize_socket_key(&family, &type, &protocol);
> +	KUNIT_EXPECT_EQ(test, family, c->out_family);
> +	KUNIT_EXPECT_EQ(test, type, c->out_type);
> +	KUNIT_EXPECT_EQ(test, protocol, c->out_protocol);
> +}
> +
> +static struct kunit_case test_cases[] = {
> +	KUNIT_CASE_PARAM(test_family_canonicalization,
> +			 landlock_canon_family_gen_params),
> +	KUNIT_CASE(test_canonicalization_preserves_wildcards),
> +	KUNIT_CASE_PARAM(test_canonicalization_map_entry,
> +			 landlock_canon_map_gen_params),
> +	{}
> +};
> +
> +static struct kunit_suite test_suite = {
> +	.name = "landlock_socket",
> +	.test_cases = test_cases,
> +};
> +
> +kunit_test_suite(test_suite);
> +
> +#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index a091b8a883c8..5c0959f50ba2 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -7,15 +7,15 @@
>  
>  #define _GNU_SOURCE
>  
> +#include <arpa/inet.h>
> +#include <linux/can.h>
> +#include <linux/kcm.h>
>  #include <linux/landlock.h>
> -#include <sys/prctl.h>
>  #include <linux/pfkeyv2.h>
> -#include <linux/kcm.h>
> -#include <linux/can.h>
> -#include <sys/socket.h>
> -#include <stdint.h>
>  #include <linux/sctp.h>
> -#include <arpa/inet.h>
> +#include <stdint.h>
> +#include <sys/prctl.h>
> +#include <sys/socket.h>
>  
>  #include "audit.h"
>  #include "common.h"
> @@ -829,11 +829,12 @@ FIXTURE_VARIANT_ADD(tcp_protocol, variant2) {
>  };
>  
>  /*
> - * Landlock doesn't perform protocol mappings handled by network stack on
> - * protocol family level. Test verifies that if only one definition is
> - * allowed another becomes restricted.
> + * Landlock canonicalizes AF_INET, SOCK_STREAM, protocol=0 to IPPROTO_TCP at
> + * rule insertion so that a rule inserted with either the default-protocol form
> + * or the explicit IPPROTO_TCP form matches both call syntaxes.  Test verifies
> + * this aliasing.  Unrelated families (AF_PACKET here) remain restricted.
>   */
> -TEST_F(tcp_protocol, alias_restriction)
> +TEST_F(tcp_protocol, alias_equivalence)
>  {
>  	const struct landlock_ruleset_attr ruleset_attr = {
>  		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> @@ -861,14 +862,15 @@ TEST_F(tcp_protocol, alias_restriction)
>  	enforce_ruleset(_metadata, ruleset_fd);
>  	ASSERT_EQ(0, close(ruleset_fd));
>  
> -	if (protocol == 0) {
> -		EXPECT_EQ(0, test_socket(AF_INET, SOCK_STREAM, 0));
> -		EXPECT_EQ(EACCES,
> -			  test_socket(AF_PACKET, SOCK_STREAM, IPPROTO_TCP));
> -	} else if (protocol == IPPROTO_TCP) {
> -		EXPECT_EQ(EACCES, test_socket(AF_INET, SOCK_STREAM, 0));
> -		EXPECT_EQ(0, test_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
> -	}
> +	/*
> +	 * Irrespective of which call form was used to insert the rule, both
> +	 * call forms of socket(2) match.
> +	 */
> +	EXPECT_EQ(0, test_socket(AF_INET, SOCK_STREAM, 0));
> +	EXPECT_EQ(0, test_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
> +
> +	/* Other families remain restricted. */
> +	EXPECT_EQ(EACCES, test_socket(AF_PACKET, SOCK_STREAM, IPPROTO_TCP));
>  }
>  
>  static int test_socketpair(int family, int type, int protocol)
> -- 
> 2.53.0
> 

–Günther

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox