diff --git a/1288-net-bonding-fix-use-after-free-in-bond-xmit-broadcast.patch b/1288-net-bonding-fix-use-after-free-in-bond-xmit-broadcast.patch new file mode 100644 index 000000000..ce2338920 --- /dev/null +++ b/1288-net-bonding-fix-use-after-free-in-bond-xmit-broadcast.patch @@ -0,0 +1,90 @@ +From 3453882f36c40d2339267093676585a89808a73d Mon Sep 17 00:00:00 2001 +From: Xiang Mei +Date: Thu, 26 Mar 2026 00:55:53 -0700 +Subject: [PATCH] net: bonding: fix use-after-free in bond_xmit_broadcast() + +commit 2884bf72fb8f03409e423397319205de48adca16 upstream. + +bond_xmit_broadcast() reuses the original skb for the last slave +(determined by bond_is_last_slave()) and clones it for others. +Concurrent slave enslave/release can mutate the slave list during +RCU-protected iteration, changing which slave is "last" mid-loop. +This causes the original skb to be double-consumed (double-freed). + +Replace the racy bond_is_last_slave() check with a simple index +comparison (i + 1 == slaves_count) against the pre-snapshot slave +count taken via READ_ONCE() before the loop. This preserves the +zero-copy optimization for the last slave while making the "last" +determination stable against concurrent list mutations. + +The UAF can trigger the following crash: + +================================================================== +BUG: KASAN: slab-use-after-free in skb_clone +Read of size 8 at addr ffff888100ef8d40 by task exploit/147 + +CPU: 1 UID: 0 PID: 147 Comm: exploit Not tainted 7.0.0-rc3+ #4 PREEMPTLAZY +Call Trace: + + dump_stack_lvl (lib/dump_stack.c:123) + print_report (mm/kasan/report.c:379 mm/kasan/report.c:482) + kasan_report (mm/kasan/report.c:597) + skb_clone (include/linux/skbuff.h:1724 include/linux/skbuff.h:1792 include/linux/skbuff.h:3396 net/core/skbuff.c:2108) + bond_xmit_broadcast (drivers/net/bonding/bond_main.c:5334) + bond_start_xmit (drivers/net/bonding/bond_main.c:5567 drivers/net/bonding/bond_main.c:5593) + dev_hard_start_xmit (include/linux/netdevice.h:5325 include/linux/netdevice.h:5334 net/core/dev.c:3871 net/core/dev.c:3887) + __dev_queue_xmit (include/linux/netdevice.h:3601 net/core/dev.c:4838) + ip6_finish_output2 (include/net/neighbour.h:540 include/net/neighbour.h:554 net/ipv6/ip6_output.c:136) + ip6_finish_output (net/ipv6/ip6_output.c:208 net/ipv6/ip6_output.c:219) + ip6_output (net/ipv6/ip6_output.c:250) + ip6_send_skb (net/ipv6/ip6_output.c:1985) + udp_v6_send_skb (net/ipv6/udp.c:1442) + udpv6_sendmsg (net/ipv6/udp.c:1733) + __sys_sendto (net/socket.c:730 net/socket.c:742 net/socket.c:2206) + __x64_sys_sendto (net/socket.c:2209) + do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94) + entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130) + + +Allocated by task 147: + +Freed by task 147: + +The buggy address belongs to the object at ffff888100ef8c80 + which belongs to the cache skbuff_head_cache of size 224 +The buggy address is located 192 bytes inside of + freed 224-byte region [ffff888100ef8c80, ffff888100ef8d60) + +Memory state around the buggy address: + ffff888100ef8c00: fb fb fb fb fc fc fc fc fc fc fc fc fc fc fc fc + ffff888100ef8c80: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb +>ffff888100ef8d00: fb fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc + ^ + ffff888100ef8d80: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb + ffff888100ef8e00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb +================================================================== + +Fixes: 4e5bd03ae346 ("net: bonding: fix bond_xmit_broadcast return value error bug") +Reported-by: Weiming Shi +Signed-off-by: Xiang Mei +Link: https://patch.msgid.link/20260326075553.3960562-1-xmei5@asu.edu +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +Signed-off-by: Kevin Berry +Signed-off-by: Greg Kroah-Hartman + + + +diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c +index 294d00f..67fd0c4 100644 +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -5401,7 +5401,7 @@ static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb, + if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP)) + continue; + +- if (bond_is_last_slave(bond, slave)) { ++ if (i + 1 == slaves_count) { + skb2 = skb; + skb_used = true; + } else { diff --git a/1289-s390-pci-avoid-deadlock-between-pci-error-recovery-and-mlx5-.patch b/1289-s390-pci-avoid-deadlock-between-pci-error-recovery-and-mlx5-.patch new file mode 100644 index 000000000..abe85258c --- /dev/null +++ b/1289-s390-pci-avoid-deadlock-between-pci-error-recovery-and-mlx5-.patch @@ -0,0 +1,123 @@ +From ac9c3524e6b15adece8493a0db2444e83b3de591 Mon Sep 17 00:00:00 2001 +From: Mete Durlu +Date: Fri, 20 Mar 2026 15:05:43 +0100 +Subject: [PATCH] s390/pci: Avoid deadlock between PCI error recovery and mlx5 + crdump + +JIRA: https://issues.redhat.com/browse/RHEL-157932 + +commit 0fd20f65df6aa430454a0deed8f43efa91c54835 +Author: Gerd Bayer +Date: Thu Oct 16 11:27:03 2025 +0200 + + s390/pci: Avoid deadlock between PCI error recovery and mlx5 crdump + + Do not block PCI config accesses through pci_cfg_access_lock() when + executing the s390 variant of PCI error recovery: Acquire just + device_lock() instead of pci_dev_lock() as powerpc's EEH and + generig PCI AER processing do. + + During error recovery testing a pair of tasks was reported to be hung: + + mlx5_core 0000:00:00.1: mlx5_health_try_recover:338:(pid 5553): health recovery flow aborted, PCI reads still not working + INFO: task kmcheck:72 blocked for more than 122 seconds. + Not tainted 5.14.0-570.12.1.bringup7.el9.s390x #1 + "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. + task:kmcheck state:D stack:0 pid:72 tgid:72 ppid:2 flags:0x00000000 + Call Trace: + [<000000065256f030>] __schedule+0x2a0/0x590 + [<000000065256f356>] schedule+0x36/0xe0 + [<000000065256f572>] schedule_preempt_disabled+0x22/0x30 + [<0000000652570a94>] __mutex_lock.constprop.0+0x484/0x8a8 + [<000003ff800673a4>] mlx5_unload_one+0x34/0x58 [mlx5_core] + [<000003ff8006745c>] mlx5_pci_err_detected+0x94/0x140 [mlx5_core] + [<0000000652556c5a>] zpci_event_attempt_error_recovery+0xf2/0x398 + [<0000000651b9184a>] __zpci_event_error+0x23a/0x2c0 + INFO: task kworker/u1664:6:1514 blocked for more than 122 seconds. + Not tainted 5.14.0-570.12.1.bringup7.el9.s390x #1 + "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. + task:kworker/u1664:6 state:D stack:0 pid:1514 tgid:1514 ppid:2 flags:0x00000000 + Workqueue: mlx5_health0000:00:00.0 mlx5_fw_fatal_reporter_err_work [mlx5_core] + Call Trace: + [<000000065256f030>] __schedule+0x2a0/0x590 + [<000000065256f356>] schedule+0x36/0xe0 + [<0000000652172e28>] pci_wait_cfg+0x80/0xe8 + [<0000000652172f94>] pci_cfg_access_lock+0x74/0x88 + [<000003ff800916b6>] mlx5_vsc_gw_lock+0x36/0x178 [mlx5_core] + [<000003ff80098824>] mlx5_crdump_collect+0x34/0x1c8 [mlx5_core] + [<000003ff80074b62>] mlx5_fw_fatal_reporter_dump+0x6a/0xe8 [mlx5_core] + [<0000000652512242>] devlink_health_do_dump.part.0+0x82/0x168 + [<0000000652513212>] devlink_health_report+0x19a/0x230 + [<000003ff80075a12>] mlx5_fw_fatal_reporter_err_work+0xba/0x1b0 [mlx5_core] + + No kernel log of the exact same error with an upstream kernel is + available - but the very same deadlock situation can be constructed there, + too: + + - task: kmcheck + mlx5_unload_one() tries to acquire devlink lock while the PCI error + recovery code has set pdev->block_cfg_access by way of + pci_cfg_access_lock() + - task: kworker + mlx5_crdump_collect() tries to set block_cfg_access through + pci_cfg_access_lock() while devlink_health_report() had acquired + the devlink lock. + + A similar deadlock situation can be reproduced by requesting a + crdump with + > devlink health dump show pci/ reporter fw_fatal + + while PCI error recovery is executed on the same physical function + by mlx5_core's pci_error_handlers. On s390 this can be injected with + > zpcictl --reset-fw + + Tests with this patch failed to reproduce that second deadlock situation, + the devlink command is rejected with "kernel answers: Permission denied" - + and we get a kernel log message of: + + mlx5_core 1ed0:00:00.1: mlx5_crdump_collect:50:(pid 254382): crdump: failed to lock vsc gw err -5 + + because the config read of VSC_SEMAPHORE is rejected by the underlying + hardware. + + Two prior attempts to address this issue have been discussed and + ultimately rejected [see link], with the primary argument that s390's + implementation of PCI error recovery is imposing restrictions that + neither powerpc's EEH nor PCI AER handling need. Tests show that PCI + error recovery on s390 is running to completion even without blocking + access to PCI config space. + + Link: https://lore.kernel.org/all/20251007144826.2825134-1-gbayer@linux.ibm.com/ + Cc: stable@vger.kernel.org + Fixes: 4cdf2f4e24ff ("s390/pci: implement minimal PCI error recovery") + Reviewed-by: Niklas Schnelle + Signed-off-by: Gerd Bayer + Signed-off-by: Heiko Carstens + +Signed-off-by: Mete Durlu + +diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c +index d930416d4c90..da0de34d2e5c 100644 +--- a/arch/s390/pci/pci_event.c ++++ b/arch/s390/pci/pci_event.c +@@ -187,7 +187,7 @@ static pci_ers_result_t zpci_event_attempt_error_recovery(struct pci_dev *pdev) + * is unbound or probed and that userspace can't access its + * configuration space while we perform recovery. + */ +- pci_dev_lock(pdev); ++ device_lock(&pdev->dev); + if (pdev->error_state == pci_channel_io_perm_failure) { + ers_res = PCI_ERS_RESULT_DISCONNECT; + goto out_unlock; +@@ -254,7 +254,7 @@ static pci_ers_result_t zpci_event_attempt_error_recovery(struct pci_dev *pdev) + if (driver->err_handler->resume) + driver->err_handler->resume(pdev); + out_unlock: +- pci_dev_unlock(pdev); ++ device_unlock(&pdev->dev); + zpci_report_status(zdev, "recovery", status_str); + + return ers_res; +-- +2.50.1 (Apple Git-155) + diff --git a/1290-ice-fix-stats-array-overflow-when-vf-requests-more-queues.patch b/1290-ice-fix-stats-array-overflow-when-vf-requests-more-queues.patch new file mode 100644 index 000000000..779cbf0f1 --- /dev/null +++ b/1290-ice-fix-stats-array-overflow-when-vf-requests-more-queues.patch @@ -0,0 +1,138 @@ +From 9837d44c3a8c7908fca4a3e4ef2e753b43b2e7da Mon Sep 17 00:00:00 2001 +From: Michal Schmidt +Date: Mon, 27 Apr 2026 17:18:26 +0200 +Subject: [PATCH] ice: fix stats array overflow when VF requests more queues + +JIRA: https://redhat.atlassian.net/browse/RHEL-163186 + +Upstream status: Posted: https://lore.kernel.org/netdev/20260427151827.43342-1-mschmidt@redhat.com/ + +When a VF increases its queue count via VIRTCHNL_OP_REQUEST_QUEUES, +ice_vc_request_qs_msg() sets vf->num_req_qs and triggers a VF reset. +The reset calls ice_vf_reconfig_vsi(), which does ice_vsi_decfg() +followed by ice_vsi_cfg(). ice_vsi_decfg() does not free the per-ring +stats arrays. Inside ice_vsi_cfg_def(), ice_vsi_set_num_qs() updates +alloc_txq/alloc_rxq to the new larger value, but +ice_vsi_alloc_stat_arrays() returns early because the stats already +exist. ice_vsi_alloc_ring_stats() then iterates using the new larger +alloc_txq and writes beyond the bounds of the old, smaller +tx_ring_stats/rx_ring_stats pointer arrays, corrupting adjacent SLUB +metadata. + +KASAN detects the bug: + ================================================================== + BUG: KASAN: slab-out-of-bounds in ice_vsi_alloc_ring_stats+0x385/0x4a0 [ice] + Read of size 8 at addr ffff88810affea60 by task kworker/u131:7/221 + + CPU: 24 UID: 0 PID: 221 Comm: kworker/u131:7 Not tainted 7.1.0-rc1+ #1 PREEMPT(lazy) + ... + Workqueue: ice ice_service_task [ice] + Call Trace: + + ... + kasan_report+0xd7/0x120 + ice_vsi_alloc_ring_stats+0x385/0x4a0 [ice] + ice_vsi_cfg_def+0x12e2/0x2060 [ice] + ice_vsi_cfg+0xb5/0x3c0 [ice] + ice_reset_vf+0x858/0xf80 [ice] + ice_vc_request_qs_msg+0x1da/0x290 [ice] + ice_vc_process_vf_msg+0xb15/0x1430 [ice] + __ice_clean_ctrlq+0x70d/0x9d0 [ice] + ice_service_task+0x840/0xf20 [ice] + process_one_work+0x690/0xff0 + worker_thread+0x4d9/0xd20 + kthread+0x322/0x410 + ret_from_fork+0x332/0x660 + ret_from_fork_asm+0x1a/0x30 + + + Allocated by task 2439: + kasan_save_stack+0x1c/0x40 + kasan_save_track+0x10/0x30 + __kasan_kmalloc+0x96/0xb0 + __kmalloc_noprof+0x1d8/0x580 + ice_vsi_cfg_def+0x115c/0x2060 [ice] + ice_vsi_cfg+0xb5/0x3c0 [ice] + ice_vsi_setup+0x180/0x320 [ice] + ice_start_vfs+0x1f3/0x590 [ice] + ice_ena_vfs+0x66d/0x798 [ice] + ice_sriov_configure.cold+0xe4/0x121 [ice] + sriov_numvfs_store+0x279/0x480 + kernfs_fop_write_iter+0x331/0x4f0 + vfs_write+0x4c4/0xe40 + ksys_write+0x10c/0x240 + do_syscall_64+0xd9/0x650 + entry_SYSCALL_64_after_hwframe+0x76/0x7e + + The buggy address belongs to the object at ffff88810affea40 + which belongs to the cache kmalloc-32 of size 32 + The buggy address is located 0 bytes to the right of + allocated 32-byte region [ffff88810affea40, ffff88810affea60) + ... + ================================================================== + +ice_vsi_rebuild() handles this correctly by calling +ice_vsi_realloc_stat_arrays() before reconfiguration, but +ice_vf_reconfig_vsi() was missing this call. + +Fix by calling ice_vsi_realloc_stat_arrays() in ice_vf_reconfig_vsi() +before ice_vsi_decfg(), mirroring the ice_vsi_rebuild() pattern. Set +vsi->req_txq/req_rxq from vf->num_req_qs so the realloc function knows +the target array size. + +See the linked RHEL Jira item for a reproducer. + +Fixes: 2a2cb4c6c181 ("ice: replace ice_vf_recreate_vsi() with ice_vf_reconfig_vsi()") +Closes: https://redhat.atlassian.net/browse/RHEL-164321 +Signed-off-by: Michal Schmidt +Assisted-by: Claude:claude-opus-4-6 semcode +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Simon Horman +Reviewed-by: Przemek Kitszel + +diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c +index 26496eaed981..7bc5a7009cbb 100644 +--- a/drivers/net/ethernet/intel/ice/ice_lib.c ++++ b/drivers/net/ethernet/intel/ice/ice_lib.c +@@ -3017,7 +3017,7 @@ ice_vsi_rebuild_set_coalesce(struct ice_vsi *vsi, + * ice_vsi_realloc_stat_arrays - Frees unused stat structures or alloc new ones + * @vsi: VSI pointer + */ +-static int ++int + ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi) + { + u16 req_txq = vsi->req_txq ? vsi->req_txq : vsi->alloc_txq; +diff --git a/drivers/net/ethernet/intel/ice/ice_lib.h b/drivers/net/ethernet/intel/ice/ice_lib.h +index 2cb1eb98b9da..fce6f633f506 100644 +--- a/drivers/net/ethernet/intel/ice/ice_lib.h ++++ b/drivers/net/ethernet/intel/ice/ice_lib.h +@@ -66,6 +66,7 @@ int ice_ena_vsi(struct ice_vsi *vsi, bool locked); + void ice_vsi_decfg(struct ice_vsi *vsi); + void ice_dis_vsi(struct ice_vsi *vsi, bool locked); + ++int ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi); + int ice_vsi_rebuild(struct ice_vsi *vsi, u32 vsi_flags); + int ice_vsi_cfg(struct ice_vsi *vsi); + struct ice_vsi *ice_vsi_alloc(struct ice_pf *pf); +diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c +index de9e81ccee66..fd71dd10eb38 100644 +--- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c ++++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c +@@ -268,6 +268,13 @@ static int ice_vf_reconfig_vsi(struct ice_vf *vf) + + vsi->flags = ICE_VSI_FLAG_NO_INIT; + ++ vsi->req_txq = vf->num_req_qs; ++ vsi->req_rxq = vf->num_req_qs; ++ ++ err = ice_vsi_realloc_stat_arrays(vsi); ++ if (err) ++ return err; ++ + ice_vsi_decfg(vsi); + ice_fltr_remove_all(vsi); + +-- +2.50.1 (Apple Git-155) + diff --git a/1291-s390-dasd-fix-gendisk-parent-after-copy-pair-swap.patch b/1291-s390-dasd-fix-gendisk-parent-after-copy-pair-swap.patch new file mode 100644 index 000000000..f32919e87 --- /dev/null +++ b/1291-s390-dasd-fix-gendisk-parent-after-copy-pair-swap.patch @@ -0,0 +1,57 @@ +From 8ac29c454e865adf7b65a4f83d28177e0ecb1dca Mon Sep 17 00:00:00 2001 +From: Ming Lei +Date: Wed, 26 Nov 2025 17:06:31 +0100 +Subject: [PATCH] s390/dasd: Fix gendisk parent after copy pair swap + +JIRA: https://issues.redhat.com/browse/RHEL-144763 + +commit c943bfc6afb8d0e781b9b7406f36caa8bbf95cb9 +Author: Stefan Haberland +Date: Wed Nov 26 17:06:31 2025 +0100 + + s390/dasd: Fix gendisk parent after copy pair swap + + After a copy pair swap the block device's "device" symlink points to + the secondary CCW device, but the gendisk's parent remained the + primary, leaving /sys/block/ under the wrong parent. + + Move the gendisk to the secondary's device with device_move(), keeping + the sysfs topology consistent after the swap. + + Fixes: 413862caad6f ("s390/dasd: add copy pair swap capability") + Cc: stable@vger.kernel.org #6.1 + Reviewed-by: Jan Hoeppner + Signed-off-by: Stefan Haberland + Signed-off-by: Jens Axboe + +Signed-off-by: Ming Lei + +diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c +index 1ebe589b5185..78fef8959c78 100644 +--- a/drivers/s390/block/dasd_eckd.c ++++ b/drivers/s390/block/dasd_eckd.c +@@ -6149,6 +6149,7 @@ static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid + struct dasd_copy_relation *copy; + struct dasd_block *block; + struct gendisk *gdp; ++ int rc; + + copy = device->copy; + if (!copy) +@@ -6183,6 +6184,13 @@ static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid + /* swap blocklayer device link */ + gdp = block->gdp; + dasd_add_link_to_gendisk(gdp, secondary); ++ rc = device_move(disk_to_dev(gdp), &secondary->cdev->dev, DPM_ORDER_NONE); ++ if (rc) { ++ dev_err(&primary->cdev->dev, ++ "copy_pair_swap: moving blockdevice parent %s->%s failed (%d)\n", ++ dev_name(&primary->cdev->dev), ++ dev_name(&secondary->cdev->dev), rc); ++ } + + /* re-enable device */ + dasd_device_remove_stop_bits(primary, DASD_STOPPED_PPRC); +-- +2.50.1 (Apple Git-155) + diff --git a/1292-s390-dasd-move-quiesce-state-with-pprc-swap.patch b/1292-s390-dasd-move-quiesce-state-with-pprc-swap.patch new file mode 100644 index 000000000..7b738ebf2 --- /dev/null +++ b/1292-s390-dasd-move-quiesce-state-with-pprc-swap.patch @@ -0,0 +1,54 @@ +From 98d247e4eac9e5a06678190715d9f9d86902aa9c Mon Sep 17 00:00:00 2001 +From: Mete Durlu +Date: Fri, 27 Mar 2026 13:20:49 +0100 +Subject: [PATCH] s390/dasd: Move quiesce state with pprc swap + +JIRA: https://issues.redhat.com/browse/RHEL-161531 + +commit 40e9cd4ae8ec43b107ed2bff422a8fa39dcf4e4b +Author: Stefan Haberland +Date: Tue Mar 10 15:23:29 2026 +0100 + + s390/dasd: Move quiesce state with pprc swap + + Quiesce and resume is a mechanism to suspend operations on DASD devices. + In the context of a controlled copy pair swap operation, the quiesce + operation is usually issued before the actual swap and a resume + afterwards. + + During the swap operation, the underlying device is exchanged. Therefore, + the quiesce flag must be moved to the secondary device to ensure a + consistent quiesce state after the swap. + + The secondary device itself cannot be suspended separately because there + is no separate block device representation for it. + + Fixes: 413862caad6f ("s390/dasd: add copy pair swap capability") + Cc: stable@vger.kernel.org #6.1 + Reviewed-by: Jan Hoeppner + Signed-off-by: Stefan Haberland + Link: https://patch.msgid.link/20260310142330.4080106-2-sth@linux.ibm.com + Signed-off-by: Jens Axboe + +Signed-off-by: Stefan Haberland +Signed-off-by: Mete Durlu + +diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c +index 78fef8959c78..c6d825109f53 100644 +--- a/drivers/s390/block/dasd_eckd.c ++++ b/drivers/s390/block/dasd_eckd.c +@@ -6192,6 +6192,11 @@ static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid + dev_name(&secondary->cdev->dev), rc); + } + ++ if (primary->stopped & DASD_STOPPED_QUIESCE) { ++ dasd_device_set_stop_bits(secondary, DASD_STOPPED_QUIESCE); ++ dasd_device_remove_stop_bits(primary, DASD_STOPPED_QUIESCE); ++ } ++ + /* re-enable device */ + dasd_device_remove_stop_bits(primary, DASD_STOPPED_PPRC); + dasd_device_remove_stop_bits(secondary, DASD_STOPPED_PPRC); +-- +2.50.1 (Apple Git-155) + diff --git a/1293-s390-dasd-copy-detected-format-information-to-secondary-devi.patch b/1293-s390-dasd-copy-detected-format-information-to-secondary-devi.patch new file mode 100644 index 000000000..cdd50fdc5 --- /dev/null +++ b/1293-s390-dasd-copy-detected-format-information-to-secondary-devi.patch @@ -0,0 +1,83 @@ +From 2435d419304a1e5bfc93947d3c6a7902b49aadcc Mon Sep 17 00:00:00 2001 +From: Mete Durlu +Date: Fri, 27 Mar 2026 13:20:52 +0100 +Subject: [PATCH] s390/dasd: Copy detected format information to secondary + device + +JIRA: https://issues.redhat.com/browse/RHEL-161531 + +commit 4c527c7e030672efd788d0806d7a68972a7ba3c1 +Author: Stefan Haberland +Date: Tue Mar 10 15:23:30 2026 +0100 + + s390/dasd: Copy detected format information to secondary device + + During online processing for a DASD device an IO operation is started to + determine the format of the device. CDL format contains specifically + sized blocks at the beginning of the disk. + + For a PPRC secondary device no real IO operation is possible therefore + this IO request can not be started and this step is skipped for online + processing of secondary devices. This is generally fine since the + secondary is a copy of the primary device. + + In case of an additional partition detection that is run after a swap + operation the format information is needed to properly drive partition + detection IO. + + Currently the information is not passed leading to IO errors during + partition detection and a wrongly detected partition table which in turn + might lead to data corruption on the disk with the wrong partition table. + + Fix by passing the format information from primary to secondary device. + + Fixes: 413862caad6f ("s390/dasd: add copy pair swap capability") + Cc: stable@vger.kernel.org #6.1 + Reviewed-by: Jan Hoeppner + Acked-by: Eduard Shishkin + Signed-off-by: Stefan Haberland + Link: https://patch.msgid.link/20260310142330.4080106-3-sth@linux.ibm.com + Signed-off-by: Jens Axboe + +Signed-off-by: Stefan Haberland +Signed-off-by: Mete Durlu + +diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c +index c6d825109f53..0d874d92a099 100644 +--- a/drivers/s390/block/dasd_eckd.c ++++ b/drivers/s390/block/dasd_eckd.c +@@ -6145,6 +6145,7 @@ static void copy_pair_set_active(struct dasd_copy_relation *copy, char *new_busi + static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid, + char *sec_busid) + { ++ struct dasd_eckd_private *prim_priv, *sec_priv; + struct dasd_device *primary, *secondary; + struct dasd_copy_relation *copy; + struct dasd_block *block; +@@ -6165,6 +6166,9 @@ static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid + if (!secondary) + return DASD_COPYPAIRSWAP_SECONDARY; + ++ prim_priv = primary->private; ++ sec_priv = secondary->private; ++ + /* + * usually the device should be quiesced for swap + * for paranoia stop device and requeue requests again +@@ -6197,6 +6201,13 @@ static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid + dasd_device_remove_stop_bits(primary, DASD_STOPPED_QUIESCE); + } + ++ /* ++ * The secondary device never got through format detection, but since it ++ * is a copy of the primary device, the format is exactly the same; ++ * therefore, the detected layout can simply be copied. ++ */ ++ sec_priv->uses_cdl = prim_priv->uses_cdl; ++ + /* re-enable device */ + dasd_device_remove_stop_bits(primary, DASD_STOPPED_PPRC); + dasd_device_remove_stop_bits(secondary, DASD_STOPPED_PPRC); +-- +2.50.1 (Apple Git-155) + diff --git a/1294-nouveau-gsp-drop-warn-on-in-acpi-probes.patch b/1294-nouveau-gsp-drop-warn-on-in-acpi-probes.patch new file mode 100644 index 000000000..799325afb --- /dev/null +++ b/1294-nouveau-gsp-drop-warn-on-in-acpi-probes.patch @@ -0,0 +1,55 @@ +From 9478c166c46934160135e197b049b5a05753f2ad Mon Sep 17 00:00:00 2001 +From: Dave Airlie +Date: Thu, 21 Nov 2024 11:46:01 +1000 +Subject: [PATCH] nouveau/gsp: drop WARN_ON in ACPI probes + +These WARN_ONs seem to trigger a lot, and we don't seem to have a +plan to fix them, so just drop them, as they are most likely +harmless. + +Cc: stable@vger.kernel.org +Fixes: 176fdcbddfd2 ("drm/nouveau/gsp/r535: add support for booting GSP-RM") +Signed-off-by: Dave Airlie +Link: https://patch.msgid.link/20241121014601.229391-1-airlied@gmail.com +Signed-off-by: Danilo Krummrich + +diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c +index 7fb13434c051..a575a8dbf727 100644 +--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c ++++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c +@@ -737,8 +737,8 @@ r535_gsp_acpi_caps(acpi_handle handle, CAPS_METHOD_DATA *caps) + if (!obj) + goto done; + +- if (WARN_ON(obj->type != ACPI_TYPE_BUFFER) || +- WARN_ON(obj->buffer.length != 4)) ++ if (obj->type != ACPI_TYPE_BUFFER || ++ obj->buffer.length != 4) + goto done; + + caps->status = 0; +@@ -773,8 +773,8 @@ r535_gsp_acpi_jt(acpi_handle handle, JT_METHOD_DATA *jt) + if (!obj) + goto done; + +- if (WARN_ON(obj->type != ACPI_TYPE_BUFFER) || +- WARN_ON(obj->buffer.length != 4)) ++ if (obj->type != ACPI_TYPE_BUFFER || ++ obj->buffer.length != 4) + goto done; + + jt->status = 0; +@@ -861,8 +861,8 @@ r535_gsp_acpi_dod(acpi_handle handle, DOD_METHOD_DATA *dod) + + _DOD = output.pointer; + +- if (WARN_ON(_DOD->type != ACPI_TYPE_PACKAGE) || +- WARN_ON(_DOD->package.count > ARRAY_SIZE(dod->acpiIdList))) ++ if (_DOD->type != ACPI_TYPE_PACKAGE || ++ _DOD->package.count > ARRAY_SIZE(dod->acpiIdList)) + return; + + for (int i = 0; i < _DOD->package.count; i++) { +-- +2.50.1 (Apple Git-155) + diff --git a/1295-can-raw-fix-ro-uniq-use-after-free-in-raw-rcv.patch b/1295-can-raw-fix-ro-uniq-use-after-free-in-raw-rcv.patch new file mode 100644 index 000000000..9d6b3ed29 --- /dev/null +++ b/1295-can-raw-fix-ro-uniq-use-after-free-in-raw-rcv.patch @@ -0,0 +1,77 @@ +From c9a44a91015ff6116ef4dfb6e2c752e5366b2d32 Mon Sep 17 00:00:00 2001 +From: Davide Caratti +Date: Mon, 27 Apr 2026 12:58:46 +0200 +Subject: [PATCH] can: raw: fix ro->uniq use-after-free in raw_rcv() + +JIRA: https://redhat.atlassian.net/browse/RHEL-170766 +CVE: CVE-2026-31532 +Upstream Status: net-next.git commit a535a9217ca3f2fccedaafb2fddb4c48f27d36dc + +commit a535a9217ca3f2fccedaafb2fddb4c48f27d36dc +Author: Samuel Page +Date: Wed Apr 8 15:30:13 2026 +0100 + + can: raw: fix ro->uniq use-after-free in raw_rcv() + + raw_release() unregisters raw CAN receive filters via can_rx_unregister(), + but receiver deletion is deferred with call_rcu(). This leaves a window + where raw_rcv() may still be running in an RCU read-side critical section + after raw_release() frees ro->uniq, leading to a use-after-free of the + percpu uniq storage. + + Move free_percpu(ro->uniq) out of raw_release() and into a raw-specific + socket destructor. can_rx_unregister() takes an extra reference to the + socket and only drops it from the RCU callback, so freeing uniq from + sk_destruct ensures the percpu area is not released until the relevant + callbacks have drained. + + Fixes: 514ac99c64b2 ("can: fix multiple delivery of a single CAN frame for overlapping CAN filters") + Cc: stable@vger.kernel.org # v4.1+ + Assisted-by: Bynario AI + Signed-off-by: Samuel Page + Link: https://patch.msgid.link/26ec626d-cae7-4418-9782-7198864d070c@bynar.io + Acked-by: Oliver Hartkopp + [mkl: applied manually] + Signed-off-by: Marc Kleine-Budde + +Signed-off-by: Davide Caratti + +diff --git a/net/can/raw.c b/net/can/raw.c +index 2aab0398e444..c7e392fad11a 100644 +--- a/net/can/raw.c ++++ b/net/can/raw.c +@@ -360,6 +360,14 @@ static int raw_notifier(struct notifier_block *nb, unsigned long msg, + return NOTIFY_DONE; + } + ++static void raw_sock_destruct(struct sock *sk) ++{ ++ struct raw_sock *ro = raw_sk(sk); ++ ++ free_percpu(ro->uniq); ++ can_sock_destruct(sk); ++} ++ + static int raw_init(struct sock *sk) + { + struct raw_sock *ro = raw_sk(sk); +@@ -386,6 +394,8 @@ static int raw_init(struct sock *sk) + if (unlikely(!ro->uniq)) + return -ENOMEM; + ++ sk->sk_destruct = raw_sock_destruct; ++ + /* set notifier */ + spin_lock(&raw_notifier_lock); + list_add_tail(&ro->notifier, &raw_notifier_list); +@@ -433,7 +443,6 @@ static int raw_release(struct socket *sock) + ro->bound = 0; + ro->dev = NULL; + ro->count = 0; +- free_percpu(ro->uniq); + + sock_orphan(sk); + sock->sk = NULL; +-- +2.50.1 (Apple Git-155) + diff --git a/1296-erofs-add-gfp-noio-in-the-bio-completion-if-needed.patch b/1296-erofs-add-gfp-noio-in-the-bio-completion-if-needed.patch new file mode 100644 index 000000000..8c17c37fa --- /dev/null +++ b/1296-erofs-add-gfp-noio-in-the-bio-completion-if-needed.patch @@ -0,0 +1,70 @@ +From ce3e488188bb7c00b8ccdba07c6ccfb98d7384a2 Mon Sep 17 00:00:00 2001 +From: CKI Backport Bot +Date: Tue, 28 Apr 2026 18:31:31 +0000 +Subject: [PATCH] erofs: add GFP_NOIO in the bio completion if needed + +JIRA: https://redhat.atlassian.net/browse/RHEL-171687 +CVE: CVE-2026-31467 + +commit c23df30915f83e7257c8625b690a1cece94142a0 +Author: Jiucheng Xu +Date: Wed Mar 11 17:11:31 2026 +0800 + + erofs: add GFP_NOIO in the bio completion if needed + + The bio completion path in the process context (e.g. dm-verity) + will directly call into decompression rather than trigger another + workqueue context for minimal scheduling latencies, which can + then call vm_map_ram() with GFP_KERNEL. + + Due to insufficient memory, vm_map_ram() may generate memory + swapping I/O, which can cause submit_bio_wait to deadlock + in some scenarios. + + Trimmed down the call stack, as follows: + + f2fs_submit_read_io + submit_bio //bio_list is initialized. + mmc_blk_mq_recovery + z_erofs_endio + vm_map_ram + __pte_alloc_kernel + __alloc_pages_direct_reclaim + shrink_folio_list + __swap_writepage + submit_bio_wait //bio_list is non-NULL, hang!!! + + Use memalloc_noio_{save,restore}() to wrap up this path. + + Reviewed-by: Gao Xiang + Signed-off-by: Jiucheng Xu + Reviewed-by: Chao Yu + Signed-off-by: Gao Xiang + +Signed-off-by: CKI Backport Bot + +diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c +index db1f13663cd5..cfde72fcd548 100644 +--- a/fs/erofs/zdata.c ++++ b/fs/erofs/zdata.c +@@ -1337,6 +1337,7 @@ static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io, + int bios) + { + struct erofs_sb_info *const sbi = EROFS_SB(io->sb); ++ int gfp_flag; + + /* wake up the caller thread for sync decompression */ + if (io->sync) { +@@ -1370,7 +1371,9 @@ static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io, + sbi->opt.sync_decompress = EROFS_SYNC_DECOMPRESS_FORCE_ON; + return; + } ++ gfp_flag = memalloc_noio_save(); + z_erofs_decompressqueue_work(&io->u.work); ++ memalloc_noio_restore(gfp_flag); + } + + static void z_erofs_fill_bio_vec(struct bio_vec *bvec, +-- +2.50.1 (Apple Git-155) + diff --git a/1297-alsa-6fire-fix-use-after-free-on-disconnect.patch b/1297-alsa-6fire-fix-use-after-free-on-disconnect.patch new file mode 100644 index 000000000..ae880647a --- /dev/null +++ b/1297-alsa-6fire-fix-use-after-free-on-disconnect.patch @@ -0,0 +1,90 @@ +From ee288dfa7ea837c1ffe68fd01e54d422e02f0fc4 Mon Sep 17 00:00:00 2001 +From: CKI Backport Bot +Date: Mon, 4 May 2026 08:33:31 +0000 +Subject: [PATCH] ALSA: 6fire: fix use-after-free on disconnect + +JIRA: https://redhat.atlassian.net/browse/RHEL-172974 +CVE: CVE-2026-31581 + +commit b9c826916fdce6419b94eb0cd8810fdac18c2386 +Author: Berk Cem Goksel +Date: Fri Apr 10 08:13:41 2026 +0300 + + ALSA: 6fire: fix use-after-free on disconnect + + In usb6fire_chip_abort(), the chip struct is allocated as the card's + private data (via snd_card_new with sizeof(struct sfire_chip)). When + snd_card_free_when_closed() is called and no file handles are open, the + card and embedded chip are freed synchronously. The subsequent + chip->card = NULL write then hits freed slab memory. + + Call trace: + usb6fire_chip_abort sound/usb/6fire/chip.c:59 [inline] + usb6fire_chip_disconnect+0x348/0x358 sound/usb/6fire/chip.c:182 + usb_unbind_interface+0x1a8/0x88c drivers/usb/core/driver.c:458 + ... + hub_event+0x1a04/0x4518 drivers/usb/core/hub.c:5953 + + Fix by moving the card lifecycle out of usb6fire_chip_abort() and into + usb6fire_chip_disconnect(). The card pointer is saved in a local + before any teardown, snd_card_disconnect() is called first to prevent + new opens, URBs are aborted while chip is still valid, and + snd_card_free_when_closed() is called last so chip is never accessed + after the card may be freed. + + Fixes: a0810c3d6dd2 ("ALSA: 6fire: Release resources at card release") + Cc: stable@vger.kernel.org + Cc: Andrey Konovalov + Signed-off-by: Berk Cem Goksel + Link: https://patch.msgid.link/20260410051341.1069716-1-berkcgoksel@gmail.com + Signed-off-by: Takashi Iwai + +Signed-off-by: CKI Backport Bot + +diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c +index 5ff78814e687..874f6cd503ca 100644 +--- a/sound/usb/6fire/chip.c ++++ b/sound/usb/6fire/chip.c +@@ -53,11 +53,6 @@ static void usb6fire_chip_abort(struct sfire_chip *chip) + usb6fire_comm_abort(chip); + if (chip->control) + usb6fire_control_abort(chip); +- if (chip->card) { +- snd_card_disconnect(chip->card); +- snd_card_free_when_closed(chip->card); +- chip->card = NULL; +- } + } + } + +@@ -168,6 +163,7 @@ static int usb6fire_chip_probe(struct usb_interface *intf, + static void usb6fire_chip_disconnect(struct usb_interface *intf) + { + struct sfire_chip *chip; ++ struct snd_card *card; + + chip = usb_get_intfdata(intf); + if (chip) { /* if !chip, fw upload has been performed */ +@@ -178,8 +174,19 @@ static void usb6fire_chip_disconnect(struct usb_interface *intf) + chips[chip->regidx] = NULL; + } + ++ /* ++ * Save card pointer before teardown. ++ * snd_card_free_when_closed() may free card (and ++ * the embedded chip) immediately, so it must be ++ * called last and chip must not be accessed after. ++ */ ++ card = chip->card; + chip->shutdown = true; ++ if (card) ++ snd_card_disconnect(card); + usb6fire_chip_abort(chip); ++ if (card) ++ snd_card_free_when_closed(card); + } + } + } +-- +2.50.1 (Apple Git-155) + diff --git a/1298-ip6-tunnel-clear-skb2-cb-in-ip4ip6-err.patch b/1298-ip6-tunnel-clear-skb2-cb-in-ip4ip6-err.patch new file mode 100644 index 000000000..99eb592f8 --- /dev/null +++ b/1298-ip6-tunnel-clear-skb2-cb-in-ip4ip6-err.patch @@ -0,0 +1,54 @@ +From 1063515ce15ff31065c4e7f8265f4c2fd3c54876 Mon Sep 17 00:00:00 2001 +From: Eric Dumazet +Date: Thu, 26 Mar 2026 15:51:38 +0000 +Subject: [PATCH] ip6_tunnel: clear skb2->cb[] in ip4ip6_err() + +[ Upstream commit 2edfa31769a4add828a7e604b21cb82aaaa05925 ] + +Oskar Kjos reported the following problem. + +ip4ip6_err() calls icmp_send() on a cloned skb whose cb[] was written +by the IPv6 receive path as struct inet6_skb_parm. icmp_send() passes +IPCB(skb2) to __ip_options_echo(), which interprets that cb[] region +as struct inet_skb_parm (IPv4). The layouts differ: inet6_skb_parm.nhoff +at offset 14 overlaps inet_skb_parm.opt.rr, producing a non-zero rr +value. __ip_options_echo() then reads optlen from attacker-controlled +packet data at sptr[rr+1] and copies that many bytes into dopt->__data, +a fixed 40-byte stack buffer (IP_OPTIONS_DATA_FIXED_SIZE). + +To fix this we clear skb2->cb[], as suggested by Oskar Kjos. + +Also add minimal IPv4 header validation (version == 4, ihl >= 5). + +Fixes: c4d3efafcc93 ("[IPV6] IP6TUNNEL: Add support to IPv4 over IPv6 tunnel.") +Reported-by: Oskar Kjos +Signed-off-by: Eric Dumazet +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260326155138.2429480-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin + +diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c +index 9f1b66bb513c..f0a8350eb52e 100644 +--- a/net/ipv6/ip6_tunnel.c ++++ b/net/ipv6/ip6_tunnel.c +@@ -601,11 +601,16 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + if (!skb2) + return 0; + ++ /* Remove debris left by IPv6 stack. */ ++ memset(IPCB(skb2), 0, sizeof(*IPCB(skb2))); ++ + skb_dst_drop(skb2); + + skb_pull(skb2, offset); + skb_reset_network_header(skb2); + eiph = ip_hdr(skb2); ++ if (eiph->version != 4 || eiph->ihl < 5) ++ goto out; + + /* Try to guess incoming interface */ + rt = ip_route_output_ports(dev_net(skb->dev), &fl4, NULL, eiph->saddr, +-- +2.50.1 (Apple Git-155) + diff --git a/1299-ipv6-rpl-reserve-mac-len-headroom-when-recompressed-srh-grow.patch b/1299-ipv6-rpl-reserve-mac-len-headroom-when-recompressed-srh-grow.patch new file mode 100644 index 000000000..a4f20b697 --- /dev/null +++ b/1299-ipv6-rpl-reserve-mac-len-headroom-when-recompressed-srh-grow.patch @@ -0,0 +1,79 @@ +From 4babc2d9fda2df43823b85d08a0180b68f1b0854 Mon Sep 17 00:00:00 2001 +From: Greg Kroah-Hartman +Date: Tue, 21 Apr 2026 15:16:33 +0200 +Subject: [PATCH] ipv6: rpl: reserve mac_len headroom when recompressed SRH + grows + +commit 9e6bf146b55999a095bb14f73a843942456d1adc upstream. + +ipv6_rpl_srh_rcv() decompresses an RFC 6554 Source Routing Header, swaps +the next segment into ipv6_hdr->daddr, recompresses, then pulls the old +header and pushes the new one plus the IPv6 header back. The +recompressed header can be larger than the received one when the swap +reduces the common-prefix length the segments share with daddr (CmprI=0, +CmprE>0, seg[0][0] != daddr[0] gives the maximum +8 bytes). + +pskb_expand_head() was gated on segments_left == 0, so on earlier +segments the push consumed unchecked headroom. Once skb_push() leaves +fewer than skb->mac_len bytes in front of data, +skb_mac_header_rebuild()'s call to: + + skb_set_mac_header(skb, -skb->mac_len); + +will store (data - head) - mac_len into the u16 mac_header field, which +wraps to ~65530, and the following memmove() writes mac_len bytes ~64KiB +past skb->head. + +A single AF_INET6/SOCK_RAW/IPV6_HDRINCL packet over lo with a two +segment type-3 SRH (CmprI=0, CmprE=15) reaches headroom 8 after one +pass; KASAN reports a 14-byte OOB write in ipv6_rthdr_rcv. + +Fix this by expanding the head whenever the remaining room is less than +the push size plus mac_len, and request that much extra so the rebuilt +MAC header fits afterwards. + +Fixes: 8610c7c6e3bd ("net: ipv6: add support for rpl sr exthdr") +Cc: stable +Reported-by: Anthropic +Signed-off-by: Greg Kroah-Hartman +Link: https://patch.msgid.link/2026042133-gout-unvented-1bd9@gregkh +Signed-off-by: Jakub Kicinski +Signed-off-by: Greg Kroah-Hartman + +diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c +index 8a30dd83cf0b..d09ae48030b3 100644 +--- a/net/ipv6/exthdrs.c ++++ b/net/ipv6/exthdrs.c +@@ -491,6 +491,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb) + struct net *net = dev_net(skb->dev); + struct inet6_dev *idev; + struct ipv6hdr *oldhdr; ++ unsigned int chdr_len; + unsigned char *buf; + int accept_rpl_seg; + int i, err; +@@ -594,8 +595,10 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb) + skb_pull(skb, ((hdr->hdrlen + 1) << 3)); + skb_postpull_rcsum(skb, oldhdr, + sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3)); +- if (unlikely(!hdr->segments_left)) { +- if (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0, ++ chdr_len = sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3); ++ if (unlikely(!hdr->segments_left || ++ skb_headroom(skb) < chdr_len + skb->mac_len)) { ++ if (pskb_expand_head(skb, chdr_len + skb->mac_len, 0, + GFP_ATOMIC)) { + __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS); + kfree_skb(skb); +@@ -605,7 +608,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb) + + oldhdr = ipv6_hdr(skb); + } +- skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr)); ++ skb_push(skb, chdr_len); + skb_reset_network_header(skb); + skb_mac_header_rebuild(skb); + skb_set_transport_header(skb, sizeof(struct ipv6hdr)); +-- +2.50.1 (Apple Git-155) + diff --git a/1300-fs-constify-file-ptr-in-backing-file-accessor-helpers.patch b/1300-fs-constify-file-ptr-in-backing-file-accessor-helpers.patch new file mode 100644 index 000000000..fce4e68ff --- /dev/null +++ b/1300-fs-constify-file-ptr-in-backing-file-accessor-helpers.patch @@ -0,0 +1,119 @@ +From 95ceb7510d751e76b068573ecbb2030cbd73852b Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Tue, 2 Jun 2026 11:09:13 +0200 +Subject: [PATCH] fs: constify file ptr in backing_file accessor helpers + +JIRA: https://issues.redhat.com/browse/RHEL-179440 + +commit 4e301d858af17ae2ce56886296e5458c5a08219a +Author: Amir Goldstein +Date: Sat Jun 7 13:53:03 2025 +0200 + + fs: constify file ptr in backing_file accessor helpers + + Add internal helper backing_file_set_user_path() for the only + two cases that need to modify backing_file fields. + + Signed-off-by: Amir Goldstein + Link: https://lore.kernel.org/20250607115304.2521155-2-amir73il@gmail.com + Signed-off-by: Christian Brauner + +Signed-off-by: Ondrej Mosnacek + +diff --git a/fs/backing-file.c b/fs/backing-file.c +index 09a9be945d45..892361c31c3d 100644 +--- a/fs/backing-file.c ++++ b/fs/backing-file.c +@@ -41,7 +41,7 @@ struct file *backing_file_open(const struct path *user_path, int flags, + return f; + + path_get(user_path); +- *backing_file_user_path(f) = *user_path; ++ backing_file_set_user_path(f, user_path); + error = vfs_open(real_path, f); + if (error) { + fput(f); +@@ -65,7 +65,7 @@ struct file *backing_tmpfile_open(const struct path *user_path, int flags, + return f; + + path_get(user_path); +- *backing_file_user_path(f) = *user_path; ++ backing_file_set_user_path(f, user_path); + error = vfs_tmpfile(real_idmap, real_parentpath, f, mode); + if (error) { + fput(f); +diff --git a/fs/file_table.c b/fs/file_table.c +index 15a3593c6ece..9979efb9a380 100644 +--- a/fs/file_table.c ++++ b/fs/file_table.c +@@ -53,17 +53,20 @@ struct backing_file { + }; + }; + +-static inline struct backing_file *backing_file(struct file *f) +-{ +- return container_of(f, struct backing_file, file); +-} ++#define backing_file(f) container_of(f, struct backing_file, file) + +-struct path *backing_file_user_path(struct file *f) ++struct path *backing_file_user_path(const struct file *f) + { + return &backing_file(f)->user_path; + } + EXPORT_SYMBOL_GPL(backing_file_user_path); + ++void backing_file_set_user_path(struct file *f, const struct path *path) ++{ ++ backing_file(f)->user_path = *path; ++} ++EXPORT_SYMBOL_GPL(backing_file_set_user_path); ++ + static inline void file_free(struct file *f) + { + security_file_free(f); +diff --git a/fs/internal.h b/fs/internal.h +index dd13dfde4adb..67e2e164c647 100644 +--- a/fs/internal.h ++++ b/fs/internal.h +@@ -100,6 +100,7 @@ extern void chroot_fs_refs(const struct path *, const struct path *); + struct file *alloc_empty_file(int flags, const struct cred *cred); + struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred); + struct file *alloc_empty_backing_file(int flags, const struct cred *cred); ++void backing_file_set_user_path(struct file *f, const struct path *path); + + static inline void file_put_write_access(struct file *file) + { +diff --git a/include/linux/fs.h b/include/linux/fs.h +index 83ddabaa0ac3..04309befc557 100644 +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -2793,7 +2793,7 @@ struct file *dentry_open(const struct path *path, int flags, + const struct cred *creds); + struct file *dentry_create(const struct path *path, int flags, umode_t mode, + const struct cred *cred); +-struct path *backing_file_user_path(struct file *f); ++struct path *backing_file_user_path(const struct file *f); + + /* + * When mmapping a file on a stackable filesystem (e.g., overlayfs), the file +@@ -2805,14 +2805,14 @@ struct path *backing_file_user_path(struct file *f); + * by fstat() on that same fd. + */ + /* Get the path to display in /proc//maps */ +-static inline const struct path *file_user_path(struct file *f) ++static inline const struct path *file_user_path(const struct file *f) + { + if (unlikely(f->f_mode & FMODE_BACKING)) + return backing_file_user_path(f); + return &f->f_path; + } + /* Get the inode whose inode number to display in /proc//maps */ +-static inline const struct inode *file_user_inode(struct file *f) ++static inline const struct inode *file_user_inode(const struct file *f) + { + if (unlikely(f->f_mode & FMODE_BACKING)) + return d_inode(backing_file_user_path(f)->dentry); +-- +2.50.1 (Apple Git-155) + diff --git a/1301-lsm-split-the-notifier-code-out-into-lsm-notifier-c.patch b/1301-lsm-split-the-notifier-code-out-into-lsm-notifier-c.patch new file mode 100644 index 000000000..56d7da485 --- /dev/null +++ b/1301-lsm-split-the-notifier-code-out-into-lsm-notifier-c.patch @@ -0,0 +1,122 @@ +From 6a5de58e1181a41f95ac002a27d95f1dbf5933c9 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Fri, 29 May 2026 17:24:58 +0200 +Subject: [PATCH] lsm: split the notifier code out into lsm_notifier.c + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit a5e7c17c810052e94dae36f1a976a052f4490458 +Author: Paul Moore +Date: Mon Feb 10 19:20:58 2025 -0500 + + lsm: split the notifier code out into lsm_notifier.c + + In an effort to decompose security/security.c somewhat to make it less + twisted and unwieldy, pull out the LSM notifier code into a new file + as it is fairly well self-contained. + + No code changes. + + Reviewed-by: Kees Cook + Reviewed-by: John Johansen + Reviewed-by: Casey Schaufler + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/Makefile b/security/Makefile +index cc0982214b84..c7a3f1cb2fd5 100644 +--- a/security/Makefile ++++ b/security/Makefile +@@ -11,7 +11,7 @@ obj-$(CONFIG_SECURITY) += lsm_syscalls.o + obj-$(CONFIG_MMU) += min_addr.o + + # Object file lists +-obj-$(CONFIG_SECURITY) += security.o ++obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o + obj-$(CONFIG_SECURITYFS) += inode.o + obj-$(CONFIG_SECURITY_SELINUX) += selinux/ + obj-$(CONFIG_SECURITY_SMACK) += smack/ +diff --git a/security/lsm_notifier.c b/security/lsm_notifier.c +new file mode 100644 +index 000000000000..c92fad5d57d4 +--- /dev/null ++++ b/security/lsm_notifier.c +@@ -0,0 +1,31 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * LSM notifier functions ++ * ++ */ ++ ++#include ++#include ++ ++static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain); ++ ++int call_blocking_lsm_notifier(enum lsm_event event, void *data) ++{ ++ return blocking_notifier_call_chain(&blocking_lsm_notifier_chain, ++ event, data); ++} ++EXPORT_SYMBOL(call_blocking_lsm_notifier); ++ ++int register_blocking_lsm_notifier(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_register(&blocking_lsm_notifier_chain, ++ nb); ++} ++EXPORT_SYMBOL(register_blocking_lsm_notifier); ++ ++int unregister_blocking_lsm_notifier(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain, ++ nb); ++} ++EXPORT_SYMBOL(unregister_blocking_lsm_notifier); +diff --git a/security/security.c b/security/security.c +index 4535ba7d0208..9a5f9a2f8ff3 100644 +--- a/security/security.c ++++ b/security/security.c +@@ -91,8 +91,6 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = { + [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality", + }; + +-static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain); +- + static struct kmem_cache *lsm_file_cache; + static struct kmem_cache *lsm_inode_cache; + +@@ -649,27 +647,6 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count, + } + } + +-int call_blocking_lsm_notifier(enum lsm_event event, void *data) +-{ +- return blocking_notifier_call_chain(&blocking_lsm_notifier_chain, +- event, data); +-} +-EXPORT_SYMBOL(call_blocking_lsm_notifier); +- +-int register_blocking_lsm_notifier(struct notifier_block *nb) +-{ +- return blocking_notifier_chain_register(&blocking_lsm_notifier_chain, +- nb); +-} +-EXPORT_SYMBOL(register_blocking_lsm_notifier); +- +-int unregister_blocking_lsm_notifier(struct notifier_block *nb) +-{ +- return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain, +- nb); +-} +-EXPORT_SYMBOL(unregister_blocking_lsm_notifier); +- + /** + * lsm_blob_alloc - allocate a composite blob + * @dest: the destination for the blob +-- +2.50.1 (Apple Git-155) + diff --git a/1302-lsm-split-the-init-code-out-into-lsm-init-c.patch b/1302-lsm-split-the-init-code-out-into-lsm-init-c.patch new file mode 100644 index 000000000..da63e8af9 --- /dev/null +++ b/1302-lsm-split-the-init-code-out-into-lsm-init-c.patch @@ -0,0 +1,1301 @@ +From 7aa3fb5951fb2b5d19fe0795285a4d2674f125f5 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Fri, 29 May 2026 17:29:20 +0200 +Subject: [PATCH] lsm: split the init code out into lsm_init.c + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 +Conflicts: + - security/security.c: + - conflict due to 5816bf4273ed ("lsm,selinux: Add LSM blob support + for BPF objects"), which is not backported + - need to also carry over bits from RHEL-only commit 1e3e3bce934f + ("CVE-2025-1272: security: Re-enable lockdown LSM in some + setup_arch()") + +commit 67a4b6a89b99aff0883114e4ecba4b11aedc29a5 +Author: Paul Moore +Date: Thu Feb 6 16:44:10 2025 -0500 + + lsm: split the init code out into lsm_init.c + + Continue to pull code out of security/security.c to help improve + readability by pulling all of the LSM framework initialization + code out into a new file. + + No code changes. + + Reviewed-by: Kees Cook + Reviewed-by: John Johansen + Reviewed-by: Casey Schaufler + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h +index 090d1d3e19fe..eeb4bfd60b79 100644 +--- a/include/linux/lsm_hooks.h ++++ b/include/linux/lsm_hooks.h +@@ -167,11 +167,10 @@ struct lsm_info { + __used __section(".early_lsm_info.init") \ + __aligned(sizeof(unsigned long)) + ++ + /* DO NOT tamper with these variables outside of the LSM framework */ + extern char *lsm_names; + extern struct lsm_static_calls_table static_calls_table __ro_after_init; +-extern struct lsm_info __start_lsm_info[], __end_lsm_info[]; +-extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[]; + + /** + * lsm_get_xattr_slot - Return the next available slot and increment the index +diff --git a/security/Makefile b/security/Makefile +index c7a3f1cb2fd5..b40b3740104c 100644 +--- a/security/Makefile ++++ b/security/Makefile +@@ -11,7 +11,7 @@ obj-$(CONFIG_SECURITY) += lsm_syscalls.o + obj-$(CONFIG_MMU) += min_addr.o + + # Object file lists +-obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o ++obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o lsm_init.o + obj-$(CONFIG_SECURITYFS) += inode.o + obj-$(CONFIG_SECURITY_SELINUX) += selinux/ + obj-$(CONFIG_SECURITY_SMACK) += smack/ +diff --git a/security/lsm.h b/security/lsm.h +new file mode 100644 +index 000000000000..0e1731bad4a7 +--- /dev/null ++++ b/security/lsm.h +@@ -0,0 +1,22 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * LSM functions ++ */ ++ ++#ifndef _LSM_H_ ++#define _LSM_H_ ++ ++#include ++ ++/* LSM blob configuration */ ++extern struct lsm_blob_sizes blob_sizes; ++ ++/* LSM blob caches */ ++extern struct kmem_cache *lsm_file_cache; ++extern struct kmem_cache *lsm_inode_cache; ++ ++/* LSM blob allocators */ ++int lsm_cred_alloc(struct cred *cred, gfp_t gfp); ++int lsm_task_alloc(struct task_struct *task); ++ ++#endif /* _LSM_H_ */ +diff --git a/security/lsm_init.c b/security/lsm_init.c +new file mode 100644 +index 000000000000..aa64e3437d25 +--- /dev/null ++++ b/security/lsm_init.c +@@ -0,0 +1,542 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * LSM initialization functions ++ */ ++ ++#define pr_fmt(fmt) "LSM: " fmt ++ ++#include ++#include ++ ++#include "lsm.h" ++ ++char *lsm_names; ++ ++/* Pointers to LSM sections defined in include/asm-generic/vmlinux.lds.h */ ++extern struct lsm_info __start_lsm_info[], __end_lsm_info[]; ++extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[]; ++ ++/* Boot-time LSM user choice */ ++static __initconst const char *const builtin_lsm_order = CONFIG_LSM; ++static __initdata const char *chosen_lsm_order; ++static __initdata const char *chosen_major_lsm; ++ ++/* Ordered list of LSMs to initialize. */ ++static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1]; ++static __initdata struct lsm_info *exclusive; ++ ++static __initdata bool debug; ++#define init_debug(...) \ ++ do { \ ++ if (debug) \ ++ pr_info(__VA_ARGS__); \ ++ } while (0) ++ ++static int lsm_append(const char *new, char **result); ++ ++/* Save user chosen LSM */ ++static int __init choose_major_lsm(char *str) ++{ ++ chosen_major_lsm = str; ++ return 1; ++} ++__setup("security=", choose_major_lsm); ++ ++/* Explicitly choose LSM initialization order. */ ++static int __init choose_lsm_order(char *str) ++{ ++ chosen_lsm_order = str; ++ return 1; ++} ++__setup("lsm=", choose_lsm_order); ++ ++/* Enable LSM order debugging. */ ++static int __init enable_debug(char *str) ++{ ++ debug = true; ++ return 1; ++} ++__setup("lsm.debug", enable_debug); ++ ++/* Mark an LSM's enabled flag. */ ++static int lsm_enabled_true __initdata = 1; ++static int lsm_enabled_false __initdata = 0; ++static void __init set_enabled(struct lsm_info *lsm, bool enabled) ++{ ++ /* ++ * When an LSM hasn't configured an enable variable, we can use ++ * a hard-coded location for storing the default enabled state. ++ */ ++ if (!lsm->enabled) { ++ if (enabled) ++ lsm->enabled = &lsm_enabled_true; ++ else ++ lsm->enabled = &lsm_enabled_false; ++ } else if (lsm->enabled == &lsm_enabled_true) { ++ if (!enabled) ++ lsm->enabled = &lsm_enabled_false; ++ } else if (lsm->enabled == &lsm_enabled_false) { ++ if (enabled) ++ lsm->enabled = &lsm_enabled_true; ++ } else { ++ *lsm->enabled = enabled; ++ } ++} ++ ++static inline bool is_enabled(struct lsm_info *lsm) ++{ ++ if (!lsm->enabled) ++ return false; ++ ++ return *lsm->enabled; ++} ++ ++/* Is an LSM already listed in the ordered LSMs list? */ ++static bool __init exists_ordered_lsm(struct lsm_info *lsm) ++{ ++ struct lsm_info **check; ++ ++ for (check = ordered_lsms; *check; check++) ++ if (*check == lsm) ++ return true; ++ ++ return false; ++} ++ ++/* Append an LSM to the list of ordered LSMs to initialize. */ ++static int last_lsm __initdata; ++static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) ++{ ++ /* Ignore duplicate selections. */ ++ if (exists_ordered_lsm(lsm)) ++ return; ++ ++ if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from)) ++ return; ++ ++ /* Enable this LSM, if it is not already set. */ ++ if (!lsm->enabled) ++ lsm->enabled = &lsm_enabled_true; ++ ordered_lsms[last_lsm++] = lsm; ++ ++ init_debug("%s ordered: %s (%s)\n", from, lsm->name, ++ is_enabled(lsm) ? "enabled" : "disabled"); ++} ++ ++/* Is an LSM allowed to be initialized? */ ++static bool __init lsm_allowed(struct lsm_info *lsm) ++{ ++ /* Skip if the LSM is disabled. */ ++ if (!is_enabled(lsm)) ++ return false; ++ ++ /* Not allowed if another exclusive LSM already initialized. */ ++ if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) { ++ init_debug("exclusive disabled: %s\n", lsm->name); ++ return false; ++ } ++ ++ return true; ++} ++ ++static void __init lsm_set_blob_size(int *need, int *lbs) ++{ ++ int offset; ++ ++ if (*need <= 0) ++ return; ++ ++ offset = ALIGN(*lbs, sizeof(void *)); ++ *lbs = offset + *need; ++ *need = offset; ++} ++ ++static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) ++{ ++ if (!needed) ++ return; ++ ++ lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred); ++ lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file); ++ lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib); ++ /* ++ * The inode blob gets an rcu_head in addition to ++ * what the modules might need. ++ */ ++ if (needed->lbs_inode && blob_sizes.lbs_inode == 0) ++ blob_sizes.lbs_inode = sizeof(struct rcu_head); ++ lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode); ++ lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc); ++ lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key); ++ lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg); ++ lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event); ++ lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock); ++ lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock); ++ lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task); ++ lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev); ++ lsm_set_blob_size(&needed->lbs_xattr_count, ++ &blob_sizes.lbs_xattr_count); ++ lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev); ++} ++ ++/* Prepare LSM for initialization. */ ++static void __init prepare_lsm(struct lsm_info *lsm) ++{ ++ int enabled = lsm_allowed(lsm); ++ ++ /* Record enablement (to handle any following exclusive LSMs). */ ++ set_enabled(lsm, enabled); ++ ++ /* If enabled, do pre-initialization work. */ ++ if (enabled) { ++ if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) { ++ exclusive = lsm; ++ init_debug("exclusive chosen: %s\n", lsm->name); ++ } ++ ++ lsm_set_blob_sizes(lsm->blobs); ++ } ++} ++ ++/* Initialize a given LSM, if it is enabled. */ ++static void __init initialize_lsm(struct lsm_info *lsm) ++{ ++ if (is_enabled(lsm)) { ++ int ret; ++ ++ init_debug("initializing %s\n", lsm->name); ++ ret = lsm->init(); ++ WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret); ++ } ++} ++ ++/* ++ * Current index to use while initializing the lsm id list. ++ */ ++u32 lsm_active_cnt __ro_after_init; ++const struct lsm_id *lsm_idlist[MAX_LSM_COUNT]; ++ ++/* Populate ordered LSMs list from comma-separated LSM name list. */ ++static void __init ordered_lsm_parse(const char *order, const char *origin) ++{ ++ struct lsm_info *lsm; ++ char *sep, *name, *next; ++ ++ /* LSM_ORDER_FIRST is always first. */ ++ for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { ++ if (lsm->order == LSM_ORDER_FIRST) ++ append_ordered_lsm(lsm, " first"); ++ } ++ ++ /* Process "security=", if given. */ ++ if (chosen_major_lsm) { ++ struct lsm_info *major; ++ ++ /* ++ * To match the original "security=" behavior, this ++ * explicitly does NOT fallback to another Legacy Major ++ * if the selected one was separately disabled: disable ++ * all non-matching Legacy Major LSMs. ++ */ ++ for (major = __start_lsm_info; major < __end_lsm_info; ++ major++) { ++ if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && ++ strcmp(major->name, chosen_major_lsm) != 0) { ++ set_enabled(major, false); ++ init_debug("security=%s disabled: %s (only one legacy major LSM)\n", ++ chosen_major_lsm, major->name); ++ } ++ } ++ } ++ ++ sep = kstrdup(order, GFP_KERNEL); ++ next = sep; ++ /* Walk the list, looking for matching LSMs. */ ++ while ((name = strsep(&next, ",")) != NULL) { ++ bool found = false; ++ ++ for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { ++ if (strcmp(lsm->name, name) == 0) { ++ if (lsm->order == LSM_ORDER_MUTABLE) ++ append_ordered_lsm(lsm, origin); ++ found = true; ++ } ++ } ++ ++ if (!found) ++ init_debug("%s ignored: %s (not built into kernel)\n", ++ origin, name); ++ } ++ ++ /* Process "security=", if given. */ ++ if (chosen_major_lsm) { ++ for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { ++ if (exists_ordered_lsm(lsm)) ++ continue; ++ if (strcmp(lsm->name, chosen_major_lsm) == 0) ++ append_ordered_lsm(lsm, "security="); ++ } ++ } ++ ++ /* LSM_ORDER_LAST is always last. */ ++ for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { ++ if (lsm->order == LSM_ORDER_LAST) ++ append_ordered_lsm(lsm, " last"); ++ } ++ ++ /* Disable all LSMs not in the ordered list. */ ++ for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { ++ if (exists_ordered_lsm(lsm)) ++ continue; ++ set_enabled(lsm, false); ++ init_debug("%s skipped: %s (not in requested order)\n", ++ origin, lsm->name); ++ } ++ ++ kfree(sep); ++} ++ ++static void __init report_lsm_order(void) ++{ ++ struct lsm_info **lsm, *early; ++ int first = 0; ++ ++ pr_info("initializing lsm="); ++ ++ /* Report each enabled LSM name, comma separated. */ ++ for (early = __start_early_lsm_info; ++ early < __end_early_lsm_info; early++) ++ if (is_enabled(early)) ++ pr_cont("%s%s", first++ == 0 ? "" : ",", early->name); ++ for (lsm = ordered_lsms; *lsm; lsm++) ++ if (is_enabled(*lsm)) ++ pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name); ++ ++ pr_cont("\n"); ++} ++ ++/** ++ * lsm_early_cred - during initialization allocate a composite cred blob ++ * @cred: the cred that needs a blob ++ * ++ * Allocate the cred blob for all the modules ++ */ ++static void __init lsm_early_cred(struct cred *cred) ++{ ++ int rc = lsm_cred_alloc(cred, GFP_KERNEL); ++ ++ if (rc) ++ panic("%s: Early cred alloc failed.\n", __func__); ++} ++ ++/** ++ * lsm_early_task - during initialization allocate a composite task blob ++ * @task: the task that needs a blob ++ * ++ * Allocate the task blob for all the modules ++ */ ++static void __init lsm_early_task(struct task_struct *task) ++{ ++ int rc = lsm_task_alloc(task); ++ ++ if (rc) ++ panic("%s: Early task alloc failed.\n", __func__); ++} ++ ++static void __init ordered_lsm_init(void) ++{ ++ struct lsm_info **lsm; ++ ++ if (chosen_lsm_order) { ++ if (chosen_major_lsm) { ++ pr_warn("security=%s is ignored because it is superseded by lsm=%s\n", ++ chosen_major_lsm, chosen_lsm_order); ++ chosen_major_lsm = NULL; ++ } ++ ordered_lsm_parse(chosen_lsm_order, "cmdline"); ++ } else ++ ordered_lsm_parse(builtin_lsm_order, "builtin"); ++ ++ for (lsm = ordered_lsms; *lsm; lsm++) ++ prepare_lsm(*lsm); ++ ++ report_lsm_order(); ++ ++ init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); ++ init_debug("file blob size = %d\n", blob_sizes.lbs_file); ++ init_debug("ib blob size = %d\n", blob_sizes.lbs_ib); ++ init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); ++ init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc); ++#ifdef CONFIG_KEYS ++ init_debug("key blob size = %d\n", blob_sizes.lbs_key); ++#endif /* CONFIG_KEYS */ ++ init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg); ++ init_debug("sock blob size = %d\n", blob_sizes.lbs_sock); ++ init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock); ++ init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event); ++ init_debug("task blob size = %d\n", blob_sizes.lbs_task); ++ init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev); ++ init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count); ++ init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev); ++ ++ /* ++ * Create any kmem_caches needed for blobs ++ */ ++ if (blob_sizes.lbs_file) ++ lsm_file_cache = kmem_cache_create("lsm_file_cache", ++ blob_sizes.lbs_file, 0, ++ SLAB_PANIC, NULL); ++ if (blob_sizes.lbs_inode) ++ lsm_inode_cache = kmem_cache_create("lsm_inode_cache", ++ blob_sizes.lbs_inode, 0, ++ SLAB_PANIC, NULL); ++ ++ lsm_early_cred((struct cred *) current->cred); ++ lsm_early_task(current); ++ for (lsm = ordered_lsms; *lsm; lsm++) ++ initialize_lsm(*lsm); ++} ++ ++static bool match_last_lsm(const char *list, const char *lsm) ++{ ++ const char *last; ++ ++ if (WARN_ON(!list || !lsm)) ++ return false; ++ last = strrchr(list, ','); ++ if (last) ++ /* Pass the comma, strcmp() will check for '\0' */ ++ last++; ++ else ++ last = list; ++ return !strcmp(last, lsm); ++} ++ ++static int lsm_append(const char *new, char **result) ++{ ++ char *cp; ++ ++ if (*result == NULL) { ++ *result = kstrdup(new, GFP_KERNEL); ++ if (*result == NULL) ++ return -ENOMEM; ++ } else { ++ /* Check if it is the last registered name */ ++ if (match_last_lsm(*result, new)) ++ return 0; ++ cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new); ++ if (cp == NULL) ++ return -ENOMEM; ++ kfree(*result); ++ *result = cp; ++ } ++ return 0; ++} ++ ++static void __init lsm_static_call_init(struct security_hook_list *hl) ++{ ++ struct lsm_static_call *scall = hl->scalls; ++ int i; ++ ++ for (i = 0; i < MAX_LSM_COUNT; i++) { ++ /* Update the first static call that is not used yet */ ++ if (!scall->hl) { ++ __static_call_update(scall->key, scall->trampoline, ++ hl->hook.lsm_func_addr); ++ scall->hl = hl; ++ static_branch_enable(scall->active); ++ return; ++ } ++ scall++; ++ } ++ panic("%s - Ran out of static slots.\n", __func__); ++} ++ ++/** ++ * security_add_hooks - Add a modules hooks to the hook lists. ++ * @hooks: the hooks to add ++ * @count: the number of hooks to add ++ * @lsmid: the identification information for the security module ++ * ++ * Each LSM has to register its hooks with the infrastructure. ++ */ ++void __init security_add_hooks(struct security_hook_list *hooks, int count, ++ const struct lsm_id *lsmid) ++{ ++ int i; ++ ++ /* ++ * A security module may call security_add_hooks() more ++ * than once during initialization, and LSM initialization ++ * is serialized. Landlock is one such case. ++ * Look at the previous entry, if there is one, for duplication. ++ */ ++ if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) { ++ if (lsm_active_cnt >= MAX_LSM_COUNT) ++ panic("%s Too many LSMs registered.\n", __func__); ++ lsm_idlist[lsm_active_cnt++] = lsmid; ++ } ++ ++ for (i = 0; i < count; i++) { ++ hooks[i].lsmid = lsmid; ++ lsm_static_call_init(&hooks[i]); ++ } ++ ++ /* ++ * Don't try to append during early_security_init(), we'll come back ++ * and fix this up afterwards. ++ */ ++ if (slab_is_available()) { ++ if (lsm_append(lsmid->name, &lsm_names) < 0) ++ panic("%s - Cannot get early memory.\n", __func__); ++ } ++} ++ ++int __init early_security_init(void) ++{ ++ struct lsm_info *lsm; ++ static bool early_security_initialized; ++ ++ if (early_security_initialized) ++ return 0; ++ ++ for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) { ++ if (!lsm->enabled) ++ lsm->enabled = &lsm_enabled_true; ++ prepare_lsm(lsm); ++ initialize_lsm(lsm); ++ } ++ ++ early_security_initialized = true; ++ return 0; ++} ++ ++/** ++ * security_init - initializes the security framework ++ * ++ * This should be called early in the kernel initialization sequence. ++ */ ++int __init security_init(void) ++{ ++ struct lsm_info *lsm; ++ ++ init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*"); ++ init_debug(" CONFIG_LSM=%s\n", builtin_lsm_order); ++ init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*"); ++ ++ /* ++ * Append the names of the early LSM modules now that kmalloc() is ++ * available ++ */ ++ for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) { ++ init_debug(" early started: %s (%s)\n", lsm->name, ++ is_enabled(lsm) ? "enabled" : "disabled"); ++ if (lsm->enabled) ++ lsm_append(lsm->name, &lsm_names); ++ } ++ ++ /* Load LSMs in specified order. */ ++ ordered_lsm_init(); ++ ++ return 0; ++} +diff --git a/security/security.c b/security/security.c +index 9a5f9a2f8ff3..6040891f4588 100644 +--- a/security/security.c ++++ b/security/security.c +@@ -33,24 +33,7 @@ + #include + #include + +-#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX +- +-/* +- * Identifier for the LSM static calls. +- * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h +- * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT +- */ +-#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX +- +-/* +- * Call the macro M for each LSM hook MAX_LSM_COUNT times. +- */ +-#define LSM_LOOP_UNROLL(M, ...) \ +-do { \ +- UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \ +-} while (0) +- +-#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) ++#include "lsm.h" + + /* + * These are descriptions of the reasons that can be passed to the +@@ -91,21 +74,29 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = { + [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality", + }; + +-static struct kmem_cache *lsm_file_cache; +-static struct kmem_cache *lsm_inode_cache; ++struct lsm_blob_sizes blob_sizes; + +-char *lsm_names; +-static struct lsm_blob_sizes blob_sizes __ro_after_init; ++struct kmem_cache *lsm_file_cache; ++struct kmem_cache *lsm_inode_cache; + +-/* Boot-time LSM user choice */ +-static __initdata const char *chosen_lsm_order; +-static __initdata const char *chosen_major_lsm; ++#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX + +-static __initconst const char *const builtin_lsm_order = CONFIG_LSM; ++/* ++ * Identifier for the LSM static calls. ++ * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h ++ * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT ++ */ ++#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX ++ ++/* ++ * Call the macro M for each LSM hook MAX_LSM_COUNT times. ++ */ ++#define LSM_LOOP_UNROLL(M, ...) \ ++do { \ ++ UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \ ++} while (0) + +-/* Ordered list of LSMs to initialize. */ +-static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1]; +-static __initdata struct lsm_info *exclusive; ++#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) + + #ifdef CONFIG_HAVE_STATIC_CALL + #define LSM_HOOK_TRAMP(NAME, NUM) \ +@@ -156,495 +147,25 @@ struct lsm_static_calls_table + #undef INIT_LSM_STATIC_CALL + }; + +-static __initdata bool debug; +-#define init_debug(...) \ +- do { \ +- if (debug) \ +- pr_info(__VA_ARGS__); \ +- } while (0) +- +-static bool __init is_enabled(struct lsm_info *lsm) +-{ +- if (!lsm->enabled) +- return false; +- +- return *lsm->enabled; +-} +- +-/* Mark an LSM's enabled flag. */ +-static int lsm_enabled_true __initdata = 1; +-static int lsm_enabled_false __initdata = 0; +-static void __init set_enabled(struct lsm_info *lsm, bool enabled) +-{ +- /* +- * When an LSM hasn't configured an enable variable, we can use +- * a hard-coded location for storing the default enabled state. +- */ +- if (!lsm->enabled) { +- if (enabled) +- lsm->enabled = &lsm_enabled_true; +- else +- lsm->enabled = &lsm_enabled_false; +- } else if (lsm->enabled == &lsm_enabled_true) { +- if (!enabled) +- lsm->enabled = &lsm_enabled_false; +- } else if (lsm->enabled == &lsm_enabled_false) { +- if (enabled) +- lsm->enabled = &lsm_enabled_true; +- } else { +- *lsm->enabled = enabled; +- } +-} +- +-/* Is an LSM already listed in the ordered LSMs list? */ +-static bool __init exists_ordered_lsm(struct lsm_info *lsm) +-{ +- struct lsm_info **check; +- +- for (check = ordered_lsms; *check; check++) +- if (*check == lsm) +- return true; +- +- return false; +-} +- +-/* Append an LSM to the list of ordered LSMs to initialize. */ +-static int last_lsm __initdata; +-static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) +-{ +- /* Ignore duplicate selections. */ +- if (exists_ordered_lsm(lsm)) +- return; +- +- if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from)) +- return; +- +- /* Enable this LSM, if it is not already set. */ +- if (!lsm->enabled) +- lsm->enabled = &lsm_enabled_true; +- ordered_lsms[last_lsm++] = lsm; +- +- init_debug("%s ordered: %s (%s)\n", from, lsm->name, +- is_enabled(lsm) ? "enabled" : "disabled"); +-} +- +-/* Is an LSM allowed to be initialized? */ +-static bool __init lsm_allowed(struct lsm_info *lsm) +-{ +- /* Skip if the LSM is disabled. */ +- if (!is_enabled(lsm)) +- return false; +- +- /* Not allowed if another exclusive LSM already initialized. */ +- if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) { +- init_debug("exclusive disabled: %s\n", lsm->name); +- return false; +- } +- +- return true; +-} +- +-static void __init lsm_set_blob_size(int *need, int *lbs) +-{ +- int offset; +- +- if (*need <= 0) +- return; +- +- offset = ALIGN(*lbs, sizeof(void *)); +- *lbs = offset + *need; +- *need = offset; +-} +- +-static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) +-{ +- if (!needed) +- return; +- +- lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred); +- lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file); +- lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib); +- /* +- * The inode blob gets an rcu_head in addition to +- * what the modules might need. +- */ +- if (needed->lbs_inode && blob_sizes.lbs_inode == 0) +- blob_sizes.lbs_inode = sizeof(struct rcu_head); +- lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode); +- lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc); +- lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key); +- lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg); +- lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event); +- lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock); +- lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock); +- lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task); +- lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev); +- lsm_set_blob_size(&needed->lbs_xattr_count, +- &blob_sizes.lbs_xattr_count); +- lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev); +-} +- +-/* Prepare LSM for initialization. */ +-static void __init prepare_lsm(struct lsm_info *lsm) +-{ +- int enabled = lsm_allowed(lsm); +- +- /* Record enablement (to handle any following exclusive LSMs). */ +- set_enabled(lsm, enabled); +- +- /* If enabled, do pre-initialization work. */ +- if (enabled) { +- if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) { +- exclusive = lsm; +- init_debug("exclusive chosen: %s\n", lsm->name); +- } +- +- lsm_set_blob_sizes(lsm->blobs); +- } +-} +- +-/* Initialize a given LSM, if it is enabled. */ +-static void __init initialize_lsm(struct lsm_info *lsm) +-{ +- if (is_enabled(lsm)) { +- int ret; +- +- init_debug("initializing %s\n", lsm->name); +- ret = lsm->init(); +- WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret); +- } +-} +- +-/* +- * Current index to use while initializing the lsm id list. +- */ +-u32 lsm_active_cnt __ro_after_init; +-const struct lsm_id *lsm_idlist[MAX_LSM_COUNT]; +- +-/* Populate ordered LSMs list from comma-separated LSM name list. */ +-static void __init ordered_lsm_parse(const char *order, const char *origin) +-{ +- struct lsm_info *lsm; +- char *sep, *name, *next; +- +- /* LSM_ORDER_FIRST is always first. */ +- for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { +- if (lsm->order == LSM_ORDER_FIRST) +- append_ordered_lsm(lsm, " first"); +- } +- +- /* Process "security=", if given. */ +- if (chosen_major_lsm) { +- struct lsm_info *major; +- +- /* +- * To match the original "security=" behavior, this +- * explicitly does NOT fallback to another Legacy Major +- * if the selected one was separately disabled: disable +- * all non-matching Legacy Major LSMs. +- */ +- for (major = __start_lsm_info; major < __end_lsm_info; +- major++) { +- if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && +- strcmp(major->name, chosen_major_lsm) != 0) { +- set_enabled(major, false); +- init_debug("security=%s disabled: %s (only one legacy major LSM)\n", +- chosen_major_lsm, major->name); +- } +- } +- } +- +- sep = kstrdup(order, GFP_KERNEL); +- next = sep; +- /* Walk the list, looking for matching LSMs. */ +- while ((name = strsep(&next, ",")) != NULL) { +- bool found = false; +- +- for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { +- if (strcmp(lsm->name, name) == 0) { +- if (lsm->order == LSM_ORDER_MUTABLE) +- append_ordered_lsm(lsm, origin); +- found = true; +- } +- } +- +- if (!found) +- init_debug("%s ignored: %s (not built into kernel)\n", +- origin, name); +- } +- +- /* Process "security=", if given. */ +- if (chosen_major_lsm) { +- for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { +- if (exists_ordered_lsm(lsm)) +- continue; +- if (strcmp(lsm->name, chosen_major_lsm) == 0) +- append_ordered_lsm(lsm, "security="); +- } +- } +- +- /* LSM_ORDER_LAST is always last. */ +- for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { +- if (lsm->order == LSM_ORDER_LAST) +- append_ordered_lsm(lsm, " last"); +- } +- +- /* Disable all LSMs not in the ordered list. */ +- for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { +- if (exists_ordered_lsm(lsm)) +- continue; +- set_enabled(lsm, false); +- init_debug("%s skipped: %s (not in requested order)\n", +- origin, lsm->name); +- } +- +- kfree(sep); +-} +- +-static void __init lsm_static_call_init(struct security_hook_list *hl) +-{ +- struct lsm_static_call *scall = hl->scalls; +- int i; +- +- for (i = 0; i < MAX_LSM_COUNT; i++) { +- /* Update the first static call that is not used yet */ +- if (!scall->hl) { +- __static_call_update(scall->key, scall->trampoline, +- hl->hook.lsm_func_addr); +- scall->hl = hl; +- static_branch_enable(scall->active); +- return; +- } +- scall++; +- } +- panic("%s - Ran out of static slots.\n", __func__); +-} +- +-static void __init lsm_early_cred(struct cred *cred); +-static void __init lsm_early_task(struct task_struct *task); +- +-static int lsm_append(const char *new, char **result); +- +-static void __init report_lsm_order(void) +-{ +- struct lsm_info **lsm, *early; +- int first = 0; +- +- pr_info("initializing lsm="); +- +- /* Report each enabled LSM name, comma separated. */ +- for (early = __start_early_lsm_info; +- early < __end_early_lsm_info; early++) +- if (is_enabled(early)) +- pr_cont("%s%s", first++ == 0 ? "" : ",", early->name); +- for (lsm = ordered_lsms; *lsm; lsm++) +- if (is_enabled(*lsm)) +- pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name); +- +- pr_cont("\n"); +-} +- +-static void __init ordered_lsm_init(void) +-{ +- struct lsm_info **lsm; +- +- if (chosen_lsm_order) { +- if (chosen_major_lsm) { +- pr_warn("security=%s is ignored because it is superseded by lsm=%s\n", +- chosen_major_lsm, chosen_lsm_order); +- chosen_major_lsm = NULL; +- } +- ordered_lsm_parse(chosen_lsm_order, "cmdline"); +- } else +- ordered_lsm_parse(builtin_lsm_order, "builtin"); +- +- for (lsm = ordered_lsms; *lsm; lsm++) +- prepare_lsm(*lsm); +- +- report_lsm_order(); +- +- init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); +- init_debug("file blob size = %d\n", blob_sizes.lbs_file); +- init_debug("ib blob size = %d\n", blob_sizes.lbs_ib); +- init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); +- init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc); +-#ifdef CONFIG_KEYS +- init_debug("key blob size = %d\n", blob_sizes.lbs_key); +-#endif /* CONFIG_KEYS */ +- init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg); +- init_debug("sock blob size = %d\n", blob_sizes.lbs_sock); +- init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock); +- init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event); +- init_debug("task blob size = %d\n", blob_sizes.lbs_task); +- init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev); +- init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count); +- init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev); +- +- /* +- * Create any kmem_caches needed for blobs +- */ +- if (blob_sizes.lbs_file) +- lsm_file_cache = kmem_cache_create("lsm_file_cache", +- blob_sizes.lbs_file, 0, +- SLAB_PANIC, NULL); +- if (blob_sizes.lbs_inode) +- lsm_inode_cache = kmem_cache_create("lsm_inode_cache", +- blob_sizes.lbs_inode, 0, +- SLAB_PANIC, NULL); +- +- lsm_early_cred((struct cred *) current->cred); +- lsm_early_task(current); +- for (lsm = ordered_lsms; *lsm; lsm++) +- initialize_lsm(*lsm); +-} +- +-int __init early_security_init(void) +-{ +- struct lsm_info *lsm; +- static bool early_security_initialized; +- +- if (early_security_initialized) +- return 0; +- +- for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) { +- if (!lsm->enabled) +- lsm->enabled = &lsm_enabled_true; +- prepare_lsm(lsm); +- initialize_lsm(lsm); +- } +- +- early_security_initialized = true; +- return 0; +-} +- + /** +- * security_init - initializes the security framework ++ * lsm_file_alloc - allocate a composite file blob ++ * @file: the file that needs a blob + * +- * This should be called early in the kernel initialization sequence. +- */ +-int __init security_init(void) +-{ +- struct lsm_info *lsm; +- +- init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*"); +- init_debug(" CONFIG_LSM=%s\n", builtin_lsm_order); +- init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*"); +- +- /* +- * Append the names of the early LSM modules now that kmalloc() is +- * available +- */ +- for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) { +- init_debug(" early started: %s (%s)\n", lsm->name, +- is_enabled(lsm) ? "enabled" : "disabled"); +- if (lsm->enabled) +- lsm_append(lsm->name, &lsm_names); +- } +- +- /* Load LSMs in specified order. */ +- ordered_lsm_init(); +- +- return 0; +-} +- +-/* Save user chosen LSM */ +-static int __init choose_major_lsm(char *str) +-{ +- chosen_major_lsm = str; +- return 1; +-} +-__setup("security=", choose_major_lsm); +- +-/* Explicitly choose LSM initialization order. */ +-static int __init choose_lsm_order(char *str) +-{ +- chosen_lsm_order = str; +- return 1; +-} +-__setup("lsm=", choose_lsm_order); +- +-/* Enable LSM order debugging. */ +-static int __init enable_debug(char *str) +-{ +- debug = true; +- return 1; +-} +-__setup("lsm.debug", enable_debug); +- +-static bool match_last_lsm(const char *list, const char *lsm) +-{ +- const char *last; +- +- if (WARN_ON(!list || !lsm)) +- return false; +- last = strrchr(list, ','); +- if (last) +- /* Pass the comma, strcmp() will check for '\0' */ +- last++; +- else +- last = list; +- return !strcmp(last, lsm); +-} +- +-static int lsm_append(const char *new, char **result) +-{ +- char *cp; +- +- if (*result == NULL) { +- *result = kstrdup(new, GFP_KERNEL); +- if (*result == NULL) +- return -ENOMEM; +- } else { +- /* Check if it is the last registered name */ +- if (match_last_lsm(*result, new)) +- return 0; +- cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new); +- if (cp == NULL) +- return -ENOMEM; +- kfree(*result); +- *result = cp; +- } +- return 0; +-} +- +-/** +- * security_add_hooks - Add a modules hooks to the hook lists. +- * @hooks: the hooks to add +- * @count: the number of hooks to add +- * @lsmid: the identification information for the security module ++ * Allocate the file blob for all the modules + * +- * Each LSM has to register its hooks with the infrastructure. ++ * Returns 0, or -ENOMEM if memory can't be allocated. + */ +-void __init security_add_hooks(struct security_hook_list *hooks, int count, +- const struct lsm_id *lsmid) ++static int lsm_file_alloc(struct file *file) + { +- int i; +- +- /* +- * A security module may call security_add_hooks() more +- * than once during initialization, and LSM initialization +- * is serialized. Landlock is one such case. +- * Look at the previous entry, if there is one, for duplication. +- */ +- if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) { +- if (lsm_active_cnt >= MAX_LSM_COUNT) +- panic("%s Too many LSMs registered.\n", __func__); +- lsm_idlist[lsm_active_cnt++] = lsmid; +- } +- +- for (i = 0; i < count; i++) { +- hooks[i].lsmid = lsmid; +- lsm_static_call_init(&hooks[i]); ++ if (!lsm_file_cache) { ++ file->f_security = NULL; ++ return 0; + } + +- /* +- * Don't try to append during early_security_init(), we'll come back +- * and fix this up afterwards. +- */ +- if (slab_is_available()) { +- if (lsm_append(lsmid->name, &lsm_names) < 0) +- panic("%s - Cannot get early memory.\n", __func__); +- } ++ file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL); ++ if (file->f_security == NULL) ++ return -ENOMEM; ++ return 0; + } + + /** +@@ -679,46 +200,11 @@ static int lsm_blob_alloc(void **dest, size_t size, gfp_t gfp) + * + * Returns 0, or -ENOMEM if memory can't be allocated. + */ +-static int lsm_cred_alloc(struct cred *cred, gfp_t gfp) ++int lsm_cred_alloc(struct cred *cred, gfp_t gfp) + { + return lsm_blob_alloc(&cred->security, blob_sizes.lbs_cred, gfp); + } + +-/** +- * lsm_early_cred - during initialization allocate a composite cred blob +- * @cred: the cred that needs a blob +- * +- * Allocate the cred blob for all the modules +- */ +-static void __init lsm_early_cred(struct cred *cred) +-{ +- int rc = lsm_cred_alloc(cred, GFP_KERNEL); +- +- if (rc) +- panic("%s: Early cred alloc failed.\n", __func__); +-} +- +-/** +- * lsm_file_alloc - allocate a composite file blob +- * @file: the file that needs a blob +- * +- * Allocate the file blob for all the modules +- * +- * Returns 0, or -ENOMEM if memory can't be allocated. +- */ +-static int lsm_file_alloc(struct file *file) +-{ +- if (!lsm_file_cache) { +- file->f_security = NULL; +- return 0; +- } +- +- file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL); +- if (file->f_security == NULL) +- return -ENOMEM; +- return 0; +-} +- + /** + * lsm_inode_alloc - allocate a composite inode blob + * @inode: the inode that needs a blob +@@ -749,7 +235,7 @@ static int lsm_inode_alloc(struct inode *inode, gfp_t gfp) + * + * Returns 0, or -ENOMEM if memory can't be allocated. + */ +-static int lsm_task_alloc(struct task_struct *task) ++int lsm_task_alloc(struct task_struct *task) + { + return lsm_blob_alloc(&task->security, blob_sizes.lbs_task, GFP_KERNEL); + } +@@ -818,20 +304,6 @@ static int lsm_bdev_alloc(struct block_device *bdev) + return 0; + } + +-/** +- * lsm_early_task - during initialization allocate a composite task blob +- * @task: the task that needs a blob +- * +- * Allocate the task blob for all the modules +- */ +-static void __init lsm_early_task(struct task_struct *task) +-{ +- int rc = lsm_task_alloc(task); +- +- if (rc) +- panic("%s: Early task alloc failed.\n", __func__); +-} +- + /** + * lsm_superblock_alloc - allocate a composite superblock blob + * @sb: the superblock that needs a blob +-- +2.50.1 (Apple Git-155) + diff --git a/1303-lsm-consolidate-lsm-allowed-and-prepare-lsm-into-lsm-prepare.patch b/1303-lsm-consolidate-lsm-allowed-and-prepare-lsm-into-lsm-prepare.patch new file mode 100644 index 000000000..5bece84b3 --- /dev/null +++ b/1303-lsm-consolidate-lsm-allowed-and-prepare-lsm-into-lsm-prepare.patch @@ -0,0 +1,167 @@ +From 204c54d19e2c1e71bf75a49bb07d965bee634401 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Fri, 29 May 2026 17:43:36 +0200 +Subject: [PATCH] lsm: consolidate lsm_allowed() and prepare_lsm() into + lsm_prepare() + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 +Conflicts: + - conflict due to 5816bf4273ed ("lsm,selinux: Add LSM blob support for + BPF objects"), which is not backported + +commit e02578561d47567be26e603c6d27c10a5aa4c2c4 +Author: Paul Moore +Date: Tue Feb 11 12:19:47 2025 -0500 + + lsm: consolidate lsm_allowed() and prepare_lsm() into lsm_prepare() + + Simplify and consolidate the lsm_allowed() and prepare_lsm() functions + into a new function, lsm_prepare(). + + Reviewed-by: Casey Schaufler + Reviewed-by: John Johansen + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm_init.c b/security/lsm_init.c +index aa64e3437d25..719835ec8f0a 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -123,22 +123,6 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) + is_enabled(lsm) ? "enabled" : "disabled"); + } + +-/* Is an LSM allowed to be initialized? */ +-static bool __init lsm_allowed(struct lsm_info *lsm) +-{ +- /* Skip if the LSM is disabled. */ +- if (!is_enabled(lsm)) +- return false; +- +- /* Not allowed if another exclusive LSM already initialized. */ +- if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) { +- init_debug("exclusive disabled: %s\n", lsm->name); +- return false; +- } +- +- return true; +-} +- + static void __init lsm_set_blob_size(int *need, int *lbs) + { + int offset; +@@ -151,51 +135,50 @@ static void __init lsm_set_blob_size(int *need, int *lbs) + *need = offset; + } + +-static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) ++/** ++ * lsm_prepare - Prepare the LSM framework for a new LSM ++ * @lsm: LSM definition ++ */ ++static void __init lsm_prepare(struct lsm_info *lsm) + { +- if (!needed) ++ struct lsm_blob_sizes *blobs; ++ ++ if (!is_enabled(lsm)) { ++ set_enabled(lsm, false); + return; ++ } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) { ++ init_debug("exclusive disabled: %s\n", lsm->name); ++ set_enabled(lsm, false); ++ return; ++ } + +- lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred); +- lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file); +- lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib); +- /* +- * The inode blob gets an rcu_head in addition to +- * what the modules might need. +- */ +- if (needed->lbs_inode && blob_sizes.lbs_inode == 0) ++ /* Mark the LSM as enabled. */ ++ set_enabled(lsm, true); ++ if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) { ++ init_debug("exclusive chosen: %s\n", lsm->name); ++ exclusive = lsm; ++ } ++ ++ /* Register the LSM blob sizes. */ ++ blobs = lsm->blobs; ++ lsm_set_blob_size(&blobs->lbs_cred, &blob_sizes.lbs_cred); ++ lsm_set_blob_size(&blobs->lbs_file, &blob_sizes.lbs_file); ++ lsm_set_blob_size(&blobs->lbs_ib, &blob_sizes.lbs_ib); ++ /* inode blob gets an rcu_head in addition to LSM blobs. */ ++ if (blobs->lbs_inode && blob_sizes.lbs_inode == 0) + blob_sizes.lbs_inode = sizeof(struct rcu_head); +- lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode); +- lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc); +- lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key); +- lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg); +- lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event); +- lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock); +- lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock); +- lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task); +- lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev); +- lsm_set_blob_size(&needed->lbs_xattr_count, ++ lsm_set_blob_size(&blobs->lbs_inode, &blob_sizes.lbs_inode); ++ lsm_set_blob_size(&blobs->lbs_ipc, &blob_sizes.lbs_ipc); ++ lsm_set_blob_size(&blobs->lbs_key, &blob_sizes.lbs_key); ++ lsm_set_blob_size(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg); ++ lsm_set_blob_size(&blobs->lbs_perf_event, &blob_sizes.lbs_perf_event); ++ lsm_set_blob_size(&blobs->lbs_sock, &blob_sizes.lbs_sock); ++ lsm_set_blob_size(&blobs->lbs_superblock, &blob_sizes.lbs_superblock); ++ lsm_set_blob_size(&blobs->lbs_task, &blob_sizes.lbs_task); ++ lsm_set_blob_size(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev); ++ lsm_set_blob_size(&blobs->lbs_xattr_count, + &blob_sizes.lbs_xattr_count); +- lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev); +-} +- +-/* Prepare LSM for initialization. */ +-static void __init prepare_lsm(struct lsm_info *lsm) +-{ +- int enabled = lsm_allowed(lsm); +- +- /* Record enablement (to handle any following exclusive LSMs). */ +- set_enabled(lsm, enabled); +- +- /* If enabled, do pre-initialization work. */ +- if (enabled) { +- if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) { +- exclusive = lsm; +- init_debug("exclusive chosen: %s\n", lsm->name); +- } +- +- lsm_set_blob_sizes(lsm->blobs); +- } ++ lsm_set_blob_size(&blobs->lbs_bdev, &blob_sizes.lbs_bdev); + } + + /* Initialize a given LSM, if it is enabled. */ +@@ -358,7 +341,7 @@ static void __init ordered_lsm_init(void) + ordered_lsm_parse(builtin_lsm_order, "builtin"); + + for (lsm = ordered_lsms; *lsm; lsm++) +- prepare_lsm(*lsm); ++ lsm_prepare(*lsm); + + report_lsm_order(); + +@@ -503,7 +486,7 @@ int __init early_security_init(void) + for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) { + if (!lsm->enabled) + lsm->enabled = &lsm_enabled_true; +- prepare_lsm(lsm); ++ lsm_prepare(lsm); + initialize_lsm(lsm); + } + +-- +2.50.1 (Apple Git-155) + diff --git a/1304-lsm-introduce-looping-macros-for-the-initialization-code.patch b/1304-lsm-introduce-looping-macros-for-the-initialization-code.patch new file mode 100644 index 000000000..255dbb2be --- /dev/null +++ b/1304-lsm-introduce-looping-macros-for-the-initialization-code.patch @@ -0,0 +1,172 @@ +From 92ed776aa54bac14d013ad9b08ad44b7ed8af0ea Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Fri, 29 May 2026 18:31:38 +0200 +Subject: [PATCH] lsm: introduce looping macros for the initialization code + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit 37f788f65528611f4482e2135d11ca34afb25828 +Author: Paul Moore +Date: Wed Apr 9 17:59:42 2025 -0400 + + lsm: introduce looping macros for the initialization code + + There are three common for loop patterns in the LSM initialization code + to loop through the ordered LSM list and the registered "early" LSMs. + This patch implements these loop patterns as macros to help simplify the + code and reduce the chance for errors. + + Reviewed-by: Casey Schaufler + Reviewed-by: John Johansen + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm_init.c b/security/lsm_init.c +index 719835ec8f0a..2424975ca0eb 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -32,6 +32,15 @@ static __initdata bool debug; + pr_info(__VA_ARGS__); \ + } while (0) + ++#define lsm_order_for_each(iter) \ ++ for ((iter) = ordered_lsms; *(iter); (iter)++) ++#define lsm_for_each_raw(iter) \ ++ for ((iter) = __start_lsm_info; \ ++ (iter) < __end_lsm_info; (iter)++) ++#define lsm_early_for_each_raw(iter) \ ++ for ((iter) = __start_early_lsm_info; \ ++ (iter) < __end_early_lsm_info; (iter)++) ++ + static int lsm_append(const char *new, char **result); + + /* Save user chosen LSM */ +@@ -96,9 +105,10 @@ static bool __init exists_ordered_lsm(struct lsm_info *lsm) + { + struct lsm_info **check; + +- for (check = ordered_lsms; *check; check++) ++ lsm_order_for_each(check) { + if (*check == lsm) + return true; ++ } + + return false; + } +@@ -206,7 +216,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + char *sep, *name, *next; + + /* LSM_ORDER_FIRST is always first. */ +- for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { ++ lsm_for_each_raw(lsm) { + if (lsm->order == LSM_ORDER_FIRST) + append_ordered_lsm(lsm, " first"); + } +@@ -221,8 +231,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + * if the selected one was separately disabled: disable + * all non-matching Legacy Major LSMs. + */ +- for (major = __start_lsm_info; major < __end_lsm_info; +- major++) { ++ lsm_for_each_raw(major) { + if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && + strcmp(major->name, chosen_major_lsm) != 0) { + set_enabled(major, false); +@@ -238,7 +247,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + while ((name = strsep(&next, ",")) != NULL) { + bool found = false; + +- for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { ++ lsm_for_each_raw(lsm) { + if (strcmp(lsm->name, name) == 0) { + if (lsm->order == LSM_ORDER_MUTABLE) + append_ordered_lsm(lsm, origin); +@@ -253,7 +262,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + + /* Process "security=", if given. */ + if (chosen_major_lsm) { +- for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { ++ lsm_for_each_raw(lsm) { + if (exists_ordered_lsm(lsm)) + continue; + if (strcmp(lsm->name, chosen_major_lsm) == 0) +@@ -262,13 +271,13 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + } + + /* LSM_ORDER_LAST is always last. */ +- for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { ++ lsm_for_each_raw(lsm) { + if (lsm->order == LSM_ORDER_LAST) + append_ordered_lsm(lsm, " last"); + } + + /* Disable all LSMs not in the ordered list. */ +- for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { ++ lsm_for_each_raw(lsm) { + if (exists_ordered_lsm(lsm)) + continue; + set_enabled(lsm, false); +@@ -287,13 +296,14 @@ static void __init report_lsm_order(void) + pr_info("initializing lsm="); + + /* Report each enabled LSM name, comma separated. */ +- for (early = __start_early_lsm_info; +- early < __end_early_lsm_info; early++) ++ lsm_early_for_each_raw(early) { + if (is_enabled(early)) + pr_cont("%s%s", first++ == 0 ? "" : ",", early->name); +- for (lsm = ordered_lsms; *lsm; lsm++) ++ } ++ lsm_order_for_each(lsm) { + if (is_enabled(*lsm)) + pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name); ++ } + + pr_cont("\n"); + } +@@ -340,8 +350,9 @@ static void __init ordered_lsm_init(void) + } else + ordered_lsm_parse(builtin_lsm_order, "builtin"); + +- for (lsm = ordered_lsms; *lsm; lsm++) ++ lsm_order_for_each(lsm) { + lsm_prepare(*lsm); ++ } + + report_lsm_order(); + +@@ -376,8 +387,9 @@ static void __init ordered_lsm_init(void) + + lsm_early_cred((struct cred *) current->cred); + lsm_early_task(current); +- for (lsm = ordered_lsms; *lsm; lsm++) ++ lsm_order_for_each(lsm) { + initialize_lsm(*lsm); ++ } + } + + static bool match_last_lsm(const char *list, const char *lsm) +@@ -483,7 +495,7 @@ int __init early_security_init(void) + if (early_security_initialized) + return 0; + +- for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) { ++ lsm_early_for_each_raw(lsm) { + if (!lsm->enabled) + lsm->enabled = &lsm_enabled_true; + lsm_prepare(lsm); +@@ -511,7 +523,7 @@ int __init security_init(void) + * Append the names of the early LSM modules now that kmalloc() is + * available + */ +- for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) { ++ lsm_early_for_each_raw(lsm) { + init_debug(" early started: %s (%s)\n", lsm->name, + is_enabled(lsm) ? "enabled" : "disabled"); + if (lsm->enabled) +-- +2.50.1 (Apple Git-155) + diff --git a/1305-lsm-integrate-report-lsm-order-code-into-caller.patch b/1305-lsm-integrate-report-lsm-order-code-into-caller.patch new file mode 100644 index 000000000..b7d321e3c --- /dev/null +++ b/1305-lsm-integrate-report-lsm-order-code-into-caller.patch @@ -0,0 +1,86 @@ +From 4190f2549074d006122a76cdcb2a5b361d59d650 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:20:31 +0200 +Subject: [PATCH] lsm: integrate report_lsm_order() code into caller + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit cb1513db7a6ed82d22853608d78bbf72ad8c67c1 +Author: Paul Moore +Date: Sat Jul 12 16:10:15 2025 -0400 + + lsm: integrate report_lsm_order() code into caller + + With only one caller of report_lsm_order(), insert the function's code + directly into the caller and ger rid of report_lsm_order(). + + Reviewed-by: Casey Schaufler + Reviewed-by: John Johansen + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm_init.c b/security/lsm_init.c +index 2424975ca0eb..fc40d37c3321 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -288,26 +288,6 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + kfree(sep); + } + +-static void __init report_lsm_order(void) +-{ +- struct lsm_info **lsm, *early; +- int first = 0; +- +- pr_info("initializing lsm="); +- +- /* Report each enabled LSM name, comma separated. */ +- lsm_early_for_each_raw(early) { +- if (is_enabled(early)) +- pr_cont("%s%s", first++ == 0 ? "" : ",", early->name); +- } +- lsm_order_for_each(lsm) { +- if (is_enabled(*lsm)) +- pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name); +- } +- +- pr_cont("\n"); +-} +- + /** + * lsm_early_cred - during initialization allocate a composite cred blob + * @cred: the cred that needs a blob +@@ -338,7 +318,9 @@ static void __init lsm_early_task(struct task_struct *task) + + static void __init ordered_lsm_init(void) + { ++ unsigned int first = 0; + struct lsm_info **lsm; ++ struct lsm_info *early; + + if (chosen_lsm_order) { + if (chosen_major_lsm) { +@@ -354,7 +336,16 @@ static void __init ordered_lsm_init(void) + lsm_prepare(*lsm); + } + +- report_lsm_order(); ++ pr_info("initializing lsm="); ++ lsm_early_for_each_raw(early) { ++ if (is_enabled(early)) ++ pr_cont("%s%s", first++ == 0 ? "" : ",", early->name); ++ } ++ lsm_order_for_each(lsm) { ++ if (is_enabled(*lsm)) ++ pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name); ++ } ++ pr_cont("\n"); + + init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); + init_debug("file blob size = %d\n", blob_sizes.lbs_file); +-- +2.50.1 (Apple Git-155) + diff --git a/1306-lsm-integrate-lsm-early-cred-and-lsm-early-task-into-caller.patch b/1306-lsm-integrate-lsm-early-cred-and-lsm-early-task-into-caller.patch new file mode 100644 index 000000000..885a0733a --- /dev/null +++ b/1306-lsm-integrate-lsm-early-cred-and-lsm-early-task-into-caller.patch @@ -0,0 +1,82 @@ +From 7a7bb4c213345231e80b7545d00bcee30e62fd94 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:20:32 +0200 +Subject: [PATCH] lsm: integrate lsm_early_cred() and lsm_early_task() into + caller + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit 92ed3500c9a91f43e094c9b8fb4bab9976565d74 +Author: Paul Moore +Date: Sat Jul 12 16:27:39 2025 -0400 + + lsm: integrate lsm_early_cred() and lsm_early_task() into caller + + With only one caller of lsm_early_cred() and lsm_early_task(), insert + the functions' code directly into the caller and ger rid of the two + functions. + + Reviewed-by: Casey Schaufler + Reviewed-by: John Johansen + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm_init.c b/security/lsm_init.c +index fc40d37c3321..19eac2b39f2b 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -288,34 +288,6 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + kfree(sep); + } + +-/** +- * lsm_early_cred - during initialization allocate a composite cred blob +- * @cred: the cred that needs a blob +- * +- * Allocate the cred blob for all the modules +- */ +-static void __init lsm_early_cred(struct cred *cred) +-{ +- int rc = lsm_cred_alloc(cred, GFP_KERNEL); +- +- if (rc) +- panic("%s: Early cred alloc failed.\n", __func__); +-} +- +-/** +- * lsm_early_task - during initialization allocate a composite task blob +- * @task: the task that needs a blob +- * +- * Allocate the task blob for all the modules +- */ +-static void __init lsm_early_task(struct task_struct *task) +-{ +- int rc = lsm_task_alloc(task); +- +- if (rc) +- panic("%s: Early task alloc failed.\n", __func__); +-} +- + static void __init ordered_lsm_init(void) + { + unsigned int first = 0; +@@ -376,8 +348,11 @@ static void __init ordered_lsm_init(void) + blob_sizes.lbs_inode, 0, + SLAB_PANIC, NULL); + +- lsm_early_cred((struct cred *) current->cred); +- lsm_early_task(current); ++ if (lsm_cred_alloc((struct cred __rcu *)current->cred, GFP_KERNEL)) ++ panic("%s: early cred alloc failed.\n", __func__); ++ if (lsm_task_alloc(current)) ++ panic("%s: early task alloc failed.\n", __func__); ++ + lsm_order_for_each(lsm) { + initialize_lsm(*lsm); + } +-- +2.50.1 (Apple Git-155) + diff --git a/1307-lsm-rename-ordered-lsm-init-to-lsm-init-ordered.patch b/1307-lsm-rename-ordered-lsm-init-to-lsm-init-ordered.patch new file mode 100644 index 000000000..1cc768124 --- /dev/null +++ b/1307-lsm-rename-ordered-lsm-init-to-lsm-init-ordered.patch @@ -0,0 +1,63 @@ +From c2e5c69d0c70768dc6cd10ab0a5704ebb2545967 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:20:32 +0200 +Subject: [PATCH] lsm: rename ordered_lsm_init() to lsm_init_ordered() + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit faabedcd6e88ca1f65ef45d711d2e0c7288fd551 +Author: Paul Moore +Date: Tue Feb 11 12:59:30 2025 -0500 + + lsm: rename ordered_lsm_init() to lsm_init_ordered() + + The new name more closely fits the rest of the naming scheme in + security/lsm_init.c. This patch also adds a trivial comment block to + the top of the function. + + Reviewed-by: Casey Schaufler + Reviewed-by: John Johansen + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm_init.c b/security/lsm_init.c +index 19eac2b39f2b..a590a785f724 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -288,7 +288,10 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + kfree(sep); + } + +-static void __init ordered_lsm_init(void) ++/** ++ * lsm_init_ordered - Initialize the ordered LSMs ++ */ ++static void __init lsm_init_ordered(void) + { + unsigned int first = 0; + struct lsm_info **lsm; +@@ -336,9 +339,6 @@ static void __init ordered_lsm_init(void) + init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count); + init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev); + +- /* +- * Create any kmem_caches needed for blobs +- */ + if (blob_sizes.lbs_file) + lsm_file_cache = kmem_cache_create("lsm_file_cache", + blob_sizes.lbs_file, 0, +@@ -497,7 +497,7 @@ int __init security_init(void) + } + + /* Load LSMs in specified order. */ +- ordered_lsm_init(); ++ lsm_init_ordered(); + + return 0; + } +-- +2.50.1 (Apple Git-155) + diff --git a/1308-lsm-replace-the-name-field-with-a-pointer-to-the-lsm-id-stru.patch b/1308-lsm-replace-the-name-field-with-a-pointer-to-the-lsm-id-stru.patch new file mode 100644 index 000000000..e512fdcbe --- /dev/null +++ b/1308-lsm-replace-the-name-field-with-a-pointer-to-the-lsm-id-stru.patch @@ -0,0 +1,363 @@ +From 369918c7a55af0dc1d94780d5fde14536636ace1 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:20:33 +0200 +Subject: [PATCH] lsm: replace the name field with a pointer to the lsm_id + struct + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit 9f9dc69e06ecbc61e7a50b823b82a78daf130dc0 +Author: Paul Moore +Date: Wed Feb 12 14:45:06 2025 -0500 + + lsm: replace the name field with a pointer to the lsm_id struct + + Reduce the duplication between the lsm_id struct and the DEFINE_LSM() + definition by linking the lsm_id struct directly into the individual + LSM's DEFINE_LSM() instance. + + Linking the lsm_id into the LSM definition also allows us to simplify + the security_add_hooks() function by removing the code which populates + the lsm_idlist[] array and moving it into the normal LSM startup code + where the LSM list is parsed and the individual LSMs are enabled, + making for a cleaner implementation with less overhead at boot. + + Reviewed-by: Kees Cook + Reviewed-by: John Johansen + Reviewed-by: Casey Schaufler + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h +index eeb4bfd60b79..4cd17c9a229f 100644 +--- a/include/linux/lsm_hooks.h ++++ b/include/linux/lsm_hooks.h +@@ -149,7 +149,7 @@ enum lsm_order { + }; + + struct lsm_info { +- const char *name; /* Required. */ ++ const struct lsm_id *id; + enum lsm_order order; /* Optional: default is LSM_ORDER_MUTABLE */ + unsigned long flags; /* Optional: flags describing LSM */ + int *enabled; /* Optional: controlled by CONFIG_LSM */ +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index 248d68624b35..6b5742e60454 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -2275,7 +2275,7 @@ static int __init apparmor_init(void) + } + + DEFINE_LSM(apparmor) = { +- .name = "apparmor", ++ .id = &apparmor_lsmid, + .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, + .enabled = &apparmor_enabled, + .blobs = &apparmor_blob_sizes, +diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c +index db759025abe1..40efde233f3a 100644 +--- a/security/bpf/hooks.c ++++ b/security/bpf/hooks.c +@@ -33,7 +33,7 @@ struct lsm_blob_sizes bpf_lsm_blob_sizes __ro_after_init = { + }; + + DEFINE_LSM(bpf) = { +- .name = "bpf", ++ .id = &bpf_lsmid, + .init = bpf_lsm_init, + .blobs = &bpf_lsm_blob_sizes + }; +diff --git a/security/commoncap.c b/security/commoncap.c +index cefad323a0b1..7d0b5a58d4e1 100644 +--- a/security/commoncap.c ++++ b/security/commoncap.c +@@ -1470,7 +1470,7 @@ static int __init capability_init(void) + } + + DEFINE_LSM(capability) = { +- .name = "capability", ++ .id = &capability_lsmid, + .order = LSM_ORDER_FIRST, + .init = capability_init, + }; +diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c +index 377e57e9084f..bb0f8631195e 100644 +--- a/security/integrity/evm/evm_main.c ++++ b/security/integrity/evm/evm_main.c +@@ -1175,7 +1175,7 @@ struct lsm_blob_sizes evm_blob_sizes __ro_after_init = { + }; + + DEFINE_LSM(evm) = { +- .name = "evm", ++ .id = &evm_lsmid, + .init = init_evm_lsm, + .order = LSM_ORDER_LAST, + .blobs = &evm_blob_sizes, +diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c +index 66aa7bc8d76d..c60a9b1082d4 100644 +--- a/security/integrity/ima/ima_main.c ++++ b/security/integrity/ima/ima_main.c +@@ -1222,7 +1222,7 @@ struct lsm_blob_sizes ima_blob_sizes __ro_after_init = { + }; + + DEFINE_LSM(ima) = { +- .name = "ima", ++ .id = &ima_lsmid, + .init = init_ima_lsm, + .order = LSM_ORDER_LAST, + .blobs = &ima_blob_sizes, +diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c +index 4317134cb0da..2426441181dc 100644 +--- a/security/ipe/ipe.c ++++ b/security/ipe/ipe.c +@@ -92,7 +92,7 @@ static int __init ipe_init(void) + } + + DEFINE_LSM(ipe) = { +- .name = "ipe", ++ .id = &ipe_lsmid, + .init = ipe_init, + .blobs = &ipe_blobs, + }; +diff --git a/security/landlock/setup.c b/security/landlock/setup.c +index 28519a45b11f..460c5b79d957 100644 +--- a/security/landlock/setup.c ++++ b/security/landlock/setup.c +@@ -43,7 +43,7 @@ static int __init landlock_init(void) + } + + DEFINE_LSM(LANDLOCK_NAME) = { +- .name = LANDLOCK_NAME, ++ .id = &landlock_lsmid, + .init = landlock_init, + .blobs = &landlock_blob_sizes, + }; +diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c +index 02144ec39f43..92c9618e38ad 100644 +--- a/security/loadpin/loadpin.c ++++ b/security/loadpin/loadpin.c +@@ -271,7 +271,7 @@ static int __init loadpin_init(void) + } + + DEFINE_LSM(loadpin) = { +- .name = "loadpin", ++ .id = &loadpin_lsmid, + .init = loadpin_init, + }; + +diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c +index ddf496f7fca5..c27aaee60da3 100644 +--- a/security/lockdown/lockdown.c ++++ b/security/lockdown/lockdown.c +@@ -169,6 +169,6 @@ DEFINE_EARLY_LSM(lockdown) = { + #else + DEFINE_LSM(lockdown) = { + #endif +- .name = "lockdown", ++ .id = &lockdown_lsmid, + .init = lockdown_lsm_init, + }; +diff --git a/security/lsm_init.c b/security/lsm_init.c +index a590a785f724..e5faf6be44fb 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -127,9 +127,10 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) + /* Enable this LSM, if it is not already set. */ + if (!lsm->enabled) + lsm->enabled = &lsm_enabled_true; +- ordered_lsms[last_lsm++] = lsm; ++ ordered_lsms[last_lsm] = lsm; ++ lsm_idlist[last_lsm++] = lsm->id; + +- init_debug("%s ordered: %s (%s)\n", from, lsm->name, ++ init_debug("%s ordered: %s (%s)\n", from, lsm->id->name, + is_enabled(lsm) ? "enabled" : "disabled"); + } + +@@ -157,7 +158,7 @@ static void __init lsm_prepare(struct lsm_info *lsm) + set_enabled(lsm, false); + return; + } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) { +- init_debug("exclusive disabled: %s\n", lsm->name); ++ init_debug("exclusive disabled: %s\n", lsm->id->name); + set_enabled(lsm, false); + return; + } +@@ -165,7 +166,7 @@ static void __init lsm_prepare(struct lsm_info *lsm) + /* Mark the LSM as enabled. */ + set_enabled(lsm, true); + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) { +- init_debug("exclusive chosen: %s\n", lsm->name); ++ init_debug("exclusive chosen: %s\n", lsm->id->name); + exclusive = lsm; + } + +@@ -197,9 +198,9 @@ static void __init initialize_lsm(struct lsm_info *lsm) + if (is_enabled(lsm)) { + int ret; + +- init_debug("initializing %s\n", lsm->name); ++ init_debug("initializing %s\n", lsm->id->name); + ret = lsm->init(); +- WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret); ++ WARN(ret, "%s failed to initialize: %d\n", lsm->id->name, ret); + } + } + +@@ -233,10 +234,10 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + */ + lsm_for_each_raw(major) { + if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && +- strcmp(major->name, chosen_major_lsm) != 0) { ++ strcmp(major->id->name, chosen_major_lsm) != 0) { + set_enabled(major, false); + init_debug("security=%s disabled: %s (only one legacy major LSM)\n", +- chosen_major_lsm, major->name); ++ chosen_major_lsm, major->id->name); + } + } + } +@@ -248,7 +249,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + bool found = false; + + lsm_for_each_raw(lsm) { +- if (strcmp(lsm->name, name) == 0) { ++ if (strcmp(lsm->id->name, name) == 0) { + if (lsm->order == LSM_ORDER_MUTABLE) + append_ordered_lsm(lsm, origin); + found = true; +@@ -265,7 +266,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + lsm_for_each_raw(lsm) { + if (exists_ordered_lsm(lsm)) + continue; +- if (strcmp(lsm->name, chosen_major_lsm) == 0) ++ if (strcmp(lsm->id->name, chosen_major_lsm) == 0) + append_ordered_lsm(lsm, "security="); + } + } +@@ -282,7 +283,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + continue; + set_enabled(lsm, false); + init_debug("%s skipped: %s (not in requested order)\n", +- origin, lsm->name); ++ origin, lsm->id->name); + } + + kfree(sep); +@@ -314,11 +315,13 @@ static void __init lsm_init_ordered(void) + pr_info("initializing lsm="); + lsm_early_for_each_raw(early) { + if (is_enabled(early)) +- pr_cont("%s%s", first++ == 0 ? "" : ",", early->name); ++ pr_cont("%s%s", ++ first++ == 0 ? "" : ",", early->id->name); + } + lsm_order_for_each(lsm) { + if (is_enabled(*lsm)) +- pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name); ++ pr_cont("%s%s", ++ first++ == 0 ? "" : ",", (*lsm)->id->name); + } + pr_cont("\n"); + +@@ -426,18 +429,6 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count, + { + int i; + +- /* +- * A security module may call security_add_hooks() more +- * than once during initialization, and LSM initialization +- * is serialized. Landlock is one such case. +- * Look at the previous entry, if there is one, for duplication. +- */ +- if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) { +- if (lsm_active_cnt >= MAX_LSM_COUNT) +- panic("%s Too many LSMs registered.\n", __func__); +- lsm_idlist[lsm_active_cnt++] = lsmid; +- } +- + for (i = 0; i < count; i++) { + hooks[i].lsmid = lsmid; + lsm_static_call_init(&hooks[i]); +@@ -490,10 +481,10 @@ int __init security_init(void) + * available + */ + lsm_early_for_each_raw(lsm) { +- init_debug(" early started: %s (%s)\n", lsm->name, ++ init_debug(" early started: %s (%s)\n", lsm->id->name, + is_enabled(lsm) ? "enabled" : "disabled"); + if (lsm->enabled) +- lsm_append(lsm->name, &lsm_names); ++ lsm_append(lsm->id->name, &lsm_names); + } + + /* Load LSMs in specified order. */ +diff --git a/security/safesetid/lsm.c b/security/safesetid/lsm.c +index 1ba564f097f5..9a7c68d4e642 100644 +--- a/security/safesetid/lsm.c ++++ b/security/safesetid/lsm.c +@@ -287,6 +287,6 @@ static int __init safesetid_security_init(void) + } + + DEFINE_LSM(safesetid_security_init) = { ++ .id = &safesetid_lsmid, + .init = safesetid_security_init, +- .name = "safesetid", + }; +diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c +index 1bc947e2b0e7..a2323c6daafe 100644 +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -7480,7 +7480,7 @@ void selinux_complete_init(void) + /* SELinux requires early initialization in order to label + all processes and objects when they are created. */ + DEFINE_LSM(selinux) = { +- .name = "selinux", ++ .id = &selinux_lsmid, + .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, + .enabled = &selinux_enabled_boot, + .blobs = &selinux_blob_sizes, +diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c +index 7c8b06e974bb..43546723f600 100644 +--- a/security/smack/smack_lsm.c ++++ b/security/smack/smack_lsm.c +@@ -5318,7 +5318,7 @@ static __init int smack_init(void) + * all processes and objects when they are created. + */ + DEFINE_LSM(smack) = { +- .name = "smack", ++ .id = &smack_lsmid, + .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, + .blobs = &smack_blob_sizes, + .init = smack_init, +diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c +index fa08d78162bb..3c6964ea7ace 100644 +--- a/security/tomoyo/tomoyo.c ++++ b/security/tomoyo/tomoyo.c +@@ -615,7 +615,7 @@ static int __init tomoyo_init(void) + } + + DEFINE_LSM(tomoyo) = { +- .name = "tomoyo", ++ .id = &tomoyo_lsmid, + .enabled = &tomoyo_enabled, + .flags = LSM_FLAG_LEGACY_MAJOR, + .blobs = &tomoyo_blob_sizes, +diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c +index 54bd5f535ac1..f546176dd2fc 100644 +--- a/security/yama/yama_lsm.c ++++ b/security/yama/yama_lsm.c +@@ -483,6 +483,6 @@ static int __init yama_init(void) + } + + DEFINE_LSM(yama) = { +- .name = "yama", ++ .id = &yama_lsmid, + .init = yama_init, + }; +-- +2.50.1 (Apple Git-155) + diff --git a/1309-lsm-rename-the-lsm-order-variables-for-consistency.patch b/1309-lsm-rename-the-lsm-order-variables-for-consistency.patch new file mode 100644 index 000000000..1488024d2 --- /dev/null +++ b/1309-lsm-rename-the-lsm-order-variables-for-consistency.patch @@ -0,0 +1,226 @@ +From f212d5ad65c47cf954187a1d3ed14db6127b3727 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:20:33 +0200 +Subject: [PATCH] lsm: rename the lsm order variables for consistency + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit 592b104f9b516b2c22cb23a2f4c34486fdb21bae +Author: Paul Moore +Date: Tue Feb 11 17:13:26 2025 -0500 + + lsm: rename the lsm order variables for consistency + + Rename the builtin_lsm_order variable to lsm_order_builtin, + chosen_lsm_order to lsm_order_cmdline, chosen_major_lsm to + lsm_order_legacy, ordered_lsms[] to lsm_order[], and exclusive + to lsm_exclusive. + + This patch also renames the associated kernel command line parsing + functions and adds some basic function comment blocks. The parsing + function choose_major_lsm() was renamed to lsm_choose_security(), + choose_lsm_order() to lsm_choose_lsm(), and enable_debug() to + lsm_debug_enable(). + + Reviewed-by: Kees Cook + Reviewed-by: John Johansen + Reviewed-by: Casey Schaufler + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm_init.c b/security/lsm_init.c +index e5faf6be44fb..171b2b047bdc 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -16,14 +16,14 @@ char *lsm_names; + extern struct lsm_info __start_lsm_info[], __end_lsm_info[]; + extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[]; + +-/* Boot-time LSM user choice */ +-static __initconst const char *const builtin_lsm_order = CONFIG_LSM; +-static __initdata const char *chosen_lsm_order; +-static __initdata const char *chosen_major_lsm; ++/* Build and boot-time LSM ordering. */ ++static __initconst const char *const lsm_order_builtin = CONFIG_LSM; ++static __initdata const char *lsm_order_cmdline; ++static __initdata const char *lsm_order_legacy; + + /* Ordered list of LSMs to initialize. */ +-static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1]; +-static __initdata struct lsm_info *exclusive; ++static __initdata struct lsm_info *lsm_order[MAX_LSM_COUNT + 1]; ++static __initdata struct lsm_info *lsm_exclusive; + + static __initdata bool debug; + #define init_debug(...) \ +@@ -33,7 +33,7 @@ static __initdata bool debug; + } while (0) + + #define lsm_order_for_each(iter) \ +- for ((iter) = ordered_lsms; *(iter); (iter)++) ++ for ((iter) = lsm_order; *(iter); (iter)++) + #define lsm_for_each_raw(iter) \ + for ((iter) = __start_lsm_info; \ + (iter) < __end_lsm_info; (iter)++) +@@ -41,31 +41,41 @@ static __initdata bool debug; + for ((iter) = __start_early_lsm_info; \ + (iter) < __end_early_lsm_info; (iter)++) + +-static int lsm_append(const char *new, char **result); +- +-/* Save user chosen LSM */ +-static int __init choose_major_lsm(char *str) ++/** ++ * lsm_choose_security - Legacy "major" LSM selection ++ * @str: kernel command line parameter ++ */ ++static int __init lsm_choose_security(char *str) + { +- chosen_major_lsm = str; ++ lsm_order_legacy = str; + return 1; + } +-__setup("security=", choose_major_lsm); ++__setup("security=", lsm_choose_security); + +-/* Explicitly choose LSM initialization order. */ +-static int __init choose_lsm_order(char *str) ++/** ++ * lsm_choose_lsm - Modern LSM selection ++ * @str: kernel command line parameter ++ */ ++static int __init lsm_choose_lsm(char *str) + { +- chosen_lsm_order = str; ++ lsm_order_cmdline = str; + return 1; + } +-__setup("lsm=", choose_lsm_order); ++__setup("lsm=", lsm_choose_lsm); + +-/* Enable LSM order debugging. */ +-static int __init enable_debug(char *str) ++/** ++ * lsm_debug_enable - Enable LSM framework debugging ++ * @str: kernel command line parameter ++ * ++ * Currently we only provide debug info during LSM initialization, but we may ++ * want to expand this in the future. ++ */ ++static int __init lsm_debug_enable(char *str) + { + debug = true; + return 1; + } +-__setup("lsm.debug", enable_debug); ++__setup("lsm.debug", lsm_debug_enable); + + /* Mark an LSM's enabled flag. */ + static int lsm_enabled_true __initdata = 1; +@@ -127,7 +137,7 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) + /* Enable this LSM, if it is not already set. */ + if (!lsm->enabled) + lsm->enabled = &lsm_enabled_true; +- ordered_lsms[last_lsm] = lsm; ++ lsm_order[last_lsm] = lsm; + lsm_idlist[last_lsm++] = lsm->id; + + init_debug("%s ordered: %s (%s)\n", from, lsm->id->name, +@@ -157,7 +167,7 @@ static void __init lsm_prepare(struct lsm_info *lsm) + if (!is_enabled(lsm)) { + set_enabled(lsm, false); + return; +- } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) { ++ } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && lsm_exclusive) { + init_debug("exclusive disabled: %s\n", lsm->id->name); + set_enabled(lsm, false); + return; +@@ -165,9 +175,9 @@ static void __init lsm_prepare(struct lsm_info *lsm) + + /* Mark the LSM as enabled. */ + set_enabled(lsm, true); +- if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) { ++ if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !lsm_exclusive) { + init_debug("exclusive chosen: %s\n", lsm->id->name); +- exclusive = lsm; ++ lsm_exclusive = lsm; + } + + /* Register the LSM blob sizes. */ +@@ -223,7 +233,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + } + + /* Process "security=", if given. */ +- if (chosen_major_lsm) { ++ if (lsm_order_legacy) { + struct lsm_info *major; + + /* +@@ -234,10 +244,10 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + */ + lsm_for_each_raw(major) { + if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && +- strcmp(major->id->name, chosen_major_lsm) != 0) { ++ strcmp(major->id->name, lsm_order_legacy) != 0) { + set_enabled(major, false); + init_debug("security=%s disabled: %s (only one legacy major LSM)\n", +- chosen_major_lsm, major->id->name); ++ lsm_order_legacy, major->id->name); + } + } + } +@@ -262,11 +272,11 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + } + + /* Process "security=", if given. */ +- if (chosen_major_lsm) { ++ if (lsm_order_legacy) { + lsm_for_each_raw(lsm) { + if (exists_ordered_lsm(lsm)) + continue; +- if (strcmp(lsm->id->name, chosen_major_lsm) == 0) ++ if (strcmp(lsm->id->name, lsm_order_legacy) == 0) + append_ordered_lsm(lsm, "security="); + } + } +@@ -298,15 +308,15 @@ static void __init lsm_init_ordered(void) + struct lsm_info **lsm; + struct lsm_info *early; + +- if (chosen_lsm_order) { +- if (chosen_major_lsm) { ++ if (lsm_order_cmdline) { ++ if (lsm_order_legacy) { + pr_warn("security=%s is ignored because it is superseded by lsm=%s\n", +- chosen_major_lsm, chosen_lsm_order); +- chosen_major_lsm = NULL; ++ lsm_order_legacy, lsm_order_cmdline); ++ lsm_order_legacy = NULL; + } +- ordered_lsm_parse(chosen_lsm_order, "cmdline"); ++ ordered_lsm_parse(lsm_order_cmdline, "cmdline"); + } else +- ordered_lsm_parse(builtin_lsm_order, "builtin"); ++ ordered_lsm_parse(lsm_order_builtin, "builtin"); + + lsm_order_for_each(lsm) { + lsm_prepare(*lsm); +@@ -472,9 +482,9 @@ int __init security_init(void) + { + struct lsm_info *lsm; + +- init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*"); +- init_debug(" CONFIG_LSM=%s\n", builtin_lsm_order); +- init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*"); ++ init_debug("legacy security=%s\n", lsm_order_legacy ? : " *unspecified*"); ++ init_debug(" CONFIG_LSM=%s\n", lsm_order_builtin); ++ init_debug("boot arg lsm=%s\n", lsm_order_cmdline ? : " *unspecified*"); + + /* + * Append the names of the early LSM modules now that kmalloc() is +-- +2.50.1 (Apple Git-155) + diff --git a/1310-lsm-rework-lsm-active-cnt-and-lsm-idlist.patch b/1310-lsm-rework-lsm-active-cnt-and-lsm-idlist.patch new file mode 100644 index 000000000..d16321382 --- /dev/null +++ b/1310-lsm-rework-lsm-active-cnt-and-lsm-idlist.patch @@ -0,0 +1,102 @@ +From c4c061449d1ae617402680b00ab006b1e0ee2c2c Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:20:34 +0200 +Subject: [PATCH] lsm: rework lsm_active_cnt and lsm_idlist[] + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit 250898ca335f337bc032a9693dc0a30a1cb85825 +Author: Paul Moore +Date: Wed Feb 12 15:36:51 2025 -0500 + + lsm: rework lsm_active_cnt and lsm_idlist[] + + Move the LSM active count and lsm_id list declarations out of a header + that is visible across the kernel and into a header that is limited to + the LSM framework. This not only helps keep the include/linux headers + smaller and cleaner, it helps prevent misuse of these variables. + + Reviewed-by: Casey Schaufler + Reviewed-by: John Johansen + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/include/linux/security.h b/include/linux/security.h +index 3254f74457f7..2db88ddbe9a9 100644 +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -167,8 +167,6 @@ struct lsm_prop { + }; + + extern const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1]; +-extern u32 lsm_active_cnt; +-extern const struct lsm_id *lsm_idlist[]; + + /* These functions are in security/commoncap.c */ + extern int cap_capable(const struct cred *cred, struct user_namespace *ns, +diff --git a/security/lsm.h b/security/lsm.h +index 0e1731bad4a7..dbe755c45e57 100644 +--- a/security/lsm.h ++++ b/security/lsm.h +@@ -7,6 +7,11 @@ + #define _LSM_H_ + + #include ++#include ++ ++/* List of configured LSMs */ ++extern unsigned int lsm_active_cnt; ++extern const struct lsm_id *lsm_idlist[]; + + /* LSM blob configuration */ + extern struct lsm_blob_sizes blob_sizes; +diff --git a/security/lsm_init.c b/security/lsm_init.c +index 171b2b047bdc..e7afc74caa2b 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -214,12 +214,6 @@ static void __init initialize_lsm(struct lsm_info *lsm) + } + } + +-/* +- * Current index to use while initializing the lsm id list. +- */ +-u32 lsm_active_cnt __ro_after_init; +-const struct lsm_id *lsm_idlist[MAX_LSM_COUNT]; +- + /* Populate ordered LSMs list from comma-separated LSM name list. */ + static void __init ordered_lsm_parse(const char *order, const char *origin) + { +diff --git a/security/lsm_syscalls.c b/security/lsm_syscalls.c +index 8440948a690c..5648b1f0ce9c 100644 +--- a/security/lsm_syscalls.c ++++ b/security/lsm_syscalls.c +@@ -17,6 +17,8 @@ + #include + #include + ++#include "lsm.h" ++ + /** + * lsm_name_to_attr - map an LSM attribute name to its ID + * @name: name of the attribute +diff --git a/security/security.c b/security/security.c +index 6040891f4588..6f20f44c8d13 100644 +--- a/security/security.c ++++ b/security/security.c +@@ -74,6 +74,9 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = { + [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality", + }; + ++unsigned int lsm_active_cnt __ro_after_init; ++const struct lsm_id *lsm_idlist[MAX_LSM_COUNT]; ++ + struct lsm_blob_sizes blob_sizes; + + struct kmem_cache *lsm_file_cache; +-- +2.50.1 (Apple Git-155) + diff --git a/1311-lsm-get-rid-of-the-lsm-names-list-and-do-some-cleanup.patch b/1311-lsm-get-rid-of-the-lsm-names-list-and-do-some-cleanup.patch new file mode 100644 index 000000000..e658520c6 --- /dev/null +++ b/1311-lsm-get-rid-of-the-lsm-names-list-and-do-some-cleanup.patch @@ -0,0 +1,184 @@ +From e84cf2768df37393453e8bb645555e360761056c Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:20:34 +0200 +Subject: [PATCH] lsm: get rid of the lsm_names list and do some cleanup + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit 935d508d4d7ab9d19c603bd7eb2937249551d507 +Author: Paul Moore +Date: Thu Feb 13 17:34:12 2025 -0500 + + lsm: get rid of the lsm_names list and do some cleanup + + The LSM currently has a lot of code to maintain a list of the currently + active LSMs in a human readable string, with the only user being the + "/sys/kernel/security/lsm" code. Let's drop all of that code and + generate the string on first use and then cache it for subsequent use. + + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h +index 4cd17c9a229f..bc477fb20d02 100644 +--- a/include/linux/lsm_hooks.h ++++ b/include/linux/lsm_hooks.h +@@ -169,7 +169,6 @@ struct lsm_info { + + + /* DO NOT tamper with these variables outside of the LSM framework */ +-extern char *lsm_names; + extern struct lsm_static_calls_table static_calls_table __ro_after_init; + + /** +diff --git a/security/inode.c b/security/inode.c +index da3ab44c8e57..6f3caab37ab2 100644 +--- a/security/inode.c ++++ b/security/inode.c +@@ -22,6 +22,8 @@ + #include + #include + ++#include "lsm.h" ++ + static struct vfsmount *mount; + static int mount_count; + +@@ -339,12 +341,49 @@ void securityfs_recursive_remove(struct dentry *dentry) + EXPORT_SYMBOL_GPL(securityfs_recursive_remove); + + #ifdef CONFIG_SECURITY ++#include ++ + static struct dentry *lsm_dentry; ++ + static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count, + loff_t *ppos) + { +- return simple_read_from_buffer(buf, count, ppos, lsm_names, +- strlen(lsm_names)); ++ int i; ++ static char *str; ++ static size_t len; ++ static DEFINE_SPINLOCK(lock); ++ ++ /* NOTE: we never free or modify the string once it is set */ ++ ++ if (unlikely(!str || !len)) { ++ char *str_tmp; ++ size_t len_tmp = 0; ++ ++ for (i = 0; i < lsm_active_cnt; i++) ++ /* the '+ 1' accounts for either a comma or a NUL */ ++ len_tmp += strlen(lsm_idlist[i]->name) + 1; ++ ++ str_tmp = kmalloc(len_tmp, GFP_KERNEL); ++ if (!str_tmp) ++ return -ENOMEM; ++ str_tmp[0] = '\0'; ++ ++ for (i = 0; i < lsm_active_cnt; i++) { ++ if (i > 0) ++ strcat(str_tmp, ","); ++ strcat(str_tmp, lsm_idlist[i]->name); ++ } ++ ++ spin_lock(&lock); ++ if (!str) { ++ str = str_tmp; ++ len = len_tmp - 1; ++ } else ++ kfree(str_tmp); ++ spin_unlock(&lock); ++ } ++ ++ return simple_read_from_buffer(buf, count, ppos, str, len); + } + + static const struct file_operations lsm_ops = { +diff --git a/security/lsm_init.c b/security/lsm_init.c +index e7afc74caa2b..7c244a309a35 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -10,8 +10,6 @@ + + #include "lsm.h" + +-char *lsm_names; +- + /* Pointers to LSM sections defined in include/asm-generic/vmlinux.lds.h */ + extern struct lsm_info __start_lsm_info[], __end_lsm_info[]; + extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[]; +@@ -365,42 +363,6 @@ static void __init lsm_init_ordered(void) + } + } + +-static bool match_last_lsm(const char *list, const char *lsm) +-{ +- const char *last; +- +- if (WARN_ON(!list || !lsm)) +- return false; +- last = strrchr(list, ','); +- if (last) +- /* Pass the comma, strcmp() will check for '\0' */ +- last++; +- else +- last = list; +- return !strcmp(last, lsm); +-} +- +-static int lsm_append(const char *new, char **result) +-{ +- char *cp; +- +- if (*result == NULL) { +- *result = kstrdup(new, GFP_KERNEL); +- if (*result == NULL) +- return -ENOMEM; +- } else { +- /* Check if it is the last registered name */ +- if (match_last_lsm(*result, new)) +- return 0; +- cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new); +- if (cp == NULL) +- return -ENOMEM; +- kfree(*result); +- *result = cp; +- } +- return 0; +-} +- + static void __init lsm_static_call_init(struct security_hook_list *hl) + { + struct lsm_static_call *scall = hl->scalls; +@@ -437,15 +399,6 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count, + hooks[i].lsmid = lsmid; + lsm_static_call_init(&hooks[i]); + } +- +- /* +- * Don't try to append during early_security_init(), we'll come back +- * and fix this up afterwards. +- */ +- if (slab_is_available()) { +- if (lsm_append(lsmid->name, &lsm_names) < 0) +- panic("%s - Cannot get early memory.\n", __func__); +- } + } + + int __init early_security_init(void) +@@ -487,8 +440,6 @@ int __init security_init(void) + lsm_early_for_each_raw(lsm) { + init_debug(" early started: %s (%s)\n", lsm->id->name, + is_enabled(lsm) ? "enabled" : "disabled"); +- if (lsm->enabled) +- lsm_append(lsm->id->name, &lsm_names); + } + + /* Load LSMs in specified order. */ +-- +2.50.1 (Apple Git-155) + diff --git a/1312-lsm-rework-the-lsm-enable-disable-setter-getter-functions.patch b/1312-lsm-rework-the-lsm-enable-disable-setter-getter-functions.patch new file mode 100644 index 000000000..6ecb5432e --- /dev/null +++ b/1312-lsm-rework-the-lsm-enable-disable-setter-getter-functions.patch @@ -0,0 +1,179 @@ +From d52cc6c688625151f1b39aa7b2b5c532b47a2211 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:26:49 +0200 +Subject: [PATCH] lsm: rework the LSM enable/disable setter/getter functions + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit 2d67172612fd9df2c4d08533515ef483cb526dd9 +Author: Paul Moore +Date: Thu Apr 10 22:04:26 2025 -0400 + + lsm: rework the LSM enable/disable setter/getter functions + + In addition to style changes, rename set_enabled() to lsm_enabled_set() + and is_enabled() to lsm_is_enabled() to better fit within the LSM + initialization code. + + Reviewed-by: Casey Schaufler + Reviewed-by: John Johansen + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm_init.c b/security/lsm_init.c +index 7c244a309a35..9929d1a61634 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -10,6 +10,10 @@ + + #include "lsm.h" + ++/* LSM enabled constants. */ ++static __initdata int lsm_enabled_true = 1; ++static __initdata int lsm_enabled_false = 0; ++ + /* Pointers to LSM sections defined in include/asm-generic/vmlinux.lds.h */ + extern struct lsm_info __start_lsm_info[], __end_lsm_info[]; + extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[]; +@@ -75,37 +79,33 @@ static int __init lsm_debug_enable(char *str) + } + __setup("lsm.debug", lsm_debug_enable); + +-/* Mark an LSM's enabled flag. */ +-static int lsm_enabled_true __initdata = 1; +-static int lsm_enabled_false __initdata = 0; +-static void __init set_enabled(struct lsm_info *lsm, bool enabled) ++/** ++ * lsm_enabled_set - Mark a LSM as enabled ++ * @lsm: LSM definition ++ * @enabled: enabled flag ++ */ ++static void __init lsm_enabled_set(struct lsm_info *lsm, bool enabled) + { + /* + * When an LSM hasn't configured an enable variable, we can use + * a hard-coded location for storing the default enabled state. + */ +- if (!lsm->enabled) { +- if (enabled) +- lsm->enabled = &lsm_enabled_true; +- else +- lsm->enabled = &lsm_enabled_false; +- } else if (lsm->enabled == &lsm_enabled_true) { +- if (!enabled) +- lsm->enabled = &lsm_enabled_false; +- } else if (lsm->enabled == &lsm_enabled_false) { +- if (enabled) +- lsm->enabled = &lsm_enabled_true; ++ if (!lsm->enabled || ++ lsm->enabled == &lsm_enabled_true || ++ lsm->enabled == &lsm_enabled_false) { ++ lsm->enabled = enabled ? &lsm_enabled_true : &lsm_enabled_false; + } else { + *lsm->enabled = enabled; + } + } + +-static inline bool is_enabled(struct lsm_info *lsm) ++/** ++ * lsm_is_enabled - Determine if a LSM is enabled ++ * @lsm: LSM definition ++ */ ++static inline bool lsm_is_enabled(struct lsm_info *lsm) + { +- if (!lsm->enabled) +- return false; +- +- return *lsm->enabled; ++ return (lsm->enabled ? *lsm->enabled : false); + } + + /* Is an LSM already listed in the ordered LSMs list? */ +@@ -139,7 +139,7 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) + lsm_idlist[last_lsm++] = lsm->id; + + init_debug("%s ordered: %s (%s)\n", from, lsm->id->name, +- is_enabled(lsm) ? "enabled" : "disabled"); ++ lsm_is_enabled(lsm) ? "enabled" : "disabled"); + } + + static void __init lsm_set_blob_size(int *need, int *lbs) +@@ -162,17 +162,17 @@ static void __init lsm_prepare(struct lsm_info *lsm) + { + struct lsm_blob_sizes *blobs; + +- if (!is_enabled(lsm)) { +- set_enabled(lsm, false); ++ if (!lsm_is_enabled(lsm)) { ++ lsm_enabled_set(lsm, false); + return; + } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && lsm_exclusive) { + init_debug("exclusive disabled: %s\n", lsm->id->name); +- set_enabled(lsm, false); ++ lsm_enabled_set(lsm, false); + return; + } + + /* Mark the LSM as enabled. */ +- set_enabled(lsm, true); ++ lsm_enabled_set(lsm, true); + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !lsm_exclusive) { + init_debug("exclusive chosen: %s\n", lsm->id->name); + lsm_exclusive = lsm; +@@ -203,7 +203,7 @@ static void __init lsm_prepare(struct lsm_info *lsm) + /* Initialize a given LSM, if it is enabled. */ + static void __init initialize_lsm(struct lsm_info *lsm) + { +- if (is_enabled(lsm)) { ++ if (lsm_is_enabled(lsm)) { + int ret; + + init_debug("initializing %s\n", lsm->id->name); +@@ -237,7 +237,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + lsm_for_each_raw(major) { + if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && + strcmp(major->id->name, lsm_order_legacy) != 0) { +- set_enabled(major, false); ++ lsm_enabled_set(major, false); + init_debug("security=%s disabled: %s (only one legacy major LSM)\n", + lsm_order_legacy, major->id->name); + } +@@ -283,7 +283,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + lsm_for_each_raw(lsm) { + if (exists_ordered_lsm(lsm)) + continue; +- set_enabled(lsm, false); ++ lsm_enabled_set(lsm, false); + init_debug("%s skipped: %s (not in requested order)\n", + origin, lsm->id->name); + } +@@ -316,12 +316,12 @@ static void __init lsm_init_ordered(void) + + pr_info("initializing lsm="); + lsm_early_for_each_raw(early) { +- if (is_enabled(early)) ++ if (lsm_is_enabled(early)) + pr_cont("%s%s", + first++ == 0 ? "" : ",", early->id->name); + } + lsm_order_for_each(lsm) { +- if (is_enabled(*lsm)) ++ if (lsm_is_enabled(*lsm)) + pr_cont("%s%s", + first++ == 0 ? "" : ",", (*lsm)->id->name); + } +@@ -439,7 +439,7 @@ int __init security_init(void) + */ + lsm_early_for_each_raw(lsm) { + init_debug(" early started: %s (%s)\n", lsm->id->name, +- is_enabled(lsm) ? "enabled" : "disabled"); ++ lsm_is_enabled(lsm) ? "enabled" : "disabled"); + } + + /* Load LSMs in specified order. */ +-- +2.50.1 (Apple Git-155) + diff --git a/1313-lsm-rename-exists-ordered-lsm-to-lsm-order-exists.patch b/1313-lsm-rename-exists-ordered-lsm-to-lsm-order-exists.patch new file mode 100644 index 000000000..fefdf29e2 --- /dev/null +++ b/1313-lsm-rename-exists-ordered-lsm-to-lsm-order-exists.patch @@ -0,0 +1,71 @@ +From f5d2029482544f20f0ef5652593a120262b8a96e Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:26:50 +0200 +Subject: [PATCH] lsm: rename exists_ordered_lsm() to lsm_order_exists() + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit a748372a282ae1e23d5d4b14a3e190c28764cfd2 +Author: Paul Moore +Date: Sun Jul 13 17:37:56 2025 -0400 + + lsm: rename exists_ordered_lsm() to lsm_order_exists() + + Also add a header comment block to the function. + + Reviewed-by: Casey Schaufler + Reviewed-by: John Johansen + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm_init.c b/security/lsm_init.c +index 9929d1a61634..bee12c553820 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -108,8 +108,11 @@ static inline bool lsm_is_enabled(struct lsm_info *lsm) + return (lsm->enabled ? *lsm->enabled : false); + } + +-/* Is an LSM already listed in the ordered LSMs list? */ +-static bool __init exists_ordered_lsm(struct lsm_info *lsm) ++/** ++ * lsm_order_exists - Determine if a LSM exists in the ordered list ++ * @lsm: LSM definition ++ */ ++static bool __init lsm_order_exists(struct lsm_info *lsm) + { + struct lsm_info **check; + +@@ -126,7 +129,7 @@ static int last_lsm __initdata; + static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) + { + /* Ignore duplicate selections. */ +- if (exists_ordered_lsm(lsm)) ++ if (lsm_order_exists(lsm)) + return; + + if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from)) +@@ -266,7 +269,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + /* Process "security=", if given. */ + if (lsm_order_legacy) { + lsm_for_each_raw(lsm) { +- if (exists_ordered_lsm(lsm)) ++ if (lsm_order_exists(lsm)) + continue; + if (strcmp(lsm->id->name, lsm_order_legacy) == 0) + append_ordered_lsm(lsm, "security="); +@@ -281,7 +284,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + + /* Disable all LSMs not in the ordered list. */ + lsm_for_each_raw(lsm) { +- if (exists_ordered_lsm(lsm)) ++ if (lsm_order_exists(lsm)) + continue; + lsm_enabled_set(lsm, false); + init_debug("%s skipped: %s (not in requested order)\n", +-- +2.50.1 (Apple Git-155) + diff --git a/1314-lsm-rename-rework-append-ordered-lsm-into-lsm-order-append.patch b/1314-lsm-rename-rework-append-ordered-lsm-into-lsm-order-append.patch new file mode 100644 index 000000000..4e2e87084 --- /dev/null +++ b/1314-lsm-rename-rework-append-ordered-lsm-into-lsm-order-append.patch @@ -0,0 +1,170 @@ +From 2fad39b00bffc92249f43dfa2db89c89b640bcc2 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:26:50 +0200 +Subject: [PATCH] lsm: rename/rework append_ordered_lsm() into + lsm_order_append() + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit 24a9c58978ee368cbd796a03cb6e8ade6e0b6f5f +Author: Paul Moore +Date: Wed Jul 16 15:04:10 2025 -0400 + + lsm: rename/rework append_ordered_lsm() into lsm_order_append() + + Rename append_ordered_lsm() to lsm_order_append() to better match + convention and do some rework. The rework includes moving the + LSM_FLAG_EXCLUSIVE logic from lsm_prepare() to lsm_order_append() + in order to consolidate the individual LSM append/activation code, + and adding logic to skip appending explicitly disabled LSMs to the + active LSM list. + + Reviewed-by: Casey Schaufler + Reviewed-by: John Johansen + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm_init.c b/security/lsm_init.c +index bee12c553820..30ed62311565 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -124,24 +124,48 @@ static bool __init lsm_order_exists(struct lsm_info *lsm) + return false; + } + +-/* Append an LSM to the list of ordered LSMs to initialize. */ +-static int last_lsm __initdata; +-static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) ++/** ++ * lsm_order_append - Append a LSM to the ordered list ++ * @lsm: LSM definition ++ * @src: source of the addition ++ * ++ * Append @lsm to the enabled LSM array after ensuring that it hasn't been ++ * explicitly disabled, is a duplicate entry, or would run afoul of the ++ * LSM_FLAG_EXCLUSIVE logic. ++ */ ++static void __init lsm_order_append(struct lsm_info *lsm, const char *src) + { + /* Ignore duplicate selections. */ + if (lsm_order_exists(lsm)) + return; + +- if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from)) +- return; ++ /* Skip explicitly disabled LSMs. */ ++ if (lsm->enabled && !lsm_is_enabled(lsm)) ++ goto out; + +- /* Enable this LSM, if it is not already set. */ +- if (!lsm->enabled) +- lsm->enabled = &lsm_enabled_true; +- lsm_order[last_lsm] = lsm; +- lsm_idlist[last_lsm++] = lsm->id; ++ if (WARN(lsm_active_cnt == MAX_LSM_COUNT, ++ "%s: out of LSM static calls!?\n", src)) { ++ lsm_enabled_set(lsm, false); ++ goto out; ++ } ++ ++ if (lsm->flags & LSM_FLAG_EXCLUSIVE) { ++ if (lsm_exclusive) { ++ init_debug("exclusive disabled: %s\n", lsm->id->name); ++ lsm_enabled_set(lsm, false); ++ goto out; ++ } else { ++ init_debug("exclusive chosen: %s\n", lsm->id->name); ++ lsm_exclusive = lsm; ++ } ++ } + +- init_debug("%s ordered: %s (%s)\n", from, lsm->id->name, ++ lsm_enabled_set(lsm, true); ++ lsm_order[lsm_active_cnt] = lsm; ++ lsm_idlist[lsm_active_cnt++] = lsm->id; ++ ++out: ++ init_debug("%s ordered: %s (%s)\n", src, lsm->id->name, + lsm_is_enabled(lsm) ? "enabled" : "disabled"); + } + +@@ -163,26 +187,12 @@ static void __init lsm_set_blob_size(int *need, int *lbs) + */ + static void __init lsm_prepare(struct lsm_info *lsm) + { +- struct lsm_blob_sizes *blobs; ++ struct lsm_blob_sizes *blobs = lsm->blobs; + +- if (!lsm_is_enabled(lsm)) { +- lsm_enabled_set(lsm, false); +- return; +- } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && lsm_exclusive) { +- init_debug("exclusive disabled: %s\n", lsm->id->name); +- lsm_enabled_set(lsm, false); ++ if (!blobs) + return; +- } +- +- /* Mark the LSM as enabled. */ +- lsm_enabled_set(lsm, true); +- if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !lsm_exclusive) { +- init_debug("exclusive chosen: %s\n", lsm->id->name); +- lsm_exclusive = lsm; +- } + + /* Register the LSM blob sizes. */ +- blobs = lsm->blobs; + lsm_set_blob_size(&blobs->lbs_cred, &blob_sizes.lbs_cred); + lsm_set_blob_size(&blobs->lbs_file, &blob_sizes.lbs_file); + lsm_set_blob_size(&blobs->lbs_ib, &blob_sizes.lbs_ib); +@@ -224,7 +234,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + /* LSM_ORDER_FIRST is always first. */ + lsm_for_each_raw(lsm) { + if (lsm->order == LSM_ORDER_FIRST) +- append_ordered_lsm(lsm, " first"); ++ lsm_order_append(lsm, " first"); + } + + /* Process "security=", if given. */ +@@ -256,7 +266,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + lsm_for_each_raw(lsm) { + if (strcmp(lsm->id->name, name) == 0) { + if (lsm->order == LSM_ORDER_MUTABLE) +- append_ordered_lsm(lsm, origin); ++ lsm_order_append(lsm, origin); + found = true; + } + } +@@ -272,14 +282,14 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) + if (lsm_order_exists(lsm)) + continue; + if (strcmp(lsm->id->name, lsm_order_legacy) == 0) +- append_ordered_lsm(lsm, "security="); ++ lsm_order_append(lsm, "security="); + } + } + + /* LSM_ORDER_LAST is always last. */ + lsm_for_each_raw(lsm) { + if (lsm->order == LSM_ORDER_LAST) +- append_ordered_lsm(lsm, " last"); ++ lsm_order_append(lsm, " last"); + } + + /* Disable all LSMs not in the ordered list. */ +@@ -413,8 +423,8 @@ int __init early_security_init(void) + return 0; + + lsm_early_for_each_raw(lsm) { +- if (!lsm->enabled) +- lsm->enabled = &lsm_enabled_true; ++ lsm_enabled_set(lsm, true); ++ lsm_order_append(lsm, "early"); + lsm_prepare(lsm); + initialize_lsm(lsm); + } +-- +2.50.1 (Apple Git-155) + diff --git a/1315-lsm-rename-rework-ordered-lsm-parse-to-lsm-order-parse.patch b/1315-lsm-rename-rework-ordered-lsm-parse-to-lsm-order-parse.patch new file mode 100644 index 000000000..83b467c4e --- /dev/null +++ b/1315-lsm-rename-rework-ordered-lsm-parse-to-lsm-order-parse.patch @@ -0,0 +1,166 @@ +From 9a0e65bba0a4f8b08943349600337bc08612db41 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:26:51 +0200 +Subject: [PATCH] lsm: rename/rework ordered_lsm_parse() to lsm_order_parse() + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit 752db06571816a3870b17814882425318b5ec0ef +Author: Paul Moore +Date: Sun Jul 13 17:51:12 2025 -0400 + + lsm: rename/rework ordered_lsm_parse() to lsm_order_parse() + + Rename ordered_lsm_parse() to lsm_order_parse() for the sake of + consistency with the other LSM initialization routines, and also + do some minor rework of the function. Aside from some minor style + decisions, the majority of the rework involved shuffling the order + of the LSM_FLAG_LEGACY and LSM_ORDER_FIRST code so that the + LSM_FLAG_LEGACY checks are handled first; it is important to note + that this doesn't affect the order in which the LSMs are registered. + + Reviewed-by: Casey Schaufler + Reviewed-by: John Johansen + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm_init.c b/security/lsm_init.c +index 30ed62311565..d159981ad0a2 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -225,83 +225,75 @@ static void __init initialize_lsm(struct lsm_info *lsm) + } + } + +-/* Populate ordered LSMs list from comma-separated LSM name list. */ +-static void __init ordered_lsm_parse(const char *order, const char *origin) ++/** ++ * lsm_order_parse - Parse the comma delimited LSM list ++ * @list: LSM list ++ * @src: source of the list ++ */ ++static void __init lsm_order_parse(const char *list, const char *src) + { + struct lsm_info *lsm; + char *sep, *name, *next; + +- /* LSM_ORDER_FIRST is always first. */ +- lsm_for_each_raw(lsm) { +- if (lsm->order == LSM_ORDER_FIRST) +- lsm_order_append(lsm, " first"); +- } +- +- /* Process "security=", if given. */ ++ /* Handle any Legacy LSM exclusions if one was specified. */ + if (lsm_order_legacy) { +- struct lsm_info *major; +- + /* +- * To match the original "security=" behavior, this +- * explicitly does NOT fallback to another Legacy Major +- * if the selected one was separately disabled: disable +- * all non-matching Legacy Major LSMs. ++ * To match the original "security=" behavior, this explicitly ++ * does NOT fallback to another Legacy Major if the selected ++ * one was separately disabled: disable all non-matching ++ * Legacy Major LSMs. + */ +- lsm_for_each_raw(major) { +- if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && +- strcmp(major->id->name, lsm_order_legacy) != 0) { +- lsm_enabled_set(major, false); ++ lsm_for_each_raw(lsm) { ++ if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) && ++ strcmp(lsm->id->name, lsm_order_legacy)) { ++ lsm_enabled_set(lsm, false); + init_debug("security=%s disabled: %s (only one legacy major LSM)\n", +- lsm_order_legacy, major->id->name); ++ lsm_order_legacy, lsm->id->name); + } + } + } + +- sep = kstrdup(order, GFP_KERNEL); ++ /* LSM_ORDER_FIRST */ ++ lsm_for_each_raw(lsm) { ++ if (lsm->order == LSM_ORDER_FIRST) ++ lsm_order_append(lsm, "first"); ++ } ++ ++ /* Normal or "mutable" LSMs */ ++ sep = kstrdup(list, GFP_KERNEL); + next = sep; + /* Walk the list, looking for matching LSMs. */ + while ((name = strsep(&next, ",")) != NULL) { +- bool found = false; +- + lsm_for_each_raw(lsm) { +- if (strcmp(lsm->id->name, name) == 0) { +- if (lsm->order == LSM_ORDER_MUTABLE) +- lsm_order_append(lsm, origin); +- found = true; +- } ++ if (!strcmp(lsm->id->name, name) && ++ lsm->order == LSM_ORDER_MUTABLE) ++ lsm_order_append(lsm, src); + } +- +- if (!found) +- init_debug("%s ignored: %s (not built into kernel)\n", +- origin, name); + } ++ kfree(sep); + +- /* Process "security=", if given. */ ++ /* Legacy LSM if specified. */ + if (lsm_order_legacy) { + lsm_for_each_raw(lsm) { +- if (lsm_order_exists(lsm)) +- continue; +- if (strcmp(lsm->id->name, lsm_order_legacy) == 0) +- lsm_order_append(lsm, "security="); ++ if (!strcmp(lsm->id->name, lsm_order_legacy)) ++ lsm_order_append(lsm, src); + } + } + +- /* LSM_ORDER_LAST is always last. */ ++ /* LSM_ORDER_LAST */ + lsm_for_each_raw(lsm) { + if (lsm->order == LSM_ORDER_LAST) +- lsm_order_append(lsm, " last"); ++ lsm_order_append(lsm, "last"); + } + +- /* Disable all LSMs not in the ordered list. */ ++ /* Disable all LSMs not previously enabled. */ + lsm_for_each_raw(lsm) { + if (lsm_order_exists(lsm)) + continue; + lsm_enabled_set(lsm, false); + init_debug("%s skipped: %s (not in requested order)\n", +- origin, lsm->id->name); ++ src, lsm->id->name); + } +- +- kfree(sep); + } + + /** +@@ -319,9 +311,9 @@ static void __init lsm_init_ordered(void) + lsm_order_legacy, lsm_order_cmdline); + lsm_order_legacy = NULL; + } +- ordered_lsm_parse(lsm_order_cmdline, "cmdline"); ++ lsm_order_parse(lsm_order_cmdline, "cmdline"); + } else +- ordered_lsm_parse(lsm_order_builtin, "builtin"); ++ lsm_order_parse(lsm_order_builtin, "builtin"); + + lsm_order_for_each(lsm) { + lsm_prepare(*lsm); +-- +2.50.1 (Apple Git-155) + diff --git a/1316-lsm-cleanup-the-lsm-blob-size-code.patch b/1316-lsm-cleanup-the-lsm-blob-size-code.patch new file mode 100644 index 000000000..ca971a2b9 --- /dev/null +++ b/1316-lsm-cleanup-the-lsm-blob-size-code.patch @@ -0,0 +1,148 @@ +From 7cbba38d94b554ea452c90264ea4f7b03ef18498 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Fri, 29 May 2026 18:37:27 +0200 +Subject: [PATCH] lsm: cleanup the LSM blob size code + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 +Conflicts: + - conflict due to 5816bf4273ed ("lsm,selinux: Add LSM blob support for + BPF objects"), which is not backported + +commit 291271e691740003021cf5b48fa7cf7e3371eaa7 +Author: Paul Moore +Date: Tue Feb 11 17:49:11 2025 -0500 + + lsm: cleanup the LSM blob size code + + Convert the lsm_blob_size fields to unsigned integers as there is no + current need for them to be negative, change "lsm_set_blob_size()" to + "lsm_blob_size_update()" to better reflect reality, and perform some + other minor cleanups to the associated code. + + Reviewed-by: Kees Cook + Reviewed-by: John Johansen + Reviewed-by: Casey Schaufler + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h +index bc477fb20d02..a7ecb0791a0f 100644 +--- a/include/linux/lsm_hooks.h ++++ b/include/linux/lsm_hooks.h +@@ -102,20 +102,20 @@ struct security_hook_list { + * Security blob size or offset data. + */ + struct lsm_blob_sizes { +- int lbs_cred; +- int lbs_file; +- int lbs_ib; +- int lbs_inode; +- int lbs_sock; +- int lbs_superblock; +- int lbs_ipc; +- int lbs_key; +- int lbs_msg_msg; +- int lbs_perf_event; +- int lbs_task; +- int lbs_xattr_count; /* number of xattr slots in new_xattrs array */ +- int lbs_tun_dev; +- int lbs_bdev; ++ unsigned int lbs_cred; ++ unsigned int lbs_file; ++ unsigned int lbs_ib; ++ unsigned int lbs_inode; ++ unsigned int lbs_sock; ++ unsigned int lbs_superblock; ++ unsigned int lbs_ipc; ++ unsigned int lbs_key; ++ unsigned int lbs_msg_msg; ++ unsigned int lbs_perf_event; ++ unsigned int lbs_task; ++ unsigned int lbs_xattr_count; /* num xattr slots in new_xattrs array */ ++ unsigned int lbs_tun_dev; ++ unsigned int lbs_bdev; + }; + + /* +diff --git a/security/lsm_init.c b/security/lsm_init.c +index d159981ad0a2..bc9191b53305 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -169,16 +169,22 @@ static void __init lsm_order_append(struct lsm_info *lsm, const char *src) + lsm_is_enabled(lsm) ? "enabled" : "disabled"); + } + +-static void __init lsm_set_blob_size(int *need, int *lbs) ++/** ++ * lsm_blob_size_update - Update the LSM blob size and offset information ++ * @sz_req: the requested additional blob size ++ * @sz_cur: the existing blob size ++ */ ++static void __init lsm_blob_size_update(unsigned int *sz_req, ++ unsigned int *sz_cur) + { +- int offset; ++ unsigned int offset; + +- if (*need <= 0) ++ if (*sz_req == 0) + return; + +- offset = ALIGN(*lbs, sizeof(void *)); +- *lbs = offset + *need; +- *need = offset; ++ offset = ALIGN(*sz_cur, sizeof(void *)); ++ *sz_cur = offset + *sz_req; ++ *sz_req = offset; + } + + /** +@@ -193,24 +199,27 @@ static void __init lsm_prepare(struct lsm_info *lsm) + return; + + /* Register the LSM blob sizes. */ +- lsm_set_blob_size(&blobs->lbs_cred, &blob_sizes.lbs_cred); +- lsm_set_blob_size(&blobs->lbs_file, &blob_sizes.lbs_file); +- lsm_set_blob_size(&blobs->lbs_ib, &blob_sizes.lbs_ib); ++ blobs = lsm->blobs; ++ lsm_blob_size_update(&blobs->lbs_cred, &blob_sizes.lbs_cred); ++ lsm_blob_size_update(&blobs->lbs_file, &blob_sizes.lbs_file); ++ lsm_blob_size_update(&blobs->lbs_ib, &blob_sizes.lbs_ib); + /* inode blob gets an rcu_head in addition to LSM blobs. */ + if (blobs->lbs_inode && blob_sizes.lbs_inode == 0) + blob_sizes.lbs_inode = sizeof(struct rcu_head); +- lsm_set_blob_size(&blobs->lbs_inode, &blob_sizes.lbs_inode); +- lsm_set_blob_size(&blobs->lbs_ipc, &blob_sizes.lbs_ipc); +- lsm_set_blob_size(&blobs->lbs_key, &blob_sizes.lbs_key); +- lsm_set_blob_size(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg); +- lsm_set_blob_size(&blobs->lbs_perf_event, &blob_sizes.lbs_perf_event); +- lsm_set_blob_size(&blobs->lbs_sock, &blob_sizes.lbs_sock); +- lsm_set_blob_size(&blobs->lbs_superblock, &blob_sizes.lbs_superblock); +- lsm_set_blob_size(&blobs->lbs_task, &blob_sizes.lbs_task); +- lsm_set_blob_size(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev); +- lsm_set_blob_size(&blobs->lbs_xattr_count, +- &blob_sizes.lbs_xattr_count); +- lsm_set_blob_size(&blobs->lbs_bdev, &blob_sizes.lbs_bdev); ++ lsm_blob_size_update(&blobs->lbs_inode, &blob_sizes.lbs_inode); ++ lsm_blob_size_update(&blobs->lbs_ipc, &blob_sizes.lbs_ipc); ++ lsm_blob_size_update(&blobs->lbs_key, &blob_sizes.lbs_key); ++ lsm_blob_size_update(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg); ++ lsm_blob_size_update(&blobs->lbs_perf_event, ++ &blob_sizes.lbs_perf_event); ++ lsm_blob_size_update(&blobs->lbs_sock, &blob_sizes.lbs_sock); ++ lsm_blob_size_update(&blobs->lbs_superblock, ++ &blob_sizes.lbs_superblock); ++ lsm_blob_size_update(&blobs->lbs_task, &blob_sizes.lbs_task); ++ lsm_blob_size_update(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev); ++ lsm_blob_size_update(&blobs->lbs_xattr_count, ++ &blob_sizes.lbs_xattr_count); ++ lsm_blob_size_update(&blobs->lbs_bdev, &blob_sizes.lbs_bdev); + } + + /* Initialize a given LSM, if it is enabled. */ +-- +2.50.1 (Apple Git-155) + diff --git a/1317-lsm-cleanup-initialize-lsm-and-rename-to-lsm-init-single.patch b/1317-lsm-cleanup-initialize-lsm-and-rename-to-lsm-init-single.patch new file mode 100644 index 000000000..0d033d6eb --- /dev/null +++ b/1317-lsm-cleanup-initialize-lsm-and-rename-to-lsm-init-single.patch @@ -0,0 +1,88 @@ +From 800c1228fa046d32687562f80731aa1cda374657 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:28:42 +0200 +Subject: [PATCH] lsm: cleanup initialize_lsm() and rename to lsm_init_single() + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit 27be5600fe852c52d5b70f4ac9406879b39c864e +Author: Paul Moore +Date: Tue Feb 11 18:24:04 2025 -0500 + + lsm: cleanup initialize_lsm() and rename to lsm_init_single() + + Rename initialize_lsm() to be more consistent with the rest of the LSM + initialization changes and rework the function itself to better fit + with the "exit on fail" coding pattern. + + Reviewed-by: Kees Cook + Reviewed-by: John Johansen + Reviewed-by: Casey Schaufler + Reviewed-by: Mimi Zohar + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm_init.c b/security/lsm_init.c +index bc9191b53305..8cf53a48f37c 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -169,6 +169,7 @@ static void __init lsm_order_append(struct lsm_info *lsm, const char *src) + lsm_is_enabled(lsm) ? "enabled" : "disabled"); + } + ++ + /** + * lsm_blob_size_update - Update the LSM blob size and offset information + * @sz_req: the requested additional blob size +@@ -222,16 +223,20 @@ static void __init lsm_prepare(struct lsm_info *lsm) + lsm_blob_size_update(&blobs->lbs_bdev, &blob_sizes.lbs_bdev); + } + +-/* Initialize a given LSM, if it is enabled. */ +-static void __init initialize_lsm(struct lsm_info *lsm) ++/** ++ * lsm_init_single - Initialize a given LSM ++ * @lsm: LSM definition ++ */ ++static void __init lsm_init_single(struct lsm_info *lsm) + { +- if (lsm_is_enabled(lsm)) { +- int ret; ++ int ret; + +- init_debug("initializing %s\n", lsm->id->name); +- ret = lsm->init(); +- WARN(ret, "%s failed to initialize: %d\n", lsm->id->name, ret); +- } ++ if (!lsm_is_enabled(lsm)) ++ return; ++ ++ init_debug("initializing %s\n", lsm->id->name); ++ ret = lsm->init(); ++ WARN(ret, "%s failed to initialize: %d\n", lsm->id->name, ret); + } + + /** +@@ -373,7 +378,7 @@ static void __init lsm_init_ordered(void) + panic("%s: early task alloc failed.\n", __func__); + + lsm_order_for_each(lsm) { +- initialize_lsm(*lsm); ++ lsm_init_single(*lsm); + } + } + +@@ -427,7 +432,7 @@ int __init early_security_init(void) + lsm_enabled_set(lsm, true); + lsm_order_append(lsm, "early"); + lsm_prepare(lsm); +- initialize_lsm(lsm); ++ lsm_init_single(lsm); + } + + early_security_initialized = true; +-- +2.50.1 (Apple Git-155) + diff --git a/1318-lsm-fold-lsm-init-ordered-into-security-init.patch b/1318-lsm-fold-lsm-init-ordered-into-security-init.patch new file mode 100644 index 000000000..e5fbcde97 --- /dev/null +++ b/1318-lsm-fold-lsm-init-ordered-into-security-init.patch @@ -0,0 +1,236 @@ +From 4b0683928d38d0fd4d8ce409bb75adfb669b9eb7 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:28:42 +0200 +Subject: [PATCH] lsm: fold lsm_init_ordered() into security_init() + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 +Conflicts: + - conflict due to 5816bf4273ed ("lsm,selinux: Add LSM blob support for + BPF objects"), which is not backported + +commit 45a41d1394aa2ed0305f0560f93bb87be7192481 +Author: Paul Moore +Date: Wed Feb 12 18:10:37 2025 -0500 + + lsm: fold lsm_init_ordered() into security_init() + + With only security_init() calling lsm_init_ordered, it makes little + sense to keep lsm_init_ordered() as a standalone function. Fold + lsm_init_ordered() into security_init(). + + Reviewed-by: Casey Schaufler + Reviewed-by: John Johansen + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm_init.c b/security/lsm_init.c +index 8cf53a48f37c..560ce78f3493 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -18,6 +18,9 @@ static __initdata int lsm_enabled_false = 0; + extern struct lsm_info __start_lsm_info[], __end_lsm_info[]; + extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[]; + ++/* Number of "early" LSMs */ ++static __initdata unsigned int lsm_count_early; ++ + /* Build and boot-time LSM ordering. */ + static __initconst const char *const lsm_order_builtin = CONFIG_LSM; + static __initdata const char *lsm_order_cmdline; +@@ -169,7 +172,6 @@ static void __init lsm_order_append(struct lsm_info *lsm, const char *src) + lsm_is_enabled(lsm) ? "enabled" : "disabled"); + } + +- + /** + * lsm_blob_size_update - Update the LSM blob size and offset information + * @sz_req: the requested additional blob size +@@ -310,78 +312,6 @@ static void __init lsm_order_parse(const char *list, const char *src) + } + } + +-/** +- * lsm_init_ordered - Initialize the ordered LSMs +- */ +-static void __init lsm_init_ordered(void) +-{ +- unsigned int first = 0; +- struct lsm_info **lsm; +- struct lsm_info *early; +- +- if (lsm_order_cmdline) { +- if (lsm_order_legacy) { +- pr_warn("security=%s is ignored because it is superseded by lsm=%s\n", +- lsm_order_legacy, lsm_order_cmdline); +- lsm_order_legacy = NULL; +- } +- lsm_order_parse(lsm_order_cmdline, "cmdline"); +- } else +- lsm_order_parse(lsm_order_builtin, "builtin"); +- +- lsm_order_for_each(lsm) { +- lsm_prepare(*lsm); +- } +- +- pr_info("initializing lsm="); +- lsm_early_for_each_raw(early) { +- if (lsm_is_enabled(early)) +- pr_cont("%s%s", +- first++ == 0 ? "" : ",", early->id->name); +- } +- lsm_order_for_each(lsm) { +- if (lsm_is_enabled(*lsm)) +- pr_cont("%s%s", +- first++ == 0 ? "" : ",", (*lsm)->id->name); +- } +- pr_cont("\n"); +- +- init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); +- init_debug("file blob size = %d\n", blob_sizes.lbs_file); +- init_debug("ib blob size = %d\n", blob_sizes.lbs_ib); +- init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); +- init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc); +-#ifdef CONFIG_KEYS +- init_debug("key blob size = %d\n", blob_sizes.lbs_key); +-#endif /* CONFIG_KEYS */ +- init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg); +- init_debug("sock blob size = %d\n", blob_sizes.lbs_sock); +- init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock); +- init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event); +- init_debug("task blob size = %d\n", blob_sizes.lbs_task); +- init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev); +- init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count); +- init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev); +- +- if (blob_sizes.lbs_file) +- lsm_file_cache = kmem_cache_create("lsm_file_cache", +- blob_sizes.lbs_file, 0, +- SLAB_PANIC, NULL); +- if (blob_sizes.lbs_inode) +- lsm_inode_cache = kmem_cache_create("lsm_inode_cache", +- blob_sizes.lbs_inode, 0, +- SLAB_PANIC, NULL); +- +- if (lsm_cred_alloc((struct cred __rcu *)current->cred, GFP_KERNEL)) +- panic("%s: early cred alloc failed.\n", __func__); +- if (lsm_task_alloc(current)) +- panic("%s: early task alloc failed.\n", __func__); +- +- lsm_order_for_each(lsm) { +- lsm_init_single(*lsm); +- } +-} +- + static void __init lsm_static_call_init(struct security_hook_list *hl) + { + struct lsm_static_call *scall = hl->scalls; +@@ -433,6 +363,7 @@ int __init early_security_init(void) + lsm_order_append(lsm, "early"); + lsm_prepare(lsm); + lsm_init_single(lsm); ++ lsm_count_early++; + } + + early_security_initialized = true; +@@ -440,29 +371,85 @@ int __init early_security_init(void) + } + + /** +- * security_init - initializes the security framework ++ * security_init - Initializes the LSM framework + * + * This should be called early in the kernel initialization sequence. + */ + int __init security_init(void) + { +- struct lsm_info *lsm; ++ unsigned int cnt; ++ struct lsm_info **lsm; ++ struct lsm_info *early; ++ unsigned int first = 0; + + init_debug("legacy security=%s\n", lsm_order_legacy ? : " *unspecified*"); + init_debug(" CONFIG_LSM=%s\n", lsm_order_builtin); + init_debug("boot arg lsm=%s\n", lsm_order_cmdline ? : " *unspecified*"); + +- /* +- * Append the names of the early LSM modules now that kmalloc() is +- * available +- */ +- lsm_early_for_each_raw(lsm) { +- init_debug(" early started: %s (%s)\n", lsm->id->name, +- lsm_is_enabled(lsm) ? "enabled" : "disabled"); ++ if (lsm_order_cmdline) { ++ if (lsm_order_legacy) { ++ pr_warn("security=%s is ignored because it is superseded by lsm=%s\n", ++ lsm_order_legacy, lsm_order_cmdline); ++ lsm_order_legacy = NULL; ++ } ++ lsm_order_parse(lsm_order_cmdline, "cmdline"); ++ } else ++ lsm_order_parse(lsm_order_builtin, "builtin"); ++ ++ lsm_order_for_each(lsm) ++ lsm_prepare(*lsm); ++ ++ pr_info("initializing lsm="); ++ lsm_early_for_each_raw(early) { ++ if (lsm_is_enabled(early)) ++ pr_cont("%s%s", ++ first++ == 0 ? "" : ",", early->id->name); ++ } ++ lsm_order_for_each(lsm) { ++ if (lsm_is_enabled(*lsm)) ++ pr_cont("%s%s", ++ first++ == 0 ? "" : ",", (*lsm)->id->name); + } ++ pr_cont("\n"); ++ ++ init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); ++ init_debug("file blob size = %d\n", blob_sizes.lbs_file); ++ init_debug("ib blob size = %d\n", blob_sizes.lbs_ib); ++ init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); ++ init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc); ++#ifdef CONFIG_KEYS ++ init_debug("key blob size = %d\n", blob_sizes.lbs_key); ++#endif /* CONFIG_KEYS */ ++ init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg); ++ init_debug("sock blob size = %d\n", blob_sizes.lbs_sock); ++ init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock); ++ init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event); ++ init_debug("task blob size = %d\n", blob_sizes.lbs_task); ++ init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev); ++ init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count); ++ init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev); ++ ++ if (blob_sizes.lbs_file) ++ lsm_file_cache = kmem_cache_create("lsm_file_cache", ++ blob_sizes.lbs_file, 0, ++ SLAB_PANIC, NULL); ++ if (blob_sizes.lbs_inode) ++ lsm_inode_cache = kmem_cache_create("lsm_inode_cache", ++ blob_sizes.lbs_inode, 0, ++ SLAB_PANIC, NULL); + +- /* Load LSMs in specified order. */ +- lsm_init_ordered(); ++ if (lsm_cred_alloc((struct cred __rcu *)current->cred, GFP_KERNEL)) ++ panic("%s: early cred alloc failed.\n", __func__); ++ if (lsm_task_alloc(current)) ++ panic("%s: early task alloc failed.\n", __func__); ++ ++ cnt = 0; ++ lsm_order_for_each(lsm) { ++ /* skip the "early" LSMs as they have already been setup */ ++ if (cnt++ < lsm_count_early) ++ continue; ++ lsm_init_single(*lsm); ++ } + + return 0; + } +-- +2.50.1 (Apple Git-155) + diff --git a/1319-lsm-add-tweak-function-header-comment-blocks-in-lsm-init-c.patch b/1319-lsm-add-tweak-function-header-comment-blocks-in-lsm-init-c.patch new file mode 100644 index 000000000..ab172dd4e --- /dev/null +++ b/1319-lsm-add-tweak-function-header-comment-blocks-in-lsm-init-c.patch @@ -0,0 +1,70 @@ +From 5a8181960a7f242505b8b6782253a87545aec9d3 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:28:43 +0200 +Subject: [PATCH] lsm: add/tweak function header comment blocks in lsm_init.c + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 + +commit 450705334f698990804b470437f3014cee979486 +Author: Paul Moore +Date: Wed Feb 12 18:17:03 2025 -0500 + + lsm: add/tweak function header comment blocks in lsm_init.c + + Add function header comments for lsm_static_call_init() and + early_security_init(), tweak the existing comment block for + security_add_hooks(). + + Reviewed-by: Casey Schaufler + Reviewed-by: John Johansen + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm_init.c b/security/lsm_init.c +index 560ce78f3493..a7eeeca2cb67 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -312,6 +312,10 @@ static void __init lsm_order_parse(const char *list, const char *src) + } + } + ++/** ++ * lsm_static_call_init - Initialize a LSM's static calls ++ * @hl: LSM hook list ++ */ + static void __init lsm_static_call_init(struct security_hook_list *hl) + { + struct lsm_static_call *scall = hl->scalls; +@@ -332,12 +336,12 @@ static void __init lsm_static_call_init(struct security_hook_list *hl) + } + + /** +- * security_add_hooks - Add a modules hooks to the hook lists. +- * @hooks: the hooks to add +- * @count: the number of hooks to add +- * @lsmid: the identification information for the security module ++ * security_add_hooks - Add a LSM's hooks to the LSM framework's hook lists ++ * @hooks: LSM hooks to add ++ * @count: number of hooks to add ++ * @lsmid: identification information for the LSM + * +- * Each LSM has to register its hooks with the infrastructure. ++ * Each LSM has to register its hooks with the LSM framework. + */ + void __init security_add_hooks(struct security_hook_list *hooks, int count, + const struct lsm_id *lsmid) +@@ -350,6 +354,9 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count, + } + } + ++/** ++ * early_security_init - Initialize the early LSMs ++ */ + int __init early_security_init(void) + { + struct lsm_info *lsm; +-- +2.50.1 (Apple Git-155) + diff --git a/1320-lsm-cleanup-the-debug-and-console-output-in-lsm-init-c.patch b/1320-lsm-cleanup-the-debug-and-console-output-in-lsm-init-c.patch new file mode 100644 index 000000000..9620f5e79 --- /dev/null +++ b/1320-lsm-cleanup-the-debug-and-console-output-in-lsm-init-c.patch @@ -0,0 +1,299 @@ +From 455eabf0b6cf32582a8f485f2628a4e0a90fe398 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Mon, 1 Jun 2026 10:30:41 +0200 +Subject: [PATCH] lsm: cleanup the debug and console output in lsm_init.c + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 +Conflicts: + - conflict due to 5816bf4273ed ("lsm,selinux: Add LSM blob support for + BPF objects"), which is not backported + +commit 5137e583ba2635b82667dc63cb35305750420411 +Author: Paul Moore +Date: Wed Feb 12 18:20:01 2025 -0500 + + lsm: cleanup the debug and console output in lsm_init.c + + Move away from an init specific init_debug() macro to a more general + lsm_pr()/lsm_pr_cont()/lsm_pr_dbg() set of macros that are available + both before and after init. In the process we do a number of minor + changes to improve the LSM initialization output and cleanup the code + somewhat. + + Reviewed-by: Casey Schaufler + Reviewed-by: John Johansen + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/lsm.h b/security/lsm.h +index dbe755c45e57..8dc267977ae0 100644 +--- a/security/lsm.h ++++ b/security/lsm.h +@@ -6,9 +6,20 @@ + #ifndef _LSM_H_ + #define _LSM_H_ + ++#include + #include + #include + ++/* LSM debugging */ ++extern bool lsm_debug; ++#define lsm_pr(...) pr_info(__VA_ARGS__) ++#define lsm_pr_cont(...) pr_cont(__VA_ARGS__) ++#define lsm_pr_dbg(...) \ ++ do { \ ++ if (lsm_debug) \ ++ pr_info(__VA_ARGS__); \ ++ } while (0) ++ + /* List of configured LSMs */ + extern unsigned int lsm_active_cnt; + extern const struct lsm_id *lsm_idlist[]; +diff --git a/security/lsm_init.c b/security/lsm_init.c +index a7eeeca2cb67..bef21da72c16 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -30,13 +30,6 @@ static __initdata const char *lsm_order_legacy; + static __initdata struct lsm_info *lsm_order[MAX_LSM_COUNT + 1]; + static __initdata struct lsm_info *lsm_exclusive; + +-static __initdata bool debug; +-#define init_debug(...) \ +- do { \ +- if (debug) \ +- pr_info(__VA_ARGS__); \ +- } while (0) +- + #define lsm_order_for_each(iter) \ + for ((iter) = lsm_order; *(iter); (iter)++) + #define lsm_for_each_raw(iter) \ +@@ -77,7 +70,7 @@ __setup("lsm=", lsm_choose_lsm); + */ + static int __init lsm_debug_enable(char *str) + { +- debug = true; ++ lsm_debug = true; + return 1; + } + __setup("lsm.debug", lsm_debug_enable); +@@ -143,22 +136,28 @@ static void __init lsm_order_append(struct lsm_info *lsm, const char *src) + return; + + /* Skip explicitly disabled LSMs. */ +- if (lsm->enabled && !lsm_is_enabled(lsm)) +- goto out; ++ if (lsm->enabled && !lsm_is_enabled(lsm)) { ++ lsm_pr_dbg("skip previously disabled LSM %s:%s\n", ++ src, lsm->id->name); ++ return; ++ } + +- if (WARN(lsm_active_cnt == MAX_LSM_COUNT, +- "%s: out of LSM static calls!?\n", src)) { ++ if (lsm_active_cnt == MAX_LSM_COUNT) { ++ pr_warn("exceeded maximum LSM count on %s:%s\n", ++ src, lsm->id->name); + lsm_enabled_set(lsm, false); +- goto out; ++ return; + } + + if (lsm->flags & LSM_FLAG_EXCLUSIVE) { + if (lsm_exclusive) { +- init_debug("exclusive disabled: %s\n", lsm->id->name); ++ lsm_pr_dbg("skip exclusive LSM conflict %s:%s\n", ++ src, lsm->id->name); + lsm_enabled_set(lsm, false); +- goto out; ++ return; + } else { +- init_debug("exclusive chosen: %s\n", lsm->id->name); ++ lsm_pr_dbg("select exclusive LSM %s:%s\n", ++ src, lsm->id->name); + lsm_exclusive = lsm; + } + } +@@ -167,9 +166,7 @@ static void __init lsm_order_append(struct lsm_info *lsm, const char *src) + lsm_order[lsm_active_cnt] = lsm; + lsm_idlist[lsm_active_cnt++] = lsm->id; + +-out: +- init_debug("%s ordered: %s (%s)\n", src, lsm->id->name, +- lsm_is_enabled(lsm) ? "enabled" : "disabled"); ++ lsm_pr_dbg("enabling LSM %s:%s\n", src, lsm->id->name); + } + + /** +@@ -236,7 +233,7 @@ static void __init lsm_init_single(struct lsm_info *lsm) + if (!lsm_is_enabled(lsm)) + return; + +- init_debug("initializing %s\n", lsm->id->name); ++ lsm_pr_dbg("initializing %s\n", lsm->id->name); + ret = lsm->init(); + WARN(ret, "%s failed to initialize: %d\n", lsm->id->name, ret); + } +@@ -263,8 +260,8 @@ static void __init lsm_order_parse(const char *list, const char *src) + if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) && + strcmp(lsm->id->name, lsm_order_legacy)) { + lsm_enabled_set(lsm, false); +- init_debug("security=%s disabled: %s (only one legacy major LSM)\n", +- lsm_order_legacy, lsm->id->name); ++ lsm_pr_dbg("skip legacy LSM conflict %s:%s\n", ++ src, lsm->id->name); + } + } + } +@@ -307,8 +304,7 @@ static void __init lsm_order_parse(const char *list, const char *src) + if (lsm_order_exists(lsm)) + continue; + lsm_enabled_set(lsm, false); +- init_debug("%s skipped: %s (not in requested order)\n", +- src, lsm->id->name); ++ lsm_pr_dbg("skip disabled LSM %s:%s\n", src, lsm->id->name); + } + } + +@@ -316,7 +312,7 @@ static void __init lsm_order_parse(const char *list, const char *src) + * lsm_static_call_init - Initialize a LSM's static calls + * @hl: LSM hook list + */ +-static void __init lsm_static_call_init(struct security_hook_list *hl) ++static int __init lsm_static_call_init(struct security_hook_list *hl) + { + struct lsm_static_call *scall = hl->scalls; + int i; +@@ -328,11 +324,12 @@ static void __init lsm_static_call_init(struct security_hook_list *hl) + hl->hook.lsm_func_addr); + scall->hl = hl; + static_branch_enable(scall->active); +- return; ++ return 0; + } + scall++; + } +- panic("%s - Ran out of static slots.\n", __func__); ++ ++ return -ENOSPC; + } + + /** +@@ -350,7 +347,9 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count, + + for (i = 0; i < count; i++) { + hooks[i].lsmid = lsmid; +- lsm_static_call_init(&hooks[i]); ++ if (lsm_static_call_init(&hooks[i])) ++ panic("exhausted LSM callback slots with LSM %s\n", ++ lsmid->name); + } + } + +@@ -386,19 +385,16 @@ int __init security_init(void) + { + unsigned int cnt; + struct lsm_info **lsm; +- struct lsm_info *early; +- unsigned int first = 0; + +- init_debug("legacy security=%s\n", lsm_order_legacy ? : " *unspecified*"); +- init_debug(" CONFIG_LSM=%s\n", lsm_order_builtin); +- init_debug("boot arg lsm=%s\n", lsm_order_cmdline ? : " *unspecified*"); ++ if (lsm_debug) { ++ lsm_pr("built-in LSM list: %s\n", lsm_order_builtin); ++ lsm_pr("legacy LSM parameter: %s\n", lsm_order_legacy); ++ lsm_pr("boot LSM parameter: %s\n", lsm_order_cmdline); ++ } + + if (lsm_order_cmdline) { +- if (lsm_order_legacy) { +- pr_warn("security=%s is ignored because it is superseded by lsm=%s\n", +- lsm_order_legacy, lsm_order_cmdline); ++ if (lsm_order_legacy) + lsm_order_legacy = NULL; +- } + lsm_order_parse(lsm_order_cmdline, "cmdline"); + } else + lsm_order_parse(lsm_order_builtin, "builtin"); +@@ -406,35 +402,22 @@ int __init security_init(void) + lsm_order_for_each(lsm) + lsm_prepare(*lsm); + +- pr_info("initializing lsm="); +- lsm_early_for_each_raw(early) { +- if (lsm_is_enabled(early)) +- pr_cont("%s%s", +- first++ == 0 ? "" : ",", early->id->name); +- } +- lsm_order_for_each(lsm) { +- if (lsm_is_enabled(*lsm)) +- pr_cont("%s%s", +- first++ == 0 ? "" : ",", (*lsm)->id->name); ++ if (lsm_debug) { ++ lsm_pr("blob(cred) size %d\n", blob_sizes.lbs_cred); ++ lsm_pr("blob(file) size %d\n", blob_sizes.lbs_file); ++ lsm_pr("blob(ib) size %d\n", blob_sizes.lbs_ib); ++ lsm_pr("blob(inode) size %d\n", blob_sizes.lbs_inode); ++ lsm_pr("blob(ipc) size %d\n", blob_sizes.lbs_ipc); ++ lsm_pr("blob(key) size %d\n", blob_sizes.lbs_key); ++ lsm_pr("blob(msg_msg)_size %d\n", blob_sizes.lbs_msg_msg); ++ lsm_pr("blob(sock) size %d\n", blob_sizes.lbs_sock); ++ lsm_pr("blob(superblock) size %d\n", blob_sizes.lbs_superblock); ++ lsm_pr("blob(perf_event) size %d\n", blob_sizes.lbs_perf_event); ++ lsm_pr("blob(task) size %d\n", blob_sizes.lbs_task); ++ lsm_pr("blob(tun_dev) size %d\n", blob_sizes.lbs_tun_dev); ++ lsm_pr("blob(xattr) count %d\n", blob_sizes.lbs_xattr_count); ++ lsm_pr("blob(bdev) size %d\n", blob_sizes.lbs_bdev); + } +- pr_cont("\n"); +- +- init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); +- init_debug("file blob size = %d\n", blob_sizes.lbs_file); +- init_debug("ib blob size = %d\n", blob_sizes.lbs_ib); +- init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); +- init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc); +-#ifdef CONFIG_KEYS +- init_debug("key blob size = %d\n", blob_sizes.lbs_key); +-#endif /* CONFIG_KEYS */ +- init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg); +- init_debug("sock blob size = %d\n", blob_sizes.lbs_sock); +- init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock); +- init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event); +- init_debug("task blob size = %d\n", blob_sizes.lbs_task); +- init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev); +- init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count); +- init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev); + + if (blob_sizes.lbs_file) + lsm_file_cache = kmem_cache_create("lsm_file_cache", +@@ -446,9 +429,9 @@ int __init security_init(void) + SLAB_PANIC, NULL); + + if (lsm_cred_alloc((struct cred __rcu *)current->cred, GFP_KERNEL)) +- panic("%s: early cred alloc failed.\n", __func__); ++ panic("early LSM cred alloc failed\n"); + if (lsm_task_alloc(current)) +- panic("%s: early task alloc failed.\n", __func__); ++ panic("early LSM task alloc failed\n"); + + cnt = 0; + lsm_order_for_each(lsm) { +diff --git a/security/security.c b/security/security.c +index 6f20f44c8d13..56c48725e334 100644 +--- a/security/security.c ++++ b/security/security.c +@@ -74,6 +74,8 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = { + [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality", + }; + ++bool lsm_debug __ro_after_init; ++ + unsigned int lsm_active_cnt __ro_after_init; + const struct lsm_id *lsm_idlist[MAX_LSM_COUNT]; + +-- +2.50.1 (Apple Git-155) + diff --git a/1321-fs-prepare-for-adding-lsm-blob-to-backing-file.patch b/1321-fs-prepare-for-adding-lsm-blob-to-backing-file.patch new file mode 100644 index 000000000..32aa1b0a5 --- /dev/null +++ b/1321-fs-prepare-for-adding-lsm-blob-to-backing-file.patch @@ -0,0 +1,88 @@ +From 1367930b371086acec799dd7062ce70dae049608 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Tue, 2 Jun 2026 10:09:51 +0200 +Subject: [PATCH] fs: prepare for adding LSM blob to backing_file + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 +Conflicts: + - fs/file_table.c: trivial context fuzz + +commit 880bd496ec72a6dcb00cb70c430ef752ba242ae7 +Author: Amir Goldstein +Date: Mon Mar 30 10:27:51 2026 +0200 + + fs: prepare for adding LSM blob to backing_file + + In preparation to adding LSM blob to backing_file struct, factor out + helpers init_backing_file() and backing_file_free(). + + Cc: stable@vger.kernel.org + Cc: linux-fsdevel@vger.kernel.org + Cc: linux-unionfs@vger.kernel.org + Cc: linux-erofs@lists.ozlabs.org + Signed-off-by: Amir Goldstein + Reviewed-by: Serge Hallyn + [PM: use the term "LSM blob", fix comment style to match file] + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/fs/file_table.c b/fs/file_table.c +index 9979efb9a380..4b4d50a1d9da 100644 +--- a/fs/file_table.c ++++ b/fs/file_table.c +@@ -67,6 +67,12 @@ void backing_file_set_user_path(struct file *f, const struct path *path) + } + EXPORT_SYMBOL_GPL(backing_file_set_user_path); + ++static inline void backing_file_free(struct backing_file *ff) ++{ ++ path_put(&ff->user_path); ++ kmem_cache_free(bfilp_cachep, ff); ++} ++ + static inline void file_free(struct file *f) + { + security_file_free(f); +@@ -74,8 +80,7 @@ static inline void file_free(struct file *f) + percpu_counter_dec(&nr_files); + put_cred(f->f_cred); + if (unlikely(f->f_mode & FMODE_BACKING)) { +- path_put(backing_file_user_path(f)); +- kmem_cache_free(bfilp_cachep, backing_file(f)); ++ backing_file_free(backing_file(f)); + } else { + kmem_cache_free(filp_cachep, f); + } +@@ -278,6 +283,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred) + return f; + } + ++static int init_backing_file(struct backing_file *ff) ++{ ++ memset(&ff->user_path, 0, sizeof(ff->user_path)); ++ return 0; ++} ++ + /* + * Variant of alloc_empty_file() that allocates a backing_file container + * and doesn't check and modify nr_files. +@@ -300,7 +311,14 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred) + return ERR_PTR(error); + } + ++ /* The f_mode flags must be set before fput(). */ + ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT; ++ error = init_backing_file(ff); ++ if (unlikely(error)) { ++ fput(&ff->file); ++ return ERR_PTR(error); ++ } ++ + return &ff->file; + } + +-- +2.50.1 (Apple Git-155) + diff --git a/1322-lsm-add-backing-file-lsm-hooks.patch b/1322-lsm-add-backing-file-lsm-hooks.patch new file mode 100644 index 000000000..4d044d1f4 --- /dev/null +++ b/1322-lsm-add-backing-file-lsm-hooks.patch @@ -0,0 +1,569 @@ +From c2685f8b7b4392f68d8492a331de2f544cc80a8b Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Tue, 2 Jun 2026 10:36:28 +0200 +Subject: [PATCH] lsm: add backing_file LSM hooks + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 +Conflicts: + - fs/backing-file.c: adjust backing_file_mmap() to downstream state (missing + scoped guards) + - fs/erofs/ishare.c: hunk dropped, files not in RHEL10 + - fs/overlayfs/dir.c: adjust to different indentation + - fs/overlayfs/file.c: adjust to different indentation + +commit 6af36aeb147a06dea47c49859cd6ca5659aeb987 +Author: Paul Moore +Date: Fri Dec 19 13:18:22 2025 -0500 + + lsm: add backing_file LSM hooks + + Stacked filesystems such as overlayfs do not currently provide the + necessary mechanisms for LSMs to properly enforce access controls on the + mmap() and mprotect() operations. In order to resolve this gap, a LSM + security blob is being added to the backing_file struct and the following + new LSM hooks are being created: + + security_backing_file_alloc() + security_backing_file_free() + security_mmap_backing_file() + + The first two hooks are to manage the lifecycle of the LSM security blob + in the backing_file struct, while the third provides a new mmap() access + control point for the underlying backing file. It is also expected that + LSMs will likely want to update their security_file_mprotect() callback + to address issues with their mprotect() controls, but that does not + require a change to the security_file_mprotect() LSM hook. + + There are a three other small changes to support these new LSM hooks: + * Pass the user file associated with a backing file down to + alloc_empty_backing_file() so it can be included in the + security_backing_file_alloc() hook. + * Add getter and setter functions for the backing_file struct LSM blob + as the backing_file struct remains private to fs/file_table.c. + * Constify the file struct field in the LSM common_audit_data struct to + better support LSMs that need to pass a const file struct pointer into + the common LSM audit code. + + Thanks to Arnd Bergmann for identifying the missing EXPORT_SYMBOL_GPL() + and supplying a fixup. + + Cc: stable@vger.kernel.org + Cc: linux-fsdevel@vger.kernel.org + Cc: linux-unionfs@vger.kernel.org + Cc: linux-erofs@lists.ozlabs.org + Reviewed-by: Amir Goldstein + Reviewed-by: Serge Hallyn + Reviewed-by: Christian Brauner + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/fs/backing-file.c b/fs/backing-file.c +index 892361c31c3d..bb7ecb0fc3ae 100644 +--- a/fs/backing-file.c ++++ b/fs/backing-file.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + #include "internal.h" + +@@ -29,14 +30,15 @@ + * returned file into a container structure that also stores the stacked + * file's path, which can be retrieved using backing_file_user_path(). + */ +-struct file *backing_file_open(const struct path *user_path, int flags, ++struct file *backing_file_open(const struct file *user_file, int flags, + const struct path *real_path, + const struct cred *cred) + { ++ const struct path *user_path = &user_file->f_path; + struct file *f; + int error; + +- f = alloc_empty_backing_file(flags, cred); ++ f = alloc_empty_backing_file(flags, cred, user_file); + if (IS_ERR(f)) + return f; + +@@ -52,15 +54,16 @@ struct file *backing_file_open(const struct path *user_path, int flags, + } + EXPORT_SYMBOL_GPL(backing_file_open); + +-struct file *backing_tmpfile_open(const struct path *user_path, int flags, ++struct file *backing_tmpfile_open(const struct file *user_file, int flags, + const struct path *real_parentpath, + umode_t mode, const struct cred *cred) + { + struct mnt_idmap *real_idmap = mnt_idmap(real_parentpath->mnt); ++ const struct path *user_path = &user_file->f_path; + struct file *f; + int error; + +- f = alloc_empty_backing_file(flags, cred); ++ f = alloc_empty_backing_file(flags, cred, user_file); + if (IS_ERR(f)) + return f; + +@@ -339,6 +342,11 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma, + vma_set_file(vma, file); + + old_cred = override_creds(ctx->cred); ++ ret = security_mmap_backing_file(vma, file, ctx->user_file); ++ if (ret) { ++ revert_creds(old_cred); ++ return ret; ++ } + ret = call_mmap(vma->vm_file, vma); + revert_creds(old_cred); + +diff --git a/fs/file_table.c b/fs/file_table.c +index 4b4d50a1d9da..0a7f25596d57 100644 +--- a/fs/file_table.c ++++ b/fs/file_table.c +@@ -51,6 +51,9 @@ struct backing_file { + struct path user_path; + freeptr_t bf_freeptr; + }; ++#ifdef CONFIG_SECURITY ++ void *security; ++#endif + }; + + #define backing_file(f) container_of(f, struct backing_file, file) +@@ -67,8 +70,21 @@ void backing_file_set_user_path(struct file *f, const struct path *path) + } + EXPORT_SYMBOL_GPL(backing_file_set_user_path); + ++#ifdef CONFIG_SECURITY ++void *backing_file_security(const struct file *f) ++{ ++ return backing_file(f)->security; ++} ++ ++void backing_file_set_security(struct file *f, void *security) ++{ ++ backing_file(f)->security = security; ++} ++#endif /* CONFIG_SECURITY */ ++ + static inline void backing_file_free(struct backing_file *ff) + { ++ security_backing_file_free(&ff->file); + path_put(&ff->user_path); + kmem_cache_free(bfilp_cachep, ff); + } +@@ -283,10 +299,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred) + return f; + } + +-static int init_backing_file(struct backing_file *ff) ++static int init_backing_file(struct backing_file *ff, ++ const struct file *user_file) + { + memset(&ff->user_path, 0, sizeof(ff->user_path)); +- return 0; ++ backing_file_set_security(&ff->file, NULL); ++ return security_backing_file_alloc(&ff->file, user_file); + } + + /* +@@ -296,7 +314,8 @@ static int init_backing_file(struct backing_file *ff) + * This is only for kernel internal use, and the allocate file must not be + * installed into file tables or such. + */ +-struct file *alloc_empty_backing_file(int flags, const struct cred *cred) ++struct file *alloc_empty_backing_file(int flags, const struct cred *cred, ++ const struct file *user_file) + { + struct backing_file *ff; + int error; +@@ -313,7 +332,7 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred) + + /* The f_mode flags must be set before fput(). */ + ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT; +- error = init_backing_file(ff); ++ error = init_backing_file(ff, user_file); + if (unlikely(error)) { + fput(&ff->file); + return ERR_PTR(error); +diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c +index bbac547dfcb3..91a43c4939ec 100644 +--- a/fs/fuse/passthrough.c ++++ b/fs/fuse/passthrough.c +@@ -321,7 +321,7 @@ struct fuse_backing *fuse_passthrough_open(struct file *file, + goto out; + + /* Allocate backing file per fuse file to store fuse path */ +- backing_file = backing_file_open(&file->f_path, file->f_flags, ++ backing_file = backing_file_open(file, file->f_flags, + &fb->file->f_path, fb->cred); + err = PTR_ERR(backing_file); + if (IS_ERR(backing_file)) { +diff --git a/fs/internal.h b/fs/internal.h +index 67e2e164c647..049c800a3b16 100644 +--- a/fs/internal.h ++++ b/fs/internal.h +@@ -99,7 +99,8 @@ extern void chroot_fs_refs(const struct path *, const struct path *); + */ + struct file *alloc_empty_file(int flags, const struct cred *cred); + struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred); +-struct file *alloc_empty_backing_file(int flags, const struct cred *cred); ++struct file *alloc_empty_backing_file(int flags, const struct cred *cred, ++ const struct file *user_file); + void backing_file_set_user_path(struct file *f, const struct path *path); + + static inline void file_put_write_access(struct file *file) +diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c +index ab65e98a1def..1c8009bf194b 100644 +--- a/fs/overlayfs/dir.c ++++ b/fs/overlayfs/dir.c +@@ -1320,7 +1320,7 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry, + goto out_revert_creds; + + ovl_path_upper(dentry->d_parent, &realparentpath); +- realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath, ++ realfile = backing_tmpfile_open(file, flags, &realparentpath, + mode, current_cred()); + err = PTR_ERR_OR_ZERO(realfile); + pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err); +diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c +index 4444c78e2e0c..84aad9d19bdd 100644 +--- a/fs/overlayfs/file.c ++++ b/fs/overlayfs/file.c +@@ -48,7 +48,7 @@ static struct file *ovl_open_realfile(const struct file *file, + if (!inode_owner_or_capable(real_idmap, realinode)) + flags &= ~O_NOATIME; + +- realfile = backing_file_open(&file->f_path, flags, realpath, ++ realfile = backing_file_open(file, flags, realpath, + current_cred()); + } + revert_creds(old_cred); +diff --git a/include/linux/backing-file.h b/include/linux/backing-file.h +index 2eed0ffb5e8f..cd18acd7ac5b 100644 +--- a/include/linux/backing-file.h ++++ b/include/linux/backing-file.h +@@ -19,10 +19,10 @@ struct backing_file_ctx { + void (*end_write)(struct file *, loff_t, ssize_t); + }; + +-struct file *backing_file_open(const struct path *user_path, int flags, ++struct file *backing_file_open(const struct file *user_file, int flags, + const struct path *real_path, + const struct cred *cred); +-struct file *backing_tmpfile_open(const struct path *user_path, int flags, ++struct file *backing_tmpfile_open(const struct file *user_file, int flags, + const struct path *real_parentpath, + umode_t mode, const struct cred *cred); + ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter, +diff --git a/include/linux/fs.h b/include/linux/fs.h +index 04309befc557..975afa36e9aa 100644 +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -2795,6 +2795,19 @@ struct file *dentry_create(const struct path *path, int flags, umode_t mode, + const struct cred *cred); + struct path *backing_file_user_path(const struct file *f); + ++#ifdef CONFIG_SECURITY ++void *backing_file_security(const struct file *f); ++void backing_file_set_security(struct file *f, void *security); ++#else ++static inline void *backing_file_security(const struct file *f) ++{ ++ return NULL; ++} ++static inline void backing_file_set_security(struct file *f, void *security) ++{ ++} ++#endif /* CONFIG_SECURITY */ ++ + /* + * When mmapping a file on a stackable filesystem (e.g., overlayfs), the file + * stored in ->vm_file is a backing file whose f_inode is on the underlying +diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h +index 97a8b21eb033..c0a2839253fa 100644 +--- a/include/linux/lsm_audit.h ++++ b/include/linux/lsm_audit.h +@@ -93,7 +93,7 @@ struct common_audit_data { + #endif + char *kmod_name; + struct lsm_ioctlop_audit *op; +- struct file *file; ++ const struct file *file; + struct lsm_ibpkey_audit *ibpkey; + struct lsm_ibendport_audit *ibendport; + int reason; +diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h +index 27d599bde74a..e06665ae5102 100644 +--- a/include/linux/lsm_hook_defs.h ++++ b/include/linux/lsm_hook_defs.h +@@ -189,6 +189,9 @@ LSM_HOOK(int, 0, file_permission, struct file *file, int mask) + LSM_HOOK(int, 0, file_alloc_security, struct file *file) + LSM_HOOK(void, LSM_RET_VOID, file_release, struct file *file) + LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file) ++LSM_HOOK(int, 0, backing_file_alloc, struct file *backing_file, ++ const struct file *user_file) ++LSM_HOOK(void, LSM_RET_VOID, backing_file_free, struct file *backing_file) + LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd, + unsigned long arg) + LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd, +@@ -196,6 +199,8 @@ LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd, + LSM_HOOK(int, 0, mmap_addr, unsigned long addr) + LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags) ++LSM_HOOK(int, 0, mmap_backing_file, struct vm_area_struct *vma, ++ struct file *backing_file, struct file *user_file) + LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma, + unsigned long reqprot, unsigned long prot) + LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd) +diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h +index a7ecb0791a0f..f1c6db9c0cff 100644 +--- a/include/linux/lsm_hooks.h ++++ b/include/linux/lsm_hooks.h +@@ -104,6 +104,7 @@ struct security_hook_list { + struct lsm_blob_sizes { + unsigned int lbs_cred; + unsigned int lbs_file; ++ unsigned int lbs_backing_file; + unsigned int lbs_ib; + unsigned int lbs_inode; + unsigned int lbs_sock; +diff --git a/include/linux/security.h b/include/linux/security.h +index 2db88ddbe9a9..0a2ef373267b 100644 +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -487,11 +487,17 @@ int security_file_permission(struct file *file, int mask); + int security_file_alloc(struct file *file); + void security_file_release(struct file *file); + void security_file_free(struct file *file); ++int security_backing_file_alloc(struct file *backing_file, ++ const struct file *user_file); ++void security_backing_file_free(struct file *backing_file); + int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg); + int security_file_ioctl_compat(struct file *file, unsigned int cmd, + unsigned long arg); + int security_mmap_file(struct file *file, unsigned long prot, + unsigned long flags); ++int security_mmap_backing_file(struct vm_area_struct *vma, ++ struct file *backing_file, ++ struct file *user_file); + int security_mmap_addr(unsigned long addr); + int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, + unsigned long prot); +@@ -1147,6 +1153,15 @@ static inline void security_file_release(struct file *file) + static inline void security_file_free(struct file *file) + { } + ++static inline int security_backing_file_alloc(struct file *backing_file, ++ const struct file *user_file) ++{ ++ return 0; ++} ++ ++static inline void security_backing_file_free(struct file *backing_file) ++{ } ++ + static inline int security_file_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) + { +@@ -1166,6 +1181,13 @@ static inline int security_mmap_file(struct file *file, unsigned long prot, + return 0; + } + ++static inline int security_mmap_backing_file(struct vm_area_struct *vma, ++ struct file *backing_file, ++ struct file *user_file) ++{ ++ return 0; ++} ++ + static inline int security_mmap_addr(unsigned long addr) + { + return cap_mmap_addr(addr); +diff --git a/security/lsm.h b/security/lsm.h +index 8dc267977ae0..c3a1a9cd121e 100644 +--- a/security/lsm.h ++++ b/security/lsm.h +@@ -29,6 +29,7 @@ extern struct lsm_blob_sizes blob_sizes; + + /* LSM blob caches */ + extern struct kmem_cache *lsm_file_cache; ++extern struct kmem_cache *lsm_backing_file_cache; + extern struct kmem_cache *lsm_inode_cache; + + /* LSM blob allocators */ +diff --git a/security/lsm_init.c b/security/lsm_init.c +index bef21da72c16..31bc9e2d0f5d 100644 +--- a/security/lsm_init.c ++++ b/security/lsm_init.c +@@ -202,6 +202,8 @@ static void __init lsm_prepare(struct lsm_info *lsm) + blobs = lsm->blobs; + lsm_blob_size_update(&blobs->lbs_cred, &blob_sizes.lbs_cred); + lsm_blob_size_update(&blobs->lbs_file, &blob_sizes.lbs_file); ++ lsm_blob_size_update(&blobs->lbs_backing_file, ++ &blob_sizes.lbs_backing_file); + lsm_blob_size_update(&blobs->lbs_ib, &blob_sizes.lbs_ib); + /* inode blob gets an rcu_head in addition to LSM blobs. */ + if (blobs->lbs_inode && blob_sizes.lbs_inode == 0) +@@ -405,6 +407,8 @@ int __init security_init(void) + if (lsm_debug) { + lsm_pr("blob(cred) size %d\n", blob_sizes.lbs_cred); + lsm_pr("blob(file) size %d\n", blob_sizes.lbs_file); ++ lsm_pr("blob(backing_file) size %d\n", ++ blob_sizes.lbs_backing_file); + lsm_pr("blob(ib) size %d\n", blob_sizes.lbs_ib); + lsm_pr("blob(inode) size %d\n", blob_sizes.lbs_inode); + lsm_pr("blob(ipc) size %d\n", blob_sizes.lbs_ipc); +@@ -423,6 +427,11 @@ int __init security_init(void) + lsm_file_cache = kmem_cache_create("lsm_file_cache", + blob_sizes.lbs_file, 0, + SLAB_PANIC, NULL); ++ if (blob_sizes.lbs_backing_file) ++ lsm_backing_file_cache = kmem_cache_create( ++ "lsm_backing_file_cache", ++ blob_sizes.lbs_backing_file, ++ 0, SLAB_PANIC, NULL); + if (blob_sizes.lbs_inode) + lsm_inode_cache = kmem_cache_create("lsm_inode_cache", + blob_sizes.lbs_inode, 0, +diff --git a/security/security.c b/security/security.c +index 56c48725e334..d9a645d0038e 100644 +--- a/security/security.c ++++ b/security/security.c +@@ -82,6 +82,7 @@ const struct lsm_id *lsm_idlist[MAX_LSM_COUNT]; + struct lsm_blob_sizes blob_sizes; + + struct kmem_cache *lsm_file_cache; ++struct kmem_cache *lsm_backing_file_cache; + struct kmem_cache *lsm_inode_cache; + + #define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX +@@ -173,6 +174,30 @@ static int lsm_file_alloc(struct file *file) + return 0; + } + ++/** ++ * lsm_backing_file_alloc - allocate a composite backing file blob ++ * @backing_file: the backing file ++ * ++ * Allocate the backing file blob for all the modules. ++ * ++ * Returns 0, or -ENOMEM if memory can't be allocated. ++ */ ++static int lsm_backing_file_alloc(struct file *backing_file) ++{ ++ void *blob; ++ ++ if (!lsm_backing_file_cache) { ++ backing_file_set_security(backing_file, NULL); ++ return 0; ++ } ++ ++ blob = kmem_cache_zalloc(lsm_backing_file_cache, GFP_KERNEL); ++ backing_file_set_security(backing_file, blob); ++ if (!blob) ++ return -ENOMEM; ++ return 0; ++} ++ + /** + * lsm_blob_alloc - allocate a composite blob + * @dest: the destination for the blob +@@ -2350,6 +2375,57 @@ void security_file_free(struct file *file) + } + } + ++/** ++ * security_backing_file_alloc() - Allocate and setup a backing file blob ++ * @backing_file: the backing file ++ * @user_file: the associated user visible file ++ * ++ * Allocate a backing file LSM blob and perform any necessary initialization of ++ * the LSM blob. There will be some operations where the LSM will not have ++ * access to @user_file after this point, so any important state associated ++ * with @user_file that is important to the LSM should be captured in the ++ * backing file's LSM blob. ++ * ++ * LSM's should avoid taking a reference to @user_file in this hook as it will ++ * result in problems later when the system attempts to drop/put the file ++ * references due to a circular dependency. ++ * ++ * Return: Return 0 if the hook is successful, negative values otherwise. ++ */ ++int security_backing_file_alloc(struct file *backing_file, ++ const struct file *user_file) ++{ ++ int rc; ++ ++ rc = lsm_backing_file_alloc(backing_file); ++ if (rc) ++ return rc; ++ rc = call_int_hook(backing_file_alloc, backing_file, user_file); ++ if (unlikely(rc)) ++ security_backing_file_free(backing_file); ++ ++ return rc; ++} ++ ++/** ++ * security_backing_file_free() - Free a backing file blob ++ * @backing_file: the backing file ++ * ++ * Free any LSM state associate with a backing file's LSM blob, including the ++ * blob itself. ++ */ ++void security_backing_file_free(struct file *backing_file) ++{ ++ void *blob = backing_file_security(backing_file); ++ ++ call_void_hook(backing_file_free, backing_file); ++ ++ if (blob) { ++ backing_file_set_security(backing_file, NULL); ++ kmem_cache_free(lsm_backing_file_cache, blob); ++ } ++} ++ + /** + * security_file_ioctl() - Check if an ioctl is allowed + * @file: associated file +@@ -2438,6 +2514,32 @@ int security_mmap_file(struct file *file, unsigned long prot, + flags); + } + ++/** ++ * security_mmap_backing_file - Check if mmap'ing a backing file is allowed ++ * @vma: the vm_area_struct for the mmap'd region ++ * @backing_file: the backing file being mmap'd ++ * @user_file: the user file being mmap'd ++ * ++ * Check permissions for a mmap operation on a stacked filesystem. This hook ++ * is called after the security_mmap_file() and is responsible for authorizing ++ * the mmap on @backing_file. It is important to note that the mmap operation ++ * on @user_file has already been authorized and the @vma->vm_file has been ++ * set to @backing_file. ++ * ++ * Return: Returns 0 if permission is granted. ++ */ ++int security_mmap_backing_file(struct vm_area_struct *vma, ++ struct file *backing_file, ++ struct file *user_file) ++{ ++ /* recommended by the stackable filesystem devs */ ++ if (WARN_ON_ONCE(!(backing_file->f_mode & FMODE_BACKING))) ++ return -EIO; ++ ++ return call_int_hook(mmap_backing_file, vma, backing_file, user_file); ++} ++EXPORT_SYMBOL_GPL(security_mmap_backing_file); ++ + /** + * security_mmap_addr() - Check if mmap'ing an address is allowed + * @addr: address +-- +2.50.1 (Apple Git-155) + diff --git a/1323-selinux-fix-overlayfs-mmap-and-mprotect-access-checks.patch b/1323-selinux-fix-overlayfs-mmap-and-mprotect-access-checks.patch new file mode 100644 index 000000000..27084237d --- /dev/null +++ b/1323-selinux-fix-overlayfs-mmap-and-mprotect-access-checks.patch @@ -0,0 +1,429 @@ +From d3989d6fe668083ccd03e1cc73a46b9ed88edd54 Mon Sep 17 00:00:00 2001 +From: Ondrej Mosnacek +Date: Tue, 2 Jun 2026 10:26:36 +0200 +Subject: [PATCH] selinux: fix overlayfs mmap() and mprotect() access checks + +JIRA: https://issues.redhat.com/browse/RHEL-179440 +CVE: CVE-2026-46054 +Conflicts: + - security/selinux/hooks.c: trivial context fuzz + +commit 82544d36b1729153c8aeb179e84750f0c085d3b1 +Author: Paul Moore +Date: Thu Jan 1 17:19:18 2026 -0500 + + selinux: fix overlayfs mmap() and mprotect() access checks + + The existing SELinux security model for overlayfs is to allow access if + the current task is able to access the top level file (the "user" file) + and the mounter's credentials are sufficient to access the lower + level file (the "backing" file). Unfortunately, the current code does + not properly enforce these access controls for both mmap() and mprotect() + operations on overlayfs filesystems. + + This patch makes use of the newly created security_mmap_backing_file() + LSM hook to provide the missing backing file enforcement for mmap() + operations, and leverages the backing file API and new LSM blob to + provide the necessary information to properly enforce the mprotect() + access controls. + + Cc: stable@vger.kernel.org + Acked-by: Amir Goldstein + Signed-off-by: Paul Moore + +Signed-off-by: Ondrej Mosnacek + +diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c +index a2323c6daafe..853cf9cee2ec 100644 +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -1724,49 +1724,72 @@ static inline int file_path_has_perm(const struct cred *cred, + static int bpf_fd_pass(const struct file *file, u32 sid); + #endif + +-/* Check whether a task can use an open file descriptor to +- access an inode in a given way. Check access to the +- descriptor itself, and then use dentry_has_perm to +- check a particular permission to the file. +- Access to the descriptor is implicitly granted if it +- has the same SID as the process. If av is zero, then +- access to the file is not checked, e.g. for cases +- where only the descriptor is affected like seek. */ +-static int file_has_perm(const struct cred *cred, +- struct file *file, +- u32 av) ++static int __file_has_perm(const struct cred *cred, const struct file *file, ++ u32 av, bool bf_user_file) ++ + { +- struct file_security_struct *fsec = selinux_file(file); +- struct inode *inode = file_inode(file); + struct common_audit_data ad; +- u32 sid = cred_sid(cred); ++ struct inode *inode; ++ u32 ssid = cred_sid(cred); ++ u32 tsid_fd; + int rc; + +- ad.type = LSM_AUDIT_DATA_FILE; +- ad.u.file = file; ++ if (bf_user_file) { ++ struct backing_file_security_struct *bfsec; ++ const struct path *path; + +- if (sid != fsec->sid) { +- rc = avc_has_perm(sid, fsec->sid, +- SECCLASS_FD, +- FD__USE, +- &ad); ++ if (WARN_ON(!(file->f_mode & FMODE_BACKING))) ++ return -EIO; ++ ++ bfsec = selinux_backing_file(file); ++ path = backing_file_user_path(file); ++ tsid_fd = bfsec->uf_sid; ++ inode = d_inode(path->dentry); ++ ++ ad.type = LSM_AUDIT_DATA_PATH; ++ ad.u.path = *path; ++ } else { ++ struct file_security_struct *fsec = selinux_file(file); ++ ++ tsid_fd = fsec->sid; ++ inode = file_inode(file); ++ ++ ad.type = LSM_AUDIT_DATA_FILE; ++ ad.u.file = file; ++ } ++ ++ if (ssid != tsid_fd) { ++ rc = avc_has_perm(ssid, tsid_fd, SECCLASS_FD, FD__USE, &ad); + if (rc) +- goto out; ++ return rc; + } + + #ifdef CONFIG_BPF_SYSCALL +- rc = bpf_fd_pass(file, cred_sid(cred)); ++ /* regardless of backing vs user file, use the underlying file here */ ++ rc = bpf_fd_pass(file, ssid); + if (rc) + return rc; + #endif + + /* av is zero if only checking access to the descriptor. */ +- rc = 0; + if (av) +- rc = inode_has_perm(cred, inode, av, &ad); ++ return inode_has_perm(cred, inode, av, &ad); + +-out: +- return rc; ++ return 0; ++} ++ ++/* Check whether a task can use an open file descriptor to ++ access an inode in a given way. Check access to the ++ descriptor itself, and then use dentry_has_perm to ++ check a particular permission to the file. ++ Access to the descriptor is implicitly granted if it ++ has the same SID as the process. If av is zero, then ++ access to the file is not checked, e.g. for cases ++ where only the descriptor is affected like seek. */ ++static inline int file_has_perm(const struct cred *cred, ++ const struct file *file, u32 av) ++{ ++ return __file_has_perm(cred, file, av, false); + } + + /* +@@ -3656,6 +3679,17 @@ static int selinux_file_alloc_security(struct file *file) + return 0; + } + ++static int selinux_backing_file_alloc(struct file *backing_file, ++ const struct file *user_file) ++{ ++ struct backing_file_security_struct *bfsec; ++ ++ bfsec = selinux_backing_file(backing_file); ++ bfsec->uf_sid = selinux_file(user_file)->sid; ++ ++ return 0; ++} ++ + /* + * Check whether a task has the ioctl permission and cmd + * operation to an inode. +@@ -3773,42 +3807,55 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd, + + static int default_noexec __ro_after_init; + +-static int file_map_prot_check(struct file *file, unsigned long prot, int shared) ++static int __file_map_prot_check(const struct cred *cred, ++ const struct file *file, unsigned long prot, ++ bool shared, bool bf_user_file) + { +- const struct cred *cred = current_cred(); +- u32 sid = cred_sid(cred); +- int rc = 0; ++ struct inode *inode = NULL; ++ bool prot_exec = prot & PROT_EXEC; ++ bool prot_write = prot & PROT_WRITE; ++ ++ if (file) { ++ if (bf_user_file) ++ inode = d_inode(backing_file_user_path(file)->dentry); ++ else ++ inode = file_inode(file); ++ } ++ ++ if (default_noexec && prot_exec && ++ (!file || IS_PRIVATE(inode) || (!shared && prot_write))) { ++ int rc; ++ u32 sid = cred_sid(cred); + +- if (default_noexec && +- (prot & PROT_EXEC) && (!file || IS_PRIVATE(file_inode(file)) || +- (!shared && (prot & PROT_WRITE)))) { + /* +- * We are making executable an anonymous mapping or a +- * private file mapping that will also be writable. +- * This has an additional check. ++ * We are making executable an anonymous mapping or a private ++ * file mapping that will also be writable. + */ +- rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, +- PROCESS__EXECMEM, NULL); ++ rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM, ++ NULL); + if (rc) +- goto error; ++ return rc; + } + + if (file) { +- /* read access is always possible with a mapping */ ++ /* "read" always possible, "write" only if shared */ + u32 av = FILE__READ; +- +- /* write access only matters if the mapping is shared */ +- if (shared && (prot & PROT_WRITE)) ++ if (shared && prot_write) + av |= FILE__WRITE; +- +- if (prot & PROT_EXEC) ++ if (prot_exec) + av |= FILE__EXECUTE; + +- return file_has_perm(cred, file, av); ++ return __file_has_perm(cred, file, av, bf_user_file); + } + +-error: +- return rc; ++ return 0; ++} ++ ++static inline int file_map_prot_check(const struct cred *cred, ++ const struct file *file, ++ unsigned long prot, bool shared) ++{ ++ return __file_map_prot_check(cred, file, prot, shared, false); + } + + static int selinux_mmap_addr(unsigned long addr) +@@ -3824,36 +3871,80 @@ static int selinux_mmap_addr(unsigned long addr) + return rc; + } + +-static int selinux_mmap_file(struct file *file, +- unsigned long reqprot __always_unused, +- unsigned long prot, unsigned long flags) ++static int selinux_mmap_file_common(const struct cred *cred, struct file *file, ++ unsigned long prot, bool shared) + { +- struct common_audit_data ad; +- int rc; +- + if (file) { ++ int rc; ++ struct common_audit_data ad; ++ + ad.type = LSM_AUDIT_DATA_FILE; + ad.u.file = file; +- rc = inode_has_perm(current_cred(), file_inode(file), +- FILE__MAP, &ad); ++ rc = inode_has_perm(cred, file_inode(file), FILE__MAP, &ad); + if (rc) + return rc; + } + +- return file_map_prot_check(file, prot, +- (flags & MAP_TYPE) == MAP_SHARED); ++ return file_map_prot_check(cred, file, prot, shared); ++} ++ ++static int selinux_mmap_file(struct file *file, ++ unsigned long reqprot __always_unused, ++ unsigned long prot, unsigned long flags) ++{ ++ return selinux_mmap_file_common(current_cred(), file, prot, ++ (flags & MAP_TYPE) == MAP_SHARED); ++} ++ ++/** ++ * selinux_mmap_backing_file - Check mmap permissions on a backing file ++ * @vma: memory region ++ * @backing_file: stacked filesystem backing file ++ * @user_file: user visible file ++ * ++ * This is called after selinux_mmap_file() on stacked filesystems, and it ++ * is this function's responsibility to verify access to @backing_file and ++ * setup the SELinux state for possible later use in the mprotect() code path. ++ * ++ * By the time this function is called, mmap() access to @user_file has already ++ * been authorized and @vma->vm_file has been set to point to @backing_file. ++ * ++ * Return zero on success, negative values otherwise. ++ */ ++static int selinux_mmap_backing_file(struct vm_area_struct *vma, ++ struct file *backing_file, ++ struct file *user_file __always_unused) ++{ ++ unsigned long prot = 0; ++ ++ /* translate vma->vm_flags perms into PROT perms */ ++ if (vma->vm_flags & VM_READ) ++ prot |= PROT_READ; ++ if (vma->vm_flags & VM_WRITE) ++ prot |= PROT_WRITE; ++ if (vma->vm_flags & VM_EXEC) ++ prot |= PROT_EXEC; ++ ++ return selinux_mmap_file_common(backing_file->f_cred, backing_file, ++ prot, vma->vm_flags & VM_SHARED); + } + + static int selinux_file_mprotect(struct vm_area_struct *vma, + unsigned long reqprot __always_unused, + unsigned long prot) + { ++ int rc; + const struct cred *cred = current_cred(); + u32 sid = cred_sid(cred); ++ const struct file *file = vma->vm_file; ++ bool backing_file; ++ bool shared = vma->vm_flags & VM_SHARED; ++ ++ /* check if we need to trigger the "backing files are awful" mode */ ++ backing_file = file && (file->f_mode & FMODE_BACKING); + + if (default_noexec && + (prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) { +- int rc = 0; + /* + * We don't use the vma_is_initial_heap() helper as it has + * a history of problems and is currently broken on systems +@@ -3867,11 +3958,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, + vma->vm_end <= vma->vm_mm->brk) { + rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, + PROCESS__EXECHEAP, NULL); +- } else if (!vma->vm_file && (vma_is_initial_stack(vma) || ++ if (rc) ++ return rc; ++ } else if (!file && (vma_is_initial_stack(vma) || + vma_is_stack_for_current(vma))) { + rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, + PROCESS__EXECSTACK, NULL); +- } else if (vma->vm_file && vma->anon_vma) { ++ if (rc) ++ return rc; ++ } else if (file && vma->anon_vma) { + /* + * We are making executable a file mapping that has + * had some COW done. Since pages might have been +@@ -3879,13 +3974,29 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, + * modified content. This typically should only + * occur for text relocations. + */ +- rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD); ++ rc = __file_has_perm(cred, file, FILE__EXECMOD, ++ backing_file); ++ if (rc) ++ return rc; ++ if (backing_file) { ++ rc = file_has_perm(file->f_cred, file, ++ FILE__EXECMOD); ++ if (rc) ++ return rc; ++ } + } ++ } ++ ++ rc = __file_map_prot_check(cred, file, prot, shared, backing_file); ++ if (rc) ++ return rc; ++ if (backing_file) { ++ rc = file_map_prot_check(file->f_cred, file, prot, shared); + if (rc) + return rc; + } + +- return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED); ++ return 0; + } + + static int selinux_file_lock(struct file *file, unsigned int cmd) +@@ -6996,6 +7107,7 @@ static void selinux_bpf_token_free(struct bpf_token *token) + struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = { + .lbs_cred = sizeof(struct task_security_struct), + .lbs_file = sizeof(struct file_security_struct), ++ .lbs_backing_file = sizeof(struct backing_file_security_struct), + .lbs_inode = sizeof(struct inode_security_struct), + .lbs_ipc = sizeof(struct ipc_security_struct), + .lbs_key = sizeof(struct key_security_struct), +@@ -7201,9 +7313,11 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { + + LSM_HOOK_INIT(file_permission, selinux_file_permission), + LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security), ++ LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc), + LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl), + LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat), + LSM_HOOK_INIT(mmap_file, selinux_mmap_file), ++ LSM_HOOK_INIT(mmap_backing_file, selinux_mmap_backing_file), + LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr), + LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect), + LSM_HOOK_INIT(file_lock, selinux_file_lock), +diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h +index c88cae81ee4c..dc42282a2c05 100644 +--- a/security/selinux/include/objsec.h ++++ b/security/selinux/include/objsec.h +@@ -61,6 +61,10 @@ struct file_security_struct { + u32 pseqno; /* Policy seqno at the time of file open */ + }; + ++struct backing_file_security_struct { ++ u32 uf_sid; /* associated user file fsec->sid */ ++}; ++ + struct superblock_security_struct { + u32 sid; /* SID of file system superblock */ + u32 def_sid; /* default SID for labeling */ +@@ -159,6 +163,13 @@ static inline struct file_security_struct *selinux_file(const struct file *file) + return file->f_security + selinux_blob_sizes.lbs_file; + } + ++static inline struct backing_file_security_struct * ++selinux_backing_file(const struct file *backing_file) ++{ ++ void *blob = backing_file_security(backing_file); ++ return blob + selinux_blob_sizes.lbs_backing_file; ++} ++ + static inline struct inode_security_struct * + selinux_inode(const struct inode *inode) + { +-- +2.50.1 (Apple Git-155) + diff --git a/1324-scripts-sorttable-fix-orc-sort-cmp-to-maintain-symmetry-and-.patch b/1324-scripts-sorttable-fix-orc-sort-cmp-to-maintain-symmetry-and-.patch new file mode 100644 index 000000000..4b323b74e --- /dev/null +++ b/1324-scripts-sorttable-fix-orc-sort-cmp-to-maintain-symmetry-and-.patch @@ -0,0 +1,70 @@ +From 7a4465d25de718e57ced42030bc7194017ee4f64 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:33:16 +0200 +Subject: [PATCH] scripts/sorttable: fix orc_sort_cmp() to maintain symmetry + and transitivity + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 0210d251162f4033350a94a43f95b1c39ec84a90 +Author: Kuan-Wei Chiu +Date: Thu Dec 26 22:03:32 2024 +0800 + + scripts/sorttable: fix orc_sort_cmp() to maintain symmetry and transitivity + + The orc_sort_cmp() function, used with qsort(), previously violated the + symmetry and transitivity rules required by the C standard. Specifically, + when both entries are ORC_TYPE_UNDEFINED, it could result in both a < b + and b < a, which breaks the required symmetry and transitivity. This can + lead to undefined behavior and incorrect sorting results, potentially + causing memory corruption in glibc implementations [1]. + + Symmetry: If x < y, then y > x. + Transitivity: If x < y and y < z, then x < z. + + Fix the comparison logic to return 0 when both entries are + ORC_TYPE_UNDEFINED, ensuring compliance with qsort() requirements. + + Link: https://www.qualys.com/2024/01/30/qsort.txt [1] + Link: https://lkml.kernel.org/r/20241226140332.2670689-1-visitorckw@gmail.com + Fixes: 57fa18994285 ("scripts/sorttable: Implement build-time ORC unwind table sorting") + Fixes: fb799447ae29 ("x86,objtool: Split UNWIND_HINT_EMPTY in two") + Signed-off-by: Kuan-Wei Chiu + Cc: Ching-Chun (Jim) Huang + Cc: + Cc: Ingo Molnar + Cc: Josh Poimboeuf + Cc: Peter Zijlstra + Cc: Shile Zhang + Cc: Steven Rostedt + Cc: + Signed-off-by: Andrew Morton + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.h b/scripts/sorttable.h +index 7bd0184380d3..a7c5445baf00 100644 +--- a/scripts/sorttable.h ++++ b/scripts/sorttable.h +@@ -110,7 +110,7 @@ static inline unsigned long orc_ip(const int *ip) + + static int orc_sort_cmp(const void *_a, const void *_b) + { +- struct orc_entry *orc_a; ++ struct orc_entry *orc_a, *orc_b; + const int *a = g_orc_ip_table + *(int *)_a; + const int *b = g_orc_ip_table + *(int *)_b; + unsigned long a_val = orc_ip(a); +@@ -128,6 +128,9 @@ static int orc_sort_cmp(const void *_a, const void *_b) + * whitelisted .o files which didn't get objtool generation. + */ + orc_a = g_orc_table + (a - g_orc_ip_table); ++ orc_b = g_orc_table + (b - g_orc_ip_table); ++ if (orc_a->type == ORC_TYPE_UNDEFINED && orc_b->type == ORC_TYPE_UNDEFINED) ++ return 0; + return orc_a->type == ORC_TYPE_UNDEFINED ? -1 : 1; + } + +-- +2.50.1 (Apple Git-155) + diff --git a/1325-scripts-sorttable-remove-unused-macro-defines.patch b/1325-scripts-sorttable-remove-unused-macro-defines.patch new file mode 100644 index 000000000..eb19dc108 --- /dev/null +++ b/1325-scripts-sorttable-remove-unused-macro-defines.patch @@ -0,0 +1,104 @@ +From 90e4b4c6300522c4494664bc95285cb94e137b17 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:21:58 +0200 +Subject: [PATCH] scripts/sorttable: Remove unused macro defines + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 28b24394c6e9a3166fcb4480cba054562526657c +Author: Steven Rostedt +Date: Sun Jan 5 11:22:12 2025 -0500 + + scripts/sorttable: Remove unused macro defines + + The code of sorttable.h was copied from the recordmcount.h which defined + a bunch of Elf MACROs so that they could be used between 32bit and 64bit + functions. But there's several MACROs that sorttable.h does not use but + was copied over. Remove them to clean up the code. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Link: https://lore.kernel.org/20250105162344.128870118@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.h b/scripts/sorttable.h +index a7c5445baf00..14d0c4d843e8 100644 +--- a/scripts/sorttable.h ++++ b/scripts/sorttable.h +@@ -27,19 +27,10 @@ + #undef Elf_Ehdr + #undef Elf_Shdr + #undef Elf_Rel +-#undef Elf_Rela + #undef Elf_Sym +-#undef ELF_R_SYM +-#undef Elf_r_sym +-#undef ELF_R_INFO +-#undef Elf_r_info +-#undef ELF_ST_BIND + #undef ELF_ST_TYPE +-#undef fn_ELF_R_SYM +-#undef fn_ELF_R_INFO + #undef uint_t + #undef _r +-#undef _w + + #ifdef SORTTABLE_64 + # define extable_ent_size 16 +@@ -52,19 +43,10 @@ + # define Elf_Ehdr Elf64_Ehdr + # define Elf_Shdr Elf64_Shdr + # define Elf_Rel Elf64_Rel +-# define Elf_Rela Elf64_Rela + # define Elf_Sym Elf64_Sym +-# define ELF_R_SYM ELF64_R_SYM +-# define Elf_r_sym Elf64_r_sym +-# define ELF_R_INFO ELF64_R_INFO +-# define Elf_r_info Elf64_r_info +-# define ELF_ST_BIND ELF64_ST_BIND + # define ELF_ST_TYPE ELF64_ST_TYPE +-# define fn_ELF_R_SYM fn_ELF64_R_SYM +-# define fn_ELF_R_INFO fn_ELF64_R_INFO + # define uint_t uint64_t + # define _r r8 +-# define _w w8 + #else + # define extable_ent_size 8 + # define compare_extable compare_extable_32 +@@ -76,19 +58,10 @@ + # define Elf_Ehdr Elf32_Ehdr + # define Elf_Shdr Elf32_Shdr + # define Elf_Rel Elf32_Rel +-# define Elf_Rela Elf32_Rela + # define Elf_Sym Elf32_Sym +-# define ELF_R_SYM ELF32_R_SYM +-# define Elf_r_sym Elf32_r_sym +-# define ELF_R_INFO ELF32_R_INFO +-# define Elf_r_info Elf32_r_info +-# define ELF_ST_BIND ELF32_ST_BIND + # define ELF_ST_TYPE ELF32_ST_TYPE +-# define fn_ELF_R_SYM fn_ELF32_R_SYM +-# define fn_ELF_R_INFO fn_ELF32_R_INFO + # define uint_t uint32_t + # define _r r +-# define _w w + #endif + + #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) +-- +2.50.1 (Apple Git-155) + diff --git a/1326-scripts-sorttable-remove-unused-write-functions.patch b/1326-scripts-sorttable-remove-unused-write-functions.patch new file mode 100644 index 000000000..90cee9f42 --- /dev/null +++ b/1326-scripts-sorttable-remove-unused-write-functions.patch @@ -0,0 +1,102 @@ +From 3e1ec2703946dbf8d8f658865fc5c601e3a2cdea Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:22:02 +0200 +Subject: [PATCH] scripts/sorttable: Remove unused write functions + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 4f48a28b37d594dab38092514a42ae9f4b781553 +Author: Steven Rostedt +Date: Sun Jan 5 11:22:13 2025 -0500 + + scripts/sorttable: Remove unused write functions + + The code of sorttable.h was copied from the recordmcount.h which defined + various write functions for different sizes (2, 4, 8 byte lengths). But + sorttable only uses the 4 byte writes. Remove the extra versions as they + are not used. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Link: https://lore.kernel.org/20250105162344.314385504@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 83cdb843d92f..4dcdbf7a5e26 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -68,8 +68,6 @@ static uint32_t (*r)(const uint32_t *); + static uint16_t (*r2)(const uint16_t *); + static uint64_t (*r8)(const uint64_t *); + static void (*w)(uint32_t, uint32_t *); +-static void (*w2)(uint16_t, uint16_t *); +-static void (*w8)(uint64_t, uint64_t *); + typedef void (*table_sort_t)(char *, int); + + /* +@@ -146,31 +144,11 @@ static void wbe(uint32_t val, uint32_t *x) + put_unaligned_be32(val, x); + } + +-static void w2be(uint16_t val, uint16_t *x) +-{ +- put_unaligned_be16(val, x); +-} +- +-static void w8be(uint64_t val, uint64_t *x) +-{ +- put_unaligned_be64(val, x); +-} +- + static void wle(uint32_t val, uint32_t *x) + { + put_unaligned_le32(val, x); + } + +-static void w2le(uint16_t val, uint16_t *x) +-{ +- put_unaligned_le16(val, x); +-} +- +-static void w8le(uint64_t val, uint64_t *x) +-{ +- put_unaligned_le64(val, x); +-} +- + /* + * Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of + * the way to -256..-1, to avoid conflicting with real section +@@ -277,16 +255,12 @@ static int do_file(char const *const fname, void *addr) + r2 = r2le; + r8 = r8le; + w = wle; +- w2 = w2le; +- w8 = w8le; + break; + case ELFDATA2MSB: + r = rbe; + r2 = r2be; + r8 = r8be; + w = wbe; +- w2 = w2be; +- w8 = w8be; + break; + default: + fprintf(stderr, "unrecognized ELF data encoding %d: %s\n", +-- +2.50.1 (Apple Git-155) + diff --git a/1327-scripts-sorttable-remove-unneeded-elf-rel.patch b/1327-scripts-sorttable-remove-unneeded-elf-rel.patch new file mode 100644 index 000000000..2c8e7c604 --- /dev/null +++ b/1327-scripts-sorttable-remove-unneeded-elf-rel.patch @@ -0,0 +1,118 @@ +From b0006591a4fb4d08bab55e02d904a225bb97f803 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:22:06 +0200 +Subject: [PATCH] scripts/sorttable: Remove unneeded Elf_Rel + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 6f2c2f93a190467cebd6ebd03feb49514fead5ca +Author: Steven Rostedt +Date: Sun Jan 5 11:22:14 2025 -0500 + + scripts/sorttable: Remove unneeded Elf_Rel + + The code had references to initialize the Elf_Rel relocation tables, but + it was never used. Remove it. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Link: https://lore.kernel.org/20250105162344.515342233@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.h b/scripts/sorttable.h +index 14d0c4d843e8..18d07fdb2716 100644 +--- a/scripts/sorttable.h ++++ b/scripts/sorttable.h +@@ -26,7 +26,6 @@ + #undef Elf_Addr + #undef Elf_Ehdr + #undef Elf_Shdr +-#undef Elf_Rel + #undef Elf_Sym + #undef ELF_ST_TYPE + #undef uint_t +@@ -42,7 +41,6 @@ + # define Elf_Addr Elf64_Addr + # define Elf_Ehdr Elf64_Ehdr + # define Elf_Shdr Elf64_Shdr +-# define Elf_Rel Elf64_Rel + # define Elf_Sym Elf64_Sym + # define ELF_ST_TYPE ELF64_ST_TYPE + # define uint_t uint64_t +@@ -57,7 +55,6 @@ + # define Elf_Addr Elf32_Addr + # define Elf_Ehdr Elf32_Ehdr + # define Elf_Shdr Elf32_Shdr +-# define Elf_Rel Elf32_Rel + # define Elf_Sym Elf32_Sym + # define ELF_ST_TYPE ELF32_ST_TYPE + # define uint_t uint32_t +@@ -248,14 +245,10 @@ static int do_sort(Elf_Ehdr *ehdr, + Elf32_Word *symtab_shndx = NULL; + Elf_Sym *sort_needed_sym = NULL; + Elf_Shdr *sort_needed_sec; +- Elf_Rel *relocs = NULL; +- int relocs_size = 0; + uint32_t *sort_needed_loc; + const char *secstrings; + const char *strtab; + char *extab_image; +- int extab_index = 0; +- int i; + int idx; + unsigned int shnum; + unsigned int shstrndx; +@@ -279,23 +272,15 @@ static int do_sort(Elf_Ehdr *ehdr, + if (shnum == SHN_UNDEF) + shnum = _r(&shdr[0].sh_size); + +- for (i = 0, s = shdr; s < shdr + shnum; i++, s++) { ++ for (s = shdr; s < shdr + shnum; s++) { + idx = r(&s->sh_name); +- if (!strcmp(secstrings + idx, "__ex_table")) { ++ if (!strcmp(secstrings + idx, "__ex_table")) + extab_sec = s; +- extab_index = i; +- } + if (!strcmp(secstrings + idx, ".symtab")) + symtab_sec = s; + if (!strcmp(secstrings + idx, ".strtab")) + strtab_sec = s; + +- if ((r(&s->sh_type) == SHT_REL || +- r(&s->sh_type) == SHT_RELA) && +- r(&s->sh_info) == extab_index) { +- relocs = (void *)ehdr + _r(&s->sh_offset); +- relocs_size = _r(&s->sh_size); +- } + if (r(&s->sh_type) == SHT_SYMTAB_SHNDX) + symtab_shndx = (Elf32_Word *)((const char *)ehdr + + _r(&s->sh_offset)); +@@ -397,10 +382,6 @@ static int do_sort(Elf_Ehdr *ehdr, + extable_ent_size, compare_extable); + } + +- /* If there were relocations, we no longer need them. */ +- if (relocs) +- memset(relocs, 0, relocs_size); +- + /* find the flag main_extable_sort_needed */ + for (sym = (void *)ehdr + _r(&symtab_sec->sh_offset); + sym < sym + _r(&symtab_sec->sh_size) / sizeof(Elf_Sym); +-- +2.50.1 (Apple Git-155) + diff --git a/1328-scripts-sorttable-have-the-orc-code-use-the-r-functions-to-r.patch b/1328-scripts-sorttable-have-the-orc-code-use-the-r-functions-to-r.patch new file mode 100644 index 000000000..9055c2e6f --- /dev/null +++ b/1328-scripts-sorttable-have-the-orc-code-use-the-r-functions-to-r.patch @@ -0,0 +1,65 @@ +From 8b98d95b2f3df8cdec1154dcce05f292bdde59d0 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:22:10 +0200 +Subject: [PATCH] scripts/sorttable: Have the ORC code use the _r() functions + to read + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 66990c003306c240d570b3ba274ec4f68cf18c91 +Author: Steven Rostedt +Date: Sun Jan 5 11:22:15 2025 -0500 + + scripts/sorttable: Have the ORC code use the _r() functions to read + + The ORC code reads the section information directly from the file. This + currently works because the default read function is for 64bit little + endian machines. But if for some reason that ever changes, this will + break. Instead of having a surprise breakage, use the _r() functions that + will read the values from the file properly. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Link: https://lore.kernel.org/20250105162344.721480386@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.h b/scripts/sorttable.h +index 18d07fdb2716..58f7ab5f5644 100644 +--- a/scripts/sorttable.h ++++ b/scripts/sorttable.h +@@ -299,14 +299,14 @@ static int do_sort(Elf_Ehdr *ehdr, + #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) + /* locate the ORC unwind tables */ + if (!strcmp(secstrings + idx, ".orc_unwind_ip")) { +- orc_ip_size = s->sh_size; ++ orc_ip_size = _r(&s->sh_size); + g_orc_ip_table = (int *)((void *)ehdr + +- s->sh_offset); ++ _r(&s->sh_offset)); + } + if (!strcmp(secstrings + idx, ".orc_unwind")) { +- orc_size = s->sh_size; ++ orc_size = _r(&s->sh_size); + g_orc_table = (struct orc_entry *)((void *)ehdr + +- s->sh_offset); ++ _r(&s->sh_offset)); + } + #endif + } /* for loop */ +-- +2.50.1 (Apple Git-155) + diff --git a/1329-scripts-sorttable-make-compare-extable-into-two-functions.patch b/1329-scripts-sorttable-make-compare-extable-into-two-functions.patch new file mode 100644 index 000000000..05ae6405a --- /dev/null +++ b/1329-scripts-sorttable-make-compare-extable-into-two-functions.patch @@ -0,0 +1,132 @@ +From 3f3c4e53bd362a76e3a097c1dac3c8975cb99926 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:22:13 +0200 +Subject: [PATCH] scripts/sorttable: Make compare_extable() into two functions + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 7ffc0d0819f438779ed592e2e2e3576f43ce14f0 +Author: Steven Rostedt +Date: Sun Jan 5 11:22:16 2025 -0500 + + scripts/sorttable: Make compare_extable() into two functions + + Instead of having the compare_extable() part of the sorttable.h header + where it get's defined twice, since it is a very simple function, just + define it twice in sorttable.c, and then it can use the proper read + functions for the word size and endianess and the Elf_Addr macro can be + removed from sorttable.h. + + Also add a micro optimization. Instead of: + + if (a < b) + return -1; + if (a > b) + return 1; + return 0; + + That can be shorten to: + + if (a < b) + return -1; + return a > b; + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Link: https://lore.kernel.org/20250105162344.945299671@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 4dcdbf7a5e26..3e2c17e91485 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -173,6 +173,26 @@ static inline unsigned int get_secindex(unsigned int shndx, + return r(&symtab_shndx_start[sym_offs]); + } + ++static int compare_extable_32(const void *a, const void *b) ++{ ++ Elf32_Addr av = r(a); ++ Elf32_Addr bv = r(b); ++ ++ if (av < bv) ++ return -1; ++ return av > bv; ++} ++ ++static int compare_extable_64(const void *a, const void *b) ++{ ++ Elf64_Addr av = r8(a); ++ Elf64_Addr bv = r8(b); ++ ++ if (av < bv) ++ return -1; ++ return av > bv; ++} ++ + /* 32 bit and 64 bit are very similar */ + #include "sorttable.h" + #define SORTTABLE_64 +diff --git a/scripts/sorttable.h b/scripts/sorttable.h +index 58f7ab5f5644..36655ff16b39 100644 +--- a/scripts/sorttable.h ++++ b/scripts/sorttable.h +@@ -23,7 +23,6 @@ + #undef sort_mcount_loc + #undef elf_mcount_loc + #undef do_sort +-#undef Elf_Addr + #undef Elf_Ehdr + #undef Elf_Shdr + #undef Elf_Sym +@@ -38,7 +37,6 @@ + # define sort_mcount_loc sort_mcount_loc_64 + # define elf_mcount_loc elf_mcount_loc_64 + # define do_sort do_sort_64 +-# define Elf_Addr Elf64_Addr + # define Elf_Ehdr Elf64_Ehdr + # define Elf_Shdr Elf64_Shdr + # define Elf_Sym Elf64_Sym +@@ -52,7 +50,6 @@ + # define sort_mcount_loc sort_mcount_loc_32 + # define elf_mcount_loc elf_mcount_loc_32 + # define do_sort do_sort_32 +-# define Elf_Addr Elf32_Addr + # define Elf_Ehdr Elf32_Ehdr + # define Elf_Shdr Elf32_Shdr + # define Elf_Sym Elf32_Sym +@@ -160,17 +157,6 @@ static void *sort_orctable(void *arg) + } + #endif + +-static int compare_extable(const void *a, const void *b) +-{ +- Elf_Addr av = _r(a); +- Elf_Addr bv = _r(b); +- +- if (av < bv) +- return -1; +- if (av > bv) +- return 1; +- return 0; +-} + #ifdef MCOUNT_SORT_ENABLED + pthread_t mcount_sort_thread; + +-- +2.50.1 (Apple Git-155) + diff --git a/1330-scripts-sorttable-convert-elf-ehdr-to-union.patch b/1330-scripts-sorttable-convert-elf-ehdr-to-union.patch new file mode 100644 index 000000000..847f24707 --- /dev/null +++ b/1330-scripts-sorttable-convert-elf-ehdr-to-union.patch @@ -0,0 +1,212 @@ +From a9cb56b10f496e1e39543627f47eecda2d65213f Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:22:16 +0200 +Subject: [PATCH] scripts/sorttable: Convert Elf_Ehdr to union + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 157fb5b3cfd2cb5950314f926a76e567fc1921c5 +Author: Steven Rostedt +Date: Sun Jan 5 11:22:17 2025 -0500 + + scripts/sorttable: Convert Elf_Ehdr to union + + In order to remove the double #include of sorttable.h for 64 and 32 bit + to create duplicate functions for both, replace the Elf_Ehdr macro with a + union that defines both Elf64_Ehdr and Elf32_Ehdr, with field e64 for the + 64bit version, and e32 for the 32bit version. + + Then a macro etype can be used instead to get to the proper value. + + This will eventually be replaced with just single functions that can + handle both 32bit and 64bit ELF parsing. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Link: https://lore.kernel.org/20250105162345.148224465@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 3e2c17e91485..67cbbfc8214d 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -64,6 +64,11 @@ + #define EM_LOONGARCH 258 + #endif + ++typedef union { ++ Elf32_Ehdr e32; ++ Elf64_Ehdr e64; ++} Elf_Ehdr; ++ + static uint32_t (*r)(const uint32_t *); + static uint16_t (*r2)(const uint16_t *); + static uint64_t (*r8)(const uint64_t *); +@@ -266,10 +271,10 @@ static void sort_relative_table_with_data(char *extab_image, int image_size) + static int do_file(char const *const fname, void *addr) + { + int rc = -1; +- Elf32_Ehdr *ehdr = addr; ++ Elf_Ehdr *ehdr = addr; + table_sort_t custom_sort = NULL; + +- switch (ehdr->e_ident[EI_DATA]) { ++ switch (ehdr->e32.e_ident[EI_DATA]) { + case ELFDATA2LSB: + r = rle; + r2 = r2le; +@@ -284,18 +289,18 @@ static int do_file(char const *const fname, void *addr) + break; + default: + fprintf(stderr, "unrecognized ELF data encoding %d: %s\n", +- ehdr->e_ident[EI_DATA], fname); ++ ehdr->e32.e_ident[EI_DATA], fname); + return -1; + } + +- if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0 || +- (r2(&ehdr->e_type) != ET_EXEC && r2(&ehdr->e_type) != ET_DYN) || +- ehdr->e_ident[EI_VERSION] != EV_CURRENT) { ++ if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 || ++ (r2(&ehdr->e32.e_type) != ET_EXEC && r2(&ehdr->e32.e_type) != ET_DYN) || ++ ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) { + fprintf(stderr, "unrecognized ET_EXEC/ET_DYN file %s\n", fname); + return -1; + } + +- switch (r2(&ehdr->e_machine)) { ++ switch (r2(&ehdr->e32.e_machine)) { + case EM_386: + case EM_AARCH64: + case EM_LOONGARCH: +@@ -318,14 +323,14 @@ static int do_file(char const *const fname, void *addr) + break; + default: + fprintf(stderr, "unrecognized e_machine %d %s\n", +- r2(&ehdr->e_machine), fname); ++ r2(&ehdr->e32.e_machine), fname); + return -1; + } + +- switch (ehdr->e_ident[EI_CLASS]) { ++ switch (ehdr->e32.e_ident[EI_CLASS]) { + case ELFCLASS32: +- if (r2(&ehdr->e_ehsize) != sizeof(Elf32_Ehdr) || +- r2(&ehdr->e_shentsize) != sizeof(Elf32_Shdr)) { ++ if (r2(&ehdr->e32.e_ehsize) != sizeof(Elf32_Ehdr) || ++ r2(&ehdr->e32.e_shentsize) != sizeof(Elf32_Shdr)) { + fprintf(stderr, + "unrecognized ET_EXEC/ET_DYN file: %s\n", fname); + break; +@@ -334,20 +339,19 @@ static int do_file(char const *const fname, void *addr) + break; + case ELFCLASS64: + { +- Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr; +- if (r2(&ghdr->e_ehsize) != sizeof(Elf64_Ehdr) || +- r2(&ghdr->e_shentsize) != sizeof(Elf64_Shdr)) { ++ if (r2(&ehdr->e64.e_ehsize) != sizeof(Elf64_Ehdr) || ++ r2(&ehdr->e64.e_shentsize) != sizeof(Elf64_Shdr)) { + fprintf(stderr, + "unrecognized ET_EXEC/ET_DYN file: %s\n", + fname); + break; + } +- rc = do_sort_64(ghdr, fname, custom_sort); ++ rc = do_sort_64(ehdr, fname, custom_sort); + } + break; + default: + fprintf(stderr, "unrecognized ELF class %d %s\n", +- ehdr->e_ident[EI_CLASS], fname); ++ ehdr->e32.e_ident[EI_CLASS], fname); + break; + } + +diff --git a/scripts/sorttable.h b/scripts/sorttable.h +index 36655ff16b39..be8b529498fb 100644 +--- a/scripts/sorttable.h ++++ b/scripts/sorttable.h +@@ -23,12 +23,12 @@ + #undef sort_mcount_loc + #undef elf_mcount_loc + #undef do_sort +-#undef Elf_Ehdr + #undef Elf_Shdr + #undef Elf_Sym + #undef ELF_ST_TYPE + #undef uint_t + #undef _r ++#undef etype + + #ifdef SORTTABLE_64 + # define extable_ent_size 16 +@@ -37,12 +37,12 @@ + # define sort_mcount_loc sort_mcount_loc_64 + # define elf_mcount_loc elf_mcount_loc_64 + # define do_sort do_sort_64 +-# define Elf_Ehdr Elf64_Ehdr + # define Elf_Shdr Elf64_Shdr + # define Elf_Sym Elf64_Sym + # define ELF_ST_TYPE ELF64_ST_TYPE + # define uint_t uint64_t + # define _r r8 ++# define etype e64 + #else + # define extable_ent_size 8 + # define compare_extable compare_extable_32 +@@ -50,12 +50,12 @@ + # define sort_mcount_loc sort_mcount_loc_32 + # define elf_mcount_loc elf_mcount_loc_32 + # define do_sort do_sort_32 +-# define Elf_Ehdr Elf32_Ehdr + # define Elf_Shdr Elf32_Shdr + # define Elf_Sym Elf32_Sym + # define ELF_ST_TYPE ELF32_ST_TYPE + # define uint_t uint32_t + # define _r r ++# define etype e32 + #endif + + #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) +@@ -222,7 +222,7 @@ static int do_sort(Elf_Ehdr *ehdr, + table_sort_t custom_sort) + { + int rc = -1; +- Elf_Shdr *s, *shdr = (Elf_Shdr *)((char *)ehdr + _r(&ehdr->e_shoff)); ++ Elf_Shdr *s, *shdr = (Elf_Shdr *)((char *)ehdr + _r(&ehdr->etype.e_shoff)); + Elf_Shdr *strtab_sec = NULL; + Elf_Shdr *symtab_sec = NULL; + Elf_Shdr *extab_sec = NULL; +@@ -249,12 +249,12 @@ static int do_sort(Elf_Ehdr *ehdr, + unsigned int orc_num_entries = 0; + #endif + +- shstrndx = r2(&ehdr->e_shstrndx); ++ shstrndx = r2(&ehdr->etype.e_shstrndx); + if (shstrndx == SHN_XINDEX) + shstrndx = r(&shdr[0].sh_link); + secstrings = (const char *)ehdr + _r(&shdr[shstrndx].sh_offset); + +- shnum = r2(&ehdr->e_shnum); ++ shnum = r2(&ehdr->etype.e_shnum); + if (shnum == SHN_UNDEF) + shnum = _r(&shdr[0].sh_size); + +-- +2.50.1 (Apple Git-155) + diff --git a/1331-scripts-sorttable-replace-elf-shdr-macro-with-a-union.patch b/1331-scripts-sorttable-replace-elf-shdr-macro-with-a-union.patch new file mode 100644 index 000000000..6fc73c9f3 --- /dev/null +++ b/1331-scripts-sorttable-replace-elf-shdr-macro-with-a-union.patch @@ -0,0 +1,257 @@ +From e5bf3a68ca49663c4c12c03dbbb236a86ae7ef45 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:22:20 +0200 +Subject: [PATCH] scripts/sorttable: Replace Elf_Shdr Macro with a union + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 545f6cf8f4c9a268e0bab2637f1d279679befdbf +Author: Steven Rostedt +Date: Sun Jan 5 11:22:18 2025 -0500 + + scripts/sorttable: Replace Elf_Shdr Macro with a union + + In order to remove the double #include of sorttable.h for 64 and 32 bit + to create duplicate functions for both, replace the Elf_Shdr macro with a + union that defines both Elf64_Shdr and Elf32_Shdr, with field e64 for the + 64bit version, and e32 for the 32bit version. + + It can then use the macro etype to get the proper value. + + This will eventually be replaced with just single functions that can + handle both 32bit and 64bit ELF parsing. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Link: https://lore.kernel.org/20250105162345.339462681@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 67cbbfc8214d..94497b8ab04c 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -69,6 +69,11 @@ typedef union { + Elf64_Ehdr e64; + } Elf_Ehdr; + ++typedef union { ++ Elf32_Shdr e32; ++ Elf64_Shdr e64; ++} Elf_Shdr; ++ + static uint32_t (*r)(const uint32_t *); + static uint16_t (*r2)(const uint16_t *); + static uint64_t (*r8)(const uint64_t *); +@@ -198,6 +203,11 @@ static int compare_extable_64(const void *a, const void *b) + return av > bv; + } + ++static inline void *get_index(void *start, int entsize, int index) ++{ ++ return start + (entsize * index); ++} ++ + /* 32 bit and 64 bit are very similar */ + #include "sorttable.h" + #define SORTTABLE_64 +diff --git a/scripts/sorttable.h b/scripts/sorttable.h +index be8b529498fb..3daf37bb6b9a 100644 +--- a/scripts/sorttable.h ++++ b/scripts/sorttable.h +@@ -23,7 +23,6 @@ + #undef sort_mcount_loc + #undef elf_mcount_loc + #undef do_sort +-#undef Elf_Shdr + #undef Elf_Sym + #undef ELF_ST_TYPE + #undef uint_t +@@ -37,7 +36,6 @@ + # define sort_mcount_loc sort_mcount_loc_64 + # define elf_mcount_loc elf_mcount_loc_64 + # define do_sort do_sort_64 +-# define Elf_Shdr Elf64_Shdr + # define Elf_Sym Elf64_Sym + # define ELF_ST_TYPE ELF64_ST_TYPE + # define uint_t uint64_t +@@ -50,7 +48,6 @@ + # define sort_mcount_loc sort_mcount_loc_32 + # define elf_mcount_loc elf_mcount_loc_32 + # define do_sort do_sort_32 +-# define Elf_Shdr Elf32_Shdr + # define Elf_Sym Elf32_Sym + # define ELF_ST_TYPE ELF32_ST_TYPE + # define uint_t uint32_t +@@ -171,8 +168,8 @@ struct elf_mcount_loc { + static void *sort_mcount_loc(void *arg) + { + struct elf_mcount_loc *emloc = (struct elf_mcount_loc *)arg; +- uint_t offset = emloc->start_mcount_loc - _r(&(emloc->init_data_sec)->sh_addr) +- + _r(&(emloc->init_data_sec)->sh_offset); ++ uint_t offset = emloc->start_mcount_loc - _r(&(emloc->init_data_sec)->etype.sh_addr) ++ + _r(&(emloc->init_data_sec)->etype.sh_offset); + uint_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc; + unsigned char *start_loc = (void *)emloc->ehdr + offset; + +@@ -222,10 +219,11 @@ static int do_sort(Elf_Ehdr *ehdr, + table_sort_t custom_sort) + { + int rc = -1; +- Elf_Shdr *s, *shdr = (Elf_Shdr *)((char *)ehdr + _r(&ehdr->etype.e_shoff)); ++ Elf_Shdr *shdr_start; + Elf_Shdr *strtab_sec = NULL; + Elf_Shdr *symtab_sec = NULL; + Elf_Shdr *extab_sec = NULL; ++ Elf_Shdr *string_sec; + Elf_Sym *sym; + const Elf_Sym *symtab; + Elf32_Word *symtab_shndx = NULL; +@@ -235,7 +233,10 @@ static int do_sort(Elf_Ehdr *ehdr, + const char *secstrings; + const char *strtab; + char *extab_image; ++ int sort_need_index; ++ int shentsize; + int idx; ++ int i; + unsigned int shnum; + unsigned int shstrndx; + #ifdef MCOUNT_SORT_ENABLED +@@ -249,34 +250,40 @@ static int do_sort(Elf_Ehdr *ehdr, + unsigned int orc_num_entries = 0; + #endif + ++ shdr_start = (Elf_Shdr *)((char *)ehdr + _r(&ehdr->etype.e_shoff)); ++ shentsize = r2(&ehdr->etype.e_shentsize); ++ + shstrndx = r2(&ehdr->etype.e_shstrndx); + if (shstrndx == SHN_XINDEX) +- shstrndx = r(&shdr[0].sh_link); +- secstrings = (const char *)ehdr + _r(&shdr[shstrndx].sh_offset); ++ shstrndx = r(&shdr_start->etype.sh_link); ++ string_sec = get_index(shdr_start, shentsize, shstrndx); ++ secstrings = (const char *)ehdr + _r(&string_sec->etype.sh_offset); + + shnum = r2(&ehdr->etype.e_shnum); + if (shnum == SHN_UNDEF) +- shnum = _r(&shdr[0].sh_size); ++ shnum = _r(&shdr_start->etype.sh_size); ++ ++ for (i = 0; i < shnum; i++) { ++ Elf_Shdr *shdr = get_index(shdr_start, shentsize, i); + +- for (s = shdr; s < shdr + shnum; s++) { +- idx = r(&s->sh_name); ++ idx = r(&shdr->etype.sh_name); + if (!strcmp(secstrings + idx, "__ex_table")) +- extab_sec = s; ++ extab_sec = shdr; + if (!strcmp(secstrings + idx, ".symtab")) +- symtab_sec = s; ++ symtab_sec = shdr; + if (!strcmp(secstrings + idx, ".strtab")) +- strtab_sec = s; ++ strtab_sec = shdr; + +- if (r(&s->sh_type) == SHT_SYMTAB_SHNDX) ++ if (r(&shdr->etype.sh_type) == SHT_SYMTAB_SHNDX) + symtab_shndx = (Elf32_Word *)((const char *)ehdr + +- _r(&s->sh_offset)); ++ _r(&shdr->etype.sh_offset)); + + #ifdef MCOUNT_SORT_ENABLED + /* locate the .init.data section in vmlinux */ + if (!strcmp(secstrings + idx, ".init.data")) { + get_mcount_loc(&_start_mcount_loc, &_stop_mcount_loc); + mstruct.ehdr = ehdr; +- mstruct.init_data_sec = s; ++ mstruct.init_data_sec = shdr; + mstruct.start_mcount_loc = _start_mcount_loc; + mstruct.stop_mcount_loc = _stop_mcount_loc; + } +@@ -285,14 +292,14 @@ static int do_sort(Elf_Ehdr *ehdr, + #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) + /* locate the ORC unwind tables */ + if (!strcmp(secstrings + idx, ".orc_unwind_ip")) { +- orc_ip_size = _r(&s->sh_size); ++ orc_ip_size = _r(&shdr->etype.sh_size); + g_orc_ip_table = (int *)((void *)ehdr + +- _r(&s->sh_offset)); ++ _r(&shdr->etype.sh_offset)); + } + if (!strcmp(secstrings + idx, ".orc_unwind")) { +- orc_size = _r(&s->sh_size); ++ orc_size = _r(&shdr->etype.sh_size); + g_orc_table = (struct orc_entry *)((void *)ehdr + +- _r(&s->sh_offset)); ++ _r(&shdr->etype.sh_offset)); + } + #endif + } /* for loop */ +@@ -355,22 +362,22 @@ static int do_sort(Elf_Ehdr *ehdr, + goto out; + } + +- extab_image = (void *)ehdr + _r(&extab_sec->sh_offset); +- strtab = (const char *)ehdr + _r(&strtab_sec->sh_offset); ++ extab_image = (void *)ehdr + _r(&extab_sec->etype.sh_offset); ++ strtab = (const char *)ehdr + _r(&strtab_sec->etype.sh_offset); + symtab = (const Elf_Sym *)((const char *)ehdr + +- _r(&symtab_sec->sh_offset)); ++ _r(&symtab_sec->etype.sh_offset)); + + if (custom_sort) { +- custom_sort(extab_image, _r(&extab_sec->sh_size)); ++ custom_sort(extab_image, _r(&extab_sec->etype.sh_size)); + } else { +- int num_entries = _r(&extab_sec->sh_size) / extable_ent_size; ++ int num_entries = _r(&extab_sec->etype.sh_size) / extable_ent_size; + qsort(extab_image, num_entries, + extable_ent_size, compare_extable); + } + + /* find the flag main_extable_sort_needed */ +- for (sym = (void *)ehdr + _r(&symtab_sec->sh_offset); +- sym < sym + _r(&symtab_sec->sh_size) / sizeof(Elf_Sym); ++ for (sym = (void *)ehdr + _r(&symtab_sec->etype.sh_offset); ++ sym < sym + _r(&symtab_sec->etype.sh_size) / sizeof(Elf_Sym); + sym++) { + if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT) + continue; +@@ -388,13 +395,14 @@ static int do_sort(Elf_Ehdr *ehdr, + goto out; + } + +- sort_needed_sec = &shdr[get_secindex(r2(&sym->st_shndx), +- sort_needed_sym - symtab, +- symtab_shndx)]; ++ sort_need_index = get_secindex(r2(&sym->st_shndx), ++ sort_needed_sym - symtab, ++ symtab_shndx); ++ sort_needed_sec = get_index(shdr_start, shentsize, sort_need_index); + sort_needed_loc = (void *)ehdr + +- _r(&sort_needed_sec->sh_offset) + ++ _r(&sort_needed_sec->etype.sh_offset) + + _r(&sort_needed_sym->st_value) - +- _r(&sort_needed_sec->sh_addr); ++ _r(&sort_needed_sec->etype.sh_addr); + + /* extable has been sorted, clear the flag */ + w(0, sort_needed_loc); +-- +2.50.1 (Apple Git-155) + diff --git a/1332-scripts-sorttable-convert-elf-sym-macro-over-to-a-union.patch b/1332-scripts-sorttable-convert-elf-sym-macro-over-to-a-union.patch new file mode 100644 index 000000000..8ed4ac56f --- /dev/null +++ b/1332-scripts-sorttable-convert-elf-sym-macro-over-to-a-union.patch @@ -0,0 +1,141 @@ +From d95dcd72b2a2511fa93a2b185611cba7f4624f7c Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:22:23 +0200 +Subject: [PATCH] scripts/sorttable: Convert Elf_Sym MACRO over to a union + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 200d015e73b4da69bcd8212a7c58695452b12bad +Author: Steven Rostedt +Date: Sun Jan 5 11:22:19 2025 -0500 + + scripts/sorttable: Convert Elf_Sym MACRO over to a union + + In order to remove the double #include of sorttable.h for 64 and 32 bit + to create duplicate functions for both, replace the Elf_Sym macro with a + union that defines both Elf64_Sym and Elf32_Sym, with field e64 for the + 64bit version, and e32 for the 32bit version. + + It can then use the macro etype to get the proper value. + + This will eventually be replaced with just single functions that can + handle both 32bit and 64bit ELF parsing. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Link: https://lore.kernel.org/20250105162345.528626969@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 94497b8ab04c..57792cf2aa89 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -74,6 +74,11 @@ typedef union { + Elf64_Shdr e64; + } Elf_Shdr; + ++typedef union { ++ Elf32_Sym e32; ++ Elf64_Sym e64; ++} Elf_Sym; ++ + static uint32_t (*r)(const uint32_t *); + static uint16_t (*r2)(const uint16_t *); + static uint64_t (*r8)(const uint64_t *); +diff --git a/scripts/sorttable.h b/scripts/sorttable.h +index 3daf37bb6b9a..cd4429c8a9f4 100644 +--- a/scripts/sorttable.h ++++ b/scripts/sorttable.h +@@ -23,7 +23,6 @@ + #undef sort_mcount_loc + #undef elf_mcount_loc + #undef do_sort +-#undef Elf_Sym + #undef ELF_ST_TYPE + #undef uint_t + #undef _r +@@ -36,7 +35,6 @@ + # define sort_mcount_loc sort_mcount_loc_64 + # define elf_mcount_loc elf_mcount_loc_64 + # define do_sort do_sort_64 +-# define Elf_Sym Elf64_Sym + # define ELF_ST_TYPE ELF64_ST_TYPE + # define uint_t uint64_t + # define _r r8 +@@ -48,7 +46,6 @@ + # define sort_mcount_loc sort_mcount_loc_32 + # define elf_mcount_loc elf_mcount_loc_32 + # define do_sort do_sort_32 +-# define Elf_Sym Elf32_Sym + # define ELF_ST_TYPE ELF32_ST_TYPE + # define uint_t uint32_t + # define _r r +@@ -230,10 +227,13 @@ static int do_sort(Elf_Ehdr *ehdr, + Elf_Sym *sort_needed_sym = NULL; + Elf_Shdr *sort_needed_sec; + uint32_t *sort_needed_loc; ++ void *sym_start; ++ void *sym_end; + const char *secstrings; + const char *strtab; + char *extab_image; + int sort_need_index; ++ int symentsize; + int shentsize; + int idx; + int i; +@@ -376,12 +376,15 @@ static int do_sort(Elf_Ehdr *ehdr, + } + + /* find the flag main_extable_sort_needed */ +- for (sym = (void *)ehdr + _r(&symtab_sec->etype.sh_offset); +- sym < sym + _r(&symtab_sec->etype.sh_size) / sizeof(Elf_Sym); +- sym++) { +- if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT) ++ sym_start = (void *)ehdr + _r(&symtab_sec->etype.sh_offset); ++ sym_end = sym_start + _r(&symtab_sec->etype.sh_size); ++ symentsize = _r(&symtab_sec->etype.sh_entsize); ++ ++ for (sym = sym_start; (void *)sym + symentsize < sym_end; ++ sym = (void *)sym + symentsize) { ++ if (ELF_ST_TYPE(sym->etype.st_info) != STT_OBJECT) + continue; +- if (!strcmp(strtab + r(&sym->st_name), ++ if (!strcmp(strtab + r(&sym->etype.st_name), + "main_extable_sort_needed")) { + sort_needed_sym = sym; + break; +@@ -395,13 +398,13 @@ static int do_sort(Elf_Ehdr *ehdr, + goto out; + } + +- sort_need_index = get_secindex(r2(&sym->st_shndx), +- sort_needed_sym - symtab, ++ sort_need_index = get_secindex(r2(&sym->etype.st_shndx), ++ ((void *)sort_needed_sym - (void *)symtab) / symentsize, + symtab_shndx); + sort_needed_sec = get_index(shdr_start, shentsize, sort_need_index); + sort_needed_loc = (void *)ehdr + + _r(&sort_needed_sec->etype.sh_offset) + +- _r(&sort_needed_sym->st_value) - ++ _r(&sort_needed_sym->etype.st_value) - + _r(&sort_needed_sec->etype.sh_addr); + + /* extable has been sorted, clear the flag */ +-- +2.50.1 (Apple Git-155) + diff --git a/1333-scripts-sorttable-add-helper-functions-for-elf-ehdr.patch b/1333-scripts-sorttable-add-helper-functions-for-elf-ehdr.patch new file mode 100644 index 000000000..6086840a7 --- /dev/null +++ b/1333-scripts-sorttable-add-helper-functions-for-elf-ehdr.patch @@ -0,0 +1,139 @@ +From 1278ee787a87aaaa378d44f173cdae2fb07501ae Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:22:33 +0200 +Subject: [PATCH] scripts/sorttable: Add helper functions for Elf_Ehdr + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 1dfb59a228dde59ad7d99b2fa2104e90004995c7 +Author: Steven Rostedt +Date: Sun Jan 5 11:22:20 2025 -0500 + + scripts/sorttable: Add helper functions for Elf_Ehdr + + In order to remove the double #include of sorttable.h for 64 and 32 bit + to create duplicate functions, add helper functions for Elf_Ehdr. This + will create a function pointer for each helper that will get assigned to + the appropriate function to handle either the 64bit or 32bit version. + + This also moves the _r()/r() wrappers for the Elf_Ehdr references that + handle endian and size differences between the different architectures, + into the helper function and out of the open code which is more error + prone. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Link: https://lore.kernel.org/20250105162345.736369526@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 57792cf2aa89..5dfa734eff09 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -85,6 +85,31 @@ static uint64_t (*r8)(const uint64_t *); + static void (*w)(uint32_t, uint32_t *); + typedef void (*table_sort_t)(char *, int); + ++static uint64_t ehdr64_shoff(Elf_Ehdr *ehdr) ++{ ++ return r8(&ehdr->e64.e_shoff); ++} ++ ++static uint64_t ehdr32_shoff(Elf_Ehdr *ehdr) ++{ ++ return r(&ehdr->e32.e_shoff); ++} ++ ++#define EHDR_HALF(fn_name) \ ++static uint16_t ehdr64_##fn_name(Elf_Ehdr *ehdr) \ ++{ \ ++ return r2(&ehdr->e64.e_##fn_name); \ ++} \ ++ \ ++static uint16_t ehdr32_##fn_name(Elf_Ehdr *ehdr) \ ++{ \ ++ return r2(&ehdr->e32.e_##fn_name); \ ++} ++ ++EHDR_HALF(shentsize) ++EHDR_HALF(shstrndx) ++EHDR_HALF(shnum) ++ + /* + * Get the whole file as a programming convenience in order to avoid + * malloc+lseek+read+free of many pieces. If successful, then mmap +diff --git a/scripts/sorttable.h b/scripts/sorttable.h +index cd4429c8a9f4..97278c973bc9 100644 +--- a/scripts/sorttable.h ++++ b/scripts/sorttable.h +@@ -27,6 +27,10 @@ + #undef uint_t + #undef _r + #undef etype ++#undef ehdr_shoff ++#undef ehdr_shentsize ++#undef ehdr_shstrndx ++#undef ehdr_shnum + + #ifdef SORTTABLE_64 + # define extable_ent_size 16 +@@ -39,6 +43,10 @@ + # define uint_t uint64_t + # define _r r8 + # define etype e64 ++# define ehdr_shoff ehdr64_shoff ++# define ehdr_shentsize ehdr64_shentsize ++# define ehdr_shstrndx ehdr64_shstrndx ++# define ehdr_shnum ehdr64_shnum + #else + # define extable_ent_size 8 + # define compare_extable compare_extable_32 +@@ -50,6 +58,10 @@ + # define uint_t uint32_t + # define _r r + # define etype e32 ++# define ehdr_shoff ehdr32_shoff ++# define ehdr_shentsize ehdr32_shentsize ++# define ehdr_shstrndx ehdr32_shstrndx ++# define ehdr_shnum ehdr32_shnum + #endif + + #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) +@@ -250,16 +262,16 @@ static int do_sort(Elf_Ehdr *ehdr, + unsigned int orc_num_entries = 0; + #endif + +- shdr_start = (Elf_Shdr *)((char *)ehdr + _r(&ehdr->etype.e_shoff)); +- shentsize = r2(&ehdr->etype.e_shentsize); ++ shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr)); ++ shentsize = ehdr_shentsize(ehdr); + +- shstrndx = r2(&ehdr->etype.e_shstrndx); ++ shstrndx = ehdr_shstrndx(ehdr); + if (shstrndx == SHN_XINDEX) + shstrndx = r(&shdr_start->etype.sh_link); + string_sec = get_index(shdr_start, shentsize, shstrndx); + secstrings = (const char *)ehdr + _r(&string_sec->etype.sh_offset); + +- shnum = r2(&ehdr->etype.e_shnum); ++ shnum = ehdr_shnum(ehdr); + if (shnum == SHN_UNDEF) + shnum = _r(&shdr_start->etype.sh_size); + +-- +2.50.1 (Apple Git-155) + diff --git a/1334-scripts-sorttable-add-helper-functions-for-elf-shdr.patch b/1334-scripts-sorttable-add-helper-functions-for-elf-shdr.patch new file mode 100644 index 000000000..ed19f852b --- /dev/null +++ b/1334-scripts-sorttable-add-helper-functions-for-elf-shdr.patch @@ -0,0 +1,253 @@ +From a5efd249d8b52767833dcd9ca0b7f8f5470cf289 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:22:40 +0200 +Subject: [PATCH] scripts/sorttable: Add helper functions for Elf_Shdr + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 67afb7f504400e5b4e5ff895459fbb3eb63d4450 +Author: Steven Rostedt +Date: Sun Jan 5 11:22:21 2025 -0500 + + scripts/sorttable: Add helper functions for Elf_Shdr + + In order to remove the double #include of sorttable.h for 64 and 32 bit + to create duplicate functions, add helper functions for Elf_Shdr. This + will create a function pointer for each helper that will get assigned to + the appropriate function to handle either the 64bit or 32bit version. + + This also moves the _r()/r() wrappers for the Elf_Shdr references that + handle endian and size differences between the different architectures, + into the helper function and out of the open code which is more error + prone. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Link: https://lore.kernel.org/20250105162345.940924221@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 5dfa734eff09..b2b96ff261d6 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -110,6 +110,48 @@ EHDR_HALF(shentsize) + EHDR_HALF(shstrndx) + EHDR_HALF(shnum) + ++#define SHDR_WORD(fn_name) \ ++static uint32_t shdr64_##fn_name(Elf_Shdr *shdr) \ ++{ \ ++ return r(&shdr->e64.sh_##fn_name); \ ++} \ ++ \ ++static uint32_t shdr32_##fn_name(Elf_Shdr *shdr) \ ++{ \ ++ return r(&shdr->e32.sh_##fn_name); \ ++} ++ ++#define SHDR_ADDR(fn_name) \ ++static uint64_t shdr64_##fn_name(Elf_Shdr *shdr) \ ++{ \ ++ return r8(&shdr->e64.sh_##fn_name); \ ++} \ ++ \ ++static uint64_t shdr32_##fn_name(Elf_Shdr *shdr) \ ++{ \ ++ return r(&shdr->e32.sh_##fn_name); \ ++} ++ ++#define SHDR_WORD(fn_name) \ ++static uint32_t shdr64_##fn_name(Elf_Shdr *shdr) \ ++{ \ ++ return r(&shdr->e64.sh_##fn_name); \ ++} \ ++ \ ++static uint32_t shdr32_##fn_name(Elf_Shdr *shdr) \ ++{ \ ++ return r(&shdr->e32.sh_##fn_name); \ ++} ++ ++SHDR_ADDR(addr) ++SHDR_ADDR(offset) ++SHDR_ADDR(size) ++SHDR_ADDR(entsize) ++ ++SHDR_WORD(link) ++SHDR_WORD(name) ++SHDR_WORD(type) ++ + /* + * Get the whole file as a programming convenience in order to avoid + * malloc+lseek+read+free of many pieces. If successful, then mmap +diff --git a/scripts/sorttable.h b/scripts/sorttable.h +index 97278c973bc9..af3a5f0209a3 100644 +--- a/scripts/sorttable.h ++++ b/scripts/sorttable.h +@@ -31,6 +31,13 @@ + #undef ehdr_shentsize + #undef ehdr_shstrndx + #undef ehdr_shnum ++#undef shdr_addr ++#undef shdr_offset ++#undef shdr_link ++#undef shdr_size ++#undef shdr_name ++#undef shdr_type ++#undef shdr_entsize + + #ifdef SORTTABLE_64 + # define extable_ent_size 16 +@@ -47,6 +54,13 @@ + # define ehdr_shentsize ehdr64_shentsize + # define ehdr_shstrndx ehdr64_shstrndx + # define ehdr_shnum ehdr64_shnum ++# define shdr_addr shdr64_addr ++# define shdr_offset shdr64_offset ++# define shdr_link shdr64_link ++# define shdr_size shdr64_size ++# define shdr_name shdr64_name ++# define shdr_type shdr64_type ++# define shdr_entsize shdr64_entsize + #else + # define extable_ent_size 8 + # define compare_extable compare_extable_32 +@@ -62,6 +76,13 @@ + # define ehdr_shentsize ehdr32_shentsize + # define ehdr_shstrndx ehdr32_shstrndx + # define ehdr_shnum ehdr32_shnum ++# define shdr_addr shdr32_addr ++# define shdr_offset shdr32_offset ++# define shdr_link shdr32_link ++# define shdr_size shdr32_size ++# define shdr_name shdr32_name ++# define shdr_type shdr32_type ++# define shdr_entsize shdr32_entsize + #endif + + #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) +@@ -177,8 +198,8 @@ struct elf_mcount_loc { + static void *sort_mcount_loc(void *arg) + { + struct elf_mcount_loc *emloc = (struct elf_mcount_loc *)arg; +- uint_t offset = emloc->start_mcount_loc - _r(&(emloc->init_data_sec)->etype.sh_addr) +- + _r(&(emloc->init_data_sec)->etype.sh_offset); ++ uint_t offset = emloc->start_mcount_loc - shdr_addr(emloc->init_data_sec) ++ + shdr_offset(emloc->init_data_sec); + uint_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc; + unsigned char *start_loc = (void *)emloc->ehdr + offset; + +@@ -267,18 +288,18 @@ static int do_sort(Elf_Ehdr *ehdr, + + shstrndx = ehdr_shstrndx(ehdr); + if (shstrndx == SHN_XINDEX) +- shstrndx = r(&shdr_start->etype.sh_link); ++ shstrndx = shdr_link(shdr_start); + string_sec = get_index(shdr_start, shentsize, shstrndx); +- secstrings = (const char *)ehdr + _r(&string_sec->etype.sh_offset); ++ secstrings = (const char *)ehdr + shdr_offset(string_sec); + + shnum = ehdr_shnum(ehdr); + if (shnum == SHN_UNDEF) +- shnum = _r(&shdr_start->etype.sh_size); ++ shnum = shdr_size(shdr_start); + + for (i = 0; i < shnum; i++) { + Elf_Shdr *shdr = get_index(shdr_start, shentsize, i); + +- idx = r(&shdr->etype.sh_name); ++ idx = shdr_name(shdr); + if (!strcmp(secstrings + idx, "__ex_table")) + extab_sec = shdr; + if (!strcmp(secstrings + idx, ".symtab")) +@@ -286,9 +307,9 @@ static int do_sort(Elf_Ehdr *ehdr, + if (!strcmp(secstrings + idx, ".strtab")) + strtab_sec = shdr; + +- if (r(&shdr->etype.sh_type) == SHT_SYMTAB_SHNDX) ++ if (shdr_type(shdr) == SHT_SYMTAB_SHNDX) + symtab_shndx = (Elf32_Word *)((const char *)ehdr + +- _r(&shdr->etype.sh_offset)); ++ shdr_offset(shdr)); + + #ifdef MCOUNT_SORT_ENABLED + /* locate the .init.data section in vmlinux */ +@@ -304,14 +325,14 @@ static int do_sort(Elf_Ehdr *ehdr, + #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) + /* locate the ORC unwind tables */ + if (!strcmp(secstrings + idx, ".orc_unwind_ip")) { +- orc_ip_size = _r(&shdr->etype.sh_size); ++ orc_ip_size = shdr_size(shdr); + g_orc_ip_table = (int *)((void *)ehdr + +- _r(&shdr->etype.sh_offset)); ++ shdr_offset(shdr)); + } + if (!strcmp(secstrings + idx, ".orc_unwind")) { +- orc_size = _r(&shdr->etype.sh_size); ++ orc_size = shdr_size(shdr); + g_orc_table = (struct orc_entry *)((void *)ehdr + +- _r(&shdr->etype.sh_offset)); ++ shdr_offset(shdr)); + } + #endif + } /* for loop */ +@@ -374,23 +395,22 @@ static int do_sort(Elf_Ehdr *ehdr, + goto out; + } + +- extab_image = (void *)ehdr + _r(&extab_sec->etype.sh_offset); +- strtab = (const char *)ehdr + _r(&strtab_sec->etype.sh_offset); +- symtab = (const Elf_Sym *)((const char *)ehdr + +- _r(&symtab_sec->etype.sh_offset)); ++ extab_image = (void *)ehdr + shdr_offset(extab_sec); ++ strtab = (const char *)ehdr + shdr_offset(strtab_sec); ++ symtab = (const Elf_Sym *)((const char *)ehdr + shdr_offset(symtab_sec)); + + if (custom_sort) { +- custom_sort(extab_image, _r(&extab_sec->etype.sh_size)); ++ custom_sort(extab_image, shdr_size(extab_sec)); + } else { +- int num_entries = _r(&extab_sec->etype.sh_size) / extable_ent_size; ++ int num_entries = shdr_size(extab_sec) / extable_ent_size; + qsort(extab_image, num_entries, + extable_ent_size, compare_extable); + } + + /* find the flag main_extable_sort_needed */ +- sym_start = (void *)ehdr + _r(&symtab_sec->etype.sh_offset); +- sym_end = sym_start + _r(&symtab_sec->etype.sh_size); +- symentsize = _r(&symtab_sec->etype.sh_entsize); ++ sym_start = (void *)ehdr + shdr_offset(symtab_sec); ++ sym_end = sym_start + shdr_size(symtab_sec); ++ symentsize = shdr_entsize(symtab_sec); + + for (sym = sym_start; (void *)sym + symentsize < sym_end; + sym = (void *)sym + symentsize) { +@@ -415,9 +435,9 @@ static int do_sort(Elf_Ehdr *ehdr, + symtab_shndx); + sort_needed_sec = get_index(shdr_start, shentsize, sort_need_index); + sort_needed_loc = (void *)ehdr + +- _r(&sort_needed_sec->etype.sh_offset) + ++ shdr_offset(sort_needed_sec) + + _r(&sort_needed_sym->etype.st_value) - +- _r(&sort_needed_sec->etype.sh_addr); ++ shdr_addr(sort_needed_sec); + + /* extable has been sorted, clear the flag */ + w(0, sort_needed_loc); +-- +2.50.1 (Apple Git-155) + diff --git a/1335-scripts-sorttable-add-helper-functions-for-elf-sym.patch b/1335-scripts-sorttable-add-helper-functions-for-elf-sym.patch new file mode 100644 index 000000000..7d5abcf2c --- /dev/null +++ b/1335-scripts-sorttable-add-helper-functions-for-elf-sym.patch @@ -0,0 +1,203 @@ +From 133d6f7d2c3e689e3191bf973e07819dda085f60 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:22:43 +0200 +Subject: [PATCH] scripts/sorttable: Add helper functions for Elf_Sym + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 17bed33ac12f011f4695059960e1b1d6457229a7 +Author: Steven Rostedt +Date: Sun Jan 5 11:22:22 2025 -0500 + + scripts/sorttable: Add helper functions for Elf_Sym + + In order to remove the double #include of sorttable.h for 64 and 32 bit + to create duplicate functions, add helper functions for Elf_Sym. This + will create a function pointer for each helper that will get assigned to + the appropriate function to handle either the 64bit or 32bit version. + + This also removes the last references of etype and _r() macros from the + sorttable.h file as their references are now just defined in the + appropriate architecture version of the helper functions. All read + functions now exist in the helper functions which makes it easier to + maintain, as the helper functions define the necessary architecture sizes. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Link: https://lore.kernel.org/20250105162346.185740651@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index b2b96ff261d6..20615de18276 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -152,6 +152,53 @@ SHDR_WORD(link) + SHDR_WORD(name) + SHDR_WORD(type) + ++#define SYM_ADDR(fn_name) \ ++static uint64_t sym64_##fn_name(Elf_Sym *sym) \ ++{ \ ++ return r8(&sym->e64.st_##fn_name); \ ++} \ ++ \ ++static uint64_t sym32_##fn_name(Elf_Sym *sym) \ ++{ \ ++ return r(&sym->e32.st_##fn_name); \ ++} ++ ++#define SYM_WORD(fn_name) \ ++static uint32_t sym64_##fn_name(Elf_Sym *sym) \ ++{ \ ++ return r(&sym->e64.st_##fn_name); \ ++} \ ++ \ ++static uint32_t sym32_##fn_name(Elf_Sym *sym) \ ++{ \ ++ return r(&sym->e32.st_##fn_name); \ ++} ++ ++#define SYM_HALF(fn_name) \ ++static uint16_t sym64_##fn_name(Elf_Sym *sym) \ ++{ \ ++ return r2(&sym->e64.st_##fn_name); \ ++} \ ++ \ ++static uint16_t sym32_##fn_name(Elf_Sym *sym) \ ++{ \ ++ return r2(&sym->e32.st_##fn_name); \ ++} ++ ++static uint8_t sym64_type(Elf_Sym *sym) ++{ ++ return ELF64_ST_TYPE(sym->e64.st_info); ++} ++ ++static uint8_t sym32_type(Elf_Sym *sym) ++{ ++ return ELF32_ST_TYPE(sym->e32.st_info); ++} ++ ++SYM_ADDR(value) ++SYM_WORD(name) ++SYM_HALF(shndx) ++ + /* + * Get the whole file as a programming convenience in order to avoid + * malloc+lseek+read+free of many pieces. If successful, then mmap +diff --git a/scripts/sorttable.h b/scripts/sorttable.h +index af3a5f0209a3..ef7e5161db31 100644 +--- a/scripts/sorttable.h ++++ b/scripts/sorttable.h +@@ -23,10 +23,7 @@ + #undef sort_mcount_loc + #undef elf_mcount_loc + #undef do_sort +-#undef ELF_ST_TYPE + #undef uint_t +-#undef _r +-#undef etype + #undef ehdr_shoff + #undef ehdr_shentsize + #undef ehdr_shstrndx +@@ -38,6 +35,10 @@ + #undef shdr_name + #undef shdr_type + #undef shdr_entsize ++#undef sym_type ++#undef sym_name ++#undef sym_value ++#undef sym_shndx + + #ifdef SORTTABLE_64 + # define extable_ent_size 16 +@@ -46,10 +47,7 @@ + # define sort_mcount_loc sort_mcount_loc_64 + # define elf_mcount_loc elf_mcount_loc_64 + # define do_sort do_sort_64 +-# define ELF_ST_TYPE ELF64_ST_TYPE + # define uint_t uint64_t +-# define _r r8 +-# define etype e64 + # define ehdr_shoff ehdr64_shoff + # define ehdr_shentsize ehdr64_shentsize + # define ehdr_shstrndx ehdr64_shstrndx +@@ -61,6 +59,10 @@ + # define shdr_name shdr64_name + # define shdr_type shdr64_type + # define shdr_entsize shdr64_entsize ++# define sym_type sym64_type ++# define sym_name sym64_name ++# define sym_value sym64_value ++# define sym_shndx sym64_shndx + #else + # define extable_ent_size 8 + # define compare_extable compare_extable_32 +@@ -68,10 +70,7 @@ + # define sort_mcount_loc sort_mcount_loc_32 + # define elf_mcount_loc elf_mcount_loc_32 + # define do_sort do_sort_32 +-# define ELF_ST_TYPE ELF32_ST_TYPE + # define uint_t uint32_t +-# define _r r +-# define etype e32 + # define ehdr_shoff ehdr32_shoff + # define ehdr_shentsize ehdr32_shentsize + # define ehdr_shstrndx ehdr32_shstrndx +@@ -83,6 +82,10 @@ + # define shdr_name shdr32_name + # define shdr_type shdr32_type + # define shdr_entsize shdr32_entsize ++# define sym_type sym32_type ++# define sym_name sym32_name ++# define sym_value sym32_value ++# define sym_shndx sym32_shndx + #endif + + #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) +@@ -414,9 +417,9 @@ static int do_sort(Elf_Ehdr *ehdr, + + for (sym = sym_start; (void *)sym + symentsize < sym_end; + sym = (void *)sym + symentsize) { +- if (ELF_ST_TYPE(sym->etype.st_info) != STT_OBJECT) ++ if (sym_type(sym) != STT_OBJECT) + continue; +- if (!strcmp(strtab + r(&sym->etype.st_name), ++ if (!strcmp(strtab + sym_name(sym), + "main_extable_sort_needed")) { + sort_needed_sym = sym; + break; +@@ -430,14 +433,13 @@ static int do_sort(Elf_Ehdr *ehdr, + goto out; + } + +- sort_need_index = get_secindex(r2(&sym->etype.st_shndx), ++ sort_need_index = get_secindex(sym_shndx(sym), + ((void *)sort_needed_sym - (void *)symtab) / symentsize, + symtab_shndx); + sort_needed_sec = get_index(shdr_start, shentsize, sort_need_index); + sort_needed_loc = (void *)ehdr + + shdr_offset(sort_needed_sec) + +- _r(&sort_needed_sym->etype.st_value) - +- shdr_addr(sort_needed_sec); ++ sym_value(sort_needed_sym) - shdr_addr(sort_needed_sec); + + /* extable has been sorted, clear the flag */ + w(0, sort_needed_loc); +-- +2.50.1 (Apple Git-155) + diff --git a/1336-scripts-sorttable-use-uint64-t-for-mcount-sorting.patch b/1336-scripts-sorttable-use-uint64-t-for-mcount-sorting.patch new file mode 100644 index 000000000..e6521ae69 --- /dev/null +++ b/1336-scripts-sorttable-use-uint64-t-for-mcount-sorting.patch @@ -0,0 +1,140 @@ +From daa2ce334210ed3ebb054d2f8097312ac7a60ae4 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:22:46 +0200 +Subject: [PATCH] scripts/sorttable: Use uint64_t for mcount sorting + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 1b649e6ab8dc9188d82c64069493afe66ca0edad +Author: Steven Rostedt +Date: Sun Jan 5 11:22:23 2025 -0500 + + scripts/sorttable: Use uint64_t for mcount sorting + + The mcount sorting defines uint_t to uint64_t on 64bit architectures and + uint32_t on 32bit architectures. It can work with just using uint64_t as + that will hold the values of both, and they are not used to point into the + ELF file. + + sizeof(uint_t) is used for defining the size of the mcount_loc section. + Instead of using a type, define long_size and use that instead. This will + allow the header code to be moved into the C file as generic functions and + not need to include sorttable.h twice, once for 64bit and once for 32bit. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Link: https://lore.kernel.org/20250105162346.373528925@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.h b/scripts/sorttable.h +index ef7e5161db31..17a8541a10d6 100644 +--- a/scripts/sorttable.h ++++ b/scripts/sorttable.h +@@ -23,7 +23,6 @@ + #undef sort_mcount_loc + #undef elf_mcount_loc + #undef do_sort +-#undef uint_t + #undef ehdr_shoff + #undef ehdr_shentsize + #undef ehdr_shstrndx +@@ -39,6 +38,7 @@ + #undef sym_name + #undef sym_value + #undef sym_shndx ++#undef long_size + + #ifdef SORTTABLE_64 + # define extable_ent_size 16 +@@ -47,7 +47,6 @@ + # define sort_mcount_loc sort_mcount_loc_64 + # define elf_mcount_loc elf_mcount_loc_64 + # define do_sort do_sort_64 +-# define uint_t uint64_t + # define ehdr_shoff ehdr64_shoff + # define ehdr_shentsize ehdr64_shentsize + # define ehdr_shstrndx ehdr64_shstrndx +@@ -63,6 +62,7 @@ + # define sym_name sym64_name + # define sym_value sym64_value + # define sym_shndx sym64_shndx ++# define long_size 8 + #else + # define extable_ent_size 8 + # define compare_extable compare_extable_32 +@@ -70,7 +70,6 @@ + # define sort_mcount_loc sort_mcount_loc_32 + # define elf_mcount_loc elf_mcount_loc_32 + # define do_sort do_sort_32 +-# define uint_t uint32_t + # define ehdr_shoff ehdr32_shoff + # define ehdr_shentsize ehdr32_shentsize + # define ehdr_shstrndx ehdr32_shstrndx +@@ -86,6 +85,7 @@ + # define sym_name sym32_name + # define sym_value sym32_value + # define sym_shndx sym32_shndx ++# define long_size 4 + #endif + + #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) +@@ -193,25 +193,25 @@ pthread_t mcount_sort_thread; + struct elf_mcount_loc { + Elf_Ehdr *ehdr; + Elf_Shdr *init_data_sec; +- uint_t start_mcount_loc; +- uint_t stop_mcount_loc; ++ uint64_t start_mcount_loc; ++ uint64_t stop_mcount_loc; + }; + + /* Sort the addresses stored between __start_mcount_loc to __stop_mcount_loc in vmlinux */ + static void *sort_mcount_loc(void *arg) + { + struct elf_mcount_loc *emloc = (struct elf_mcount_loc *)arg; +- uint_t offset = emloc->start_mcount_loc - shdr_addr(emloc->init_data_sec) ++ uint64_t offset = emloc->start_mcount_loc - shdr_addr(emloc->init_data_sec) + + shdr_offset(emloc->init_data_sec); +- uint_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc; ++ uint64_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc; + unsigned char *start_loc = (void *)emloc->ehdr + offset; + +- qsort(start_loc, count/sizeof(uint_t), sizeof(uint_t), compare_extable); ++ qsort(start_loc, count/long_size, long_size, compare_extable); + return NULL; + } + + /* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */ +-static void get_mcount_loc(uint_t *_start, uint_t *_stop) ++static void get_mcount_loc(uint64_t *_start, uint64_t *_stop) + { + FILE *file_start, *file_stop; + char start_buff[20]; +@@ -277,8 +277,8 @@ static int do_sort(Elf_Ehdr *ehdr, + unsigned int shstrndx; + #ifdef MCOUNT_SORT_ENABLED + struct elf_mcount_loc mstruct = {0}; +- uint_t _start_mcount_loc = 0; +- uint_t _stop_mcount_loc = 0; ++ uint64_t _start_mcount_loc = 0; ++ uint64_t _stop_mcount_loc = 0; + #endif + #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) + unsigned int orc_ip_size = 0; +-- +2.50.1 (Apple Git-155) + diff --git a/1337-scripts-sorttable-move-code-from-sorttable-h-into-sorttable-.patch b/1337-scripts-sorttable-move-code-from-sorttable-h-into-sorttable-.patch new file mode 100644 index 000000000..bb310e3be --- /dev/null +++ b/1337-scripts-sorttable-move-code-from-sorttable-h-into-sorttable-.patch @@ -0,0 +1,1056 @@ +From db0c9930bcabee5c191d0ccad767575ae169f3e1 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:36:28 +0200 +Subject: [PATCH] scripts/sorttable: Move code from sorttable.h into + sorttable.c + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 58d87678a0f46c6120904b4326aaf5ebf4454c69 +Author: Steven Rostedt +Date: Tue Jan 7 22:32:17 2025 -0500 + + scripts/sorttable: Move code from sorttable.h into sorttable.c + + Instead of having the main code live in a header file and included twice + with MACROs that define the Elf structures for 64 bit or 32 bit, move the + code in the C file now that the Elf structures are defined in a union that + has both. All accesses to the Elf structure fields are done through helper + function pointers. If the file being parsed if for a 64 bit architecture, + all the helper functions point to the 64 bit versions to retrieve the Elf + fields. The same is true if the architecture is 32 bit, where the function + pointers will point to the 32 bit helper functions. + + Note, when the value of a field can be either 32 bit or 64 bit, a 64 bit + is always returned, as it works for the 32 bit code as well. + + This makes the code easier to read and maintain, and it now all exists in + sorttable.c and sorttable.h may be removed. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Cc: Stephen Rothwell + Link: https://lore.kernel.org/20250107223217.6f7f96a5@gandalf.local.home + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 20615de18276..ff9b60fc0dd8 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -327,10 +327,423 @@ static inline void *get_index(void *start, int entsize, int index) + return start + (entsize * index); + } + +-/* 32 bit and 64 bit are very similar */ +-#include "sorttable.h" +-#define SORTTABLE_64 +-#include "sorttable.h" ++ ++static int (*compare_extable)(const void *a, const void *b); ++static uint64_t (*ehdr_shoff)(Elf_Ehdr *ehdr); ++static uint16_t (*ehdr_shstrndx)(Elf_Ehdr *ehdr); ++static uint16_t (*ehdr_shentsize)(Elf_Ehdr *ehdr); ++static uint16_t (*ehdr_shnum)(Elf_Ehdr *ehdr); ++static uint64_t (*shdr_addr)(Elf_Shdr *shdr); ++static uint64_t (*shdr_offset)(Elf_Shdr *shdr); ++static uint64_t (*shdr_size)(Elf_Shdr *shdr); ++static uint64_t (*shdr_entsize)(Elf_Shdr *shdr); ++static uint32_t (*shdr_link)(Elf_Shdr *shdr); ++static uint32_t (*shdr_name)(Elf_Shdr *shdr); ++static uint32_t (*shdr_type)(Elf_Shdr *shdr); ++static uint8_t (*sym_type)(Elf_Sym *sym); ++static uint32_t (*sym_name)(Elf_Sym *sym); ++static uint64_t (*sym_value)(Elf_Sym *sym); ++static uint16_t (*sym_shndx)(Elf_Sym *sym); ++ ++static int extable_ent_size; ++static int long_size; ++ ++ ++#ifdef UNWINDER_ORC_ENABLED ++/* ORC unwinder only support X86_64 */ ++#include ++ ++#define ERRSTR_MAXSZ 256 ++ ++static char g_err[ERRSTR_MAXSZ]; ++static int *g_orc_ip_table; ++static struct orc_entry *g_orc_table; ++ ++static pthread_t orc_sort_thread; ++ ++static inline unsigned long orc_ip(const int *ip) ++{ ++ return (unsigned long)ip + *ip; ++} ++ ++static int orc_sort_cmp(const void *_a, const void *_b) ++{ ++ struct orc_entry *orc_a, *orc_b; ++ const int *a = g_orc_ip_table + *(int *)_a; ++ const int *b = g_orc_ip_table + *(int *)_b; ++ unsigned long a_val = orc_ip(a); ++ unsigned long b_val = orc_ip(b); ++ ++ if (a_val > b_val) ++ return 1; ++ if (a_val < b_val) ++ return -1; ++ ++ /* ++ * The "weak" section terminator entries need to always be on the left ++ * to ensure the lookup code skips them in favor of real entries. ++ * These terminator entries exist to handle any gaps created by ++ * whitelisted .o files which didn't get objtool generation. ++ */ ++ orc_a = g_orc_table + (a - g_orc_ip_table); ++ orc_b = g_orc_table + (b - g_orc_ip_table); ++ if (orc_a->type == ORC_TYPE_UNDEFINED && orc_b->type == ORC_TYPE_UNDEFINED) ++ return 0; ++ return orc_a->type == ORC_TYPE_UNDEFINED ? -1 : 1; ++} ++ ++static void *sort_orctable(void *arg) ++{ ++ int i; ++ int *idxs = NULL; ++ int *tmp_orc_ip_table = NULL; ++ struct orc_entry *tmp_orc_table = NULL; ++ unsigned int *orc_ip_size = (unsigned int *)arg; ++ unsigned int num_entries = *orc_ip_size / sizeof(int); ++ unsigned int orc_size = num_entries * sizeof(struct orc_entry); ++ ++ idxs = (int *)malloc(*orc_ip_size); ++ if (!idxs) { ++ snprintf(g_err, ERRSTR_MAXSZ, "malloc idxs: %s", ++ strerror(errno)); ++ pthread_exit(g_err); ++ } ++ ++ tmp_orc_ip_table = (int *)malloc(*orc_ip_size); ++ if (!tmp_orc_ip_table) { ++ snprintf(g_err, ERRSTR_MAXSZ, "malloc tmp_orc_ip_table: %s", ++ strerror(errno)); ++ pthread_exit(g_err); ++ } ++ ++ tmp_orc_table = (struct orc_entry *)malloc(orc_size); ++ if (!tmp_orc_table) { ++ snprintf(g_err, ERRSTR_MAXSZ, "malloc tmp_orc_table: %s", ++ strerror(errno)); ++ pthread_exit(g_err); ++ } ++ ++ /* initialize indices array, convert ip_table to absolute address */ ++ for (i = 0; i < num_entries; i++) { ++ idxs[i] = i; ++ tmp_orc_ip_table[i] = g_orc_ip_table[i] + i * sizeof(int); ++ } ++ memcpy(tmp_orc_table, g_orc_table, orc_size); ++ ++ qsort(idxs, num_entries, sizeof(int), orc_sort_cmp); ++ ++ for (i = 0; i < num_entries; i++) { ++ if (idxs[i] == i) ++ continue; ++ ++ /* convert back to relative address */ ++ g_orc_ip_table[i] = tmp_orc_ip_table[idxs[i]] - i * sizeof(int); ++ g_orc_table[i] = tmp_orc_table[idxs[i]]; ++ } ++ ++ free(idxs); ++ free(tmp_orc_ip_table); ++ free(tmp_orc_table); ++ pthread_exit(NULL); ++} ++#endif ++ ++#ifdef MCOUNT_SORT_ENABLED ++static pthread_t mcount_sort_thread; ++ ++struct elf_mcount_loc { ++ Elf_Ehdr *ehdr; ++ Elf_Shdr *init_data_sec; ++ uint64_t start_mcount_loc; ++ uint64_t stop_mcount_loc; ++}; ++ ++/* Sort the addresses stored between __start_mcount_loc to __stop_mcount_loc in vmlinux */ ++static void *sort_mcount_loc(void *arg) ++{ ++ struct elf_mcount_loc *emloc = (struct elf_mcount_loc *)arg; ++ uint64_t offset = emloc->start_mcount_loc - shdr_addr(emloc->init_data_sec) ++ + shdr_offset(emloc->init_data_sec); ++ uint64_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc; ++ unsigned char *start_loc = (void *)emloc->ehdr + offset; ++ ++ qsort(start_loc, count/long_size, long_size, compare_extable); ++ return NULL; ++} ++ ++/* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */ ++static void get_mcount_loc(uint64_t *_start, uint64_t *_stop) ++{ ++ FILE *file_start, *file_stop; ++ char start_buff[20]; ++ char stop_buff[20]; ++ int len = 0; ++ ++ file_start = popen(" grep start_mcount System.map | awk '{print $1}' ", "r"); ++ if (!file_start) { ++ fprintf(stderr, "get start_mcount_loc error!"); ++ return; ++ } ++ ++ file_stop = popen(" grep stop_mcount System.map | awk '{print $1}' ", "r"); ++ if (!file_stop) { ++ fprintf(stderr, "get stop_mcount_loc error!"); ++ pclose(file_start); ++ return; ++ } ++ ++ while (fgets(start_buff, sizeof(start_buff), file_start) != NULL) { ++ len = strlen(start_buff); ++ start_buff[len - 1] = '\0'; ++ } ++ *_start = strtoul(start_buff, NULL, 16); ++ ++ while (fgets(stop_buff, sizeof(stop_buff), file_stop) != NULL) { ++ len = strlen(stop_buff); ++ stop_buff[len - 1] = '\0'; ++ } ++ *_stop = strtoul(stop_buff, NULL, 16); ++ ++ pclose(file_start); ++ pclose(file_stop); ++} ++#endif ++static int do_sort(Elf_Ehdr *ehdr, ++ char const *const fname, ++ table_sort_t custom_sort) ++{ ++ int rc = -1; ++ Elf_Shdr *shdr_start; ++ Elf_Shdr *strtab_sec = NULL; ++ Elf_Shdr *symtab_sec = NULL; ++ Elf_Shdr *extab_sec = NULL; ++ Elf_Shdr *string_sec; ++ Elf_Sym *sym; ++ const Elf_Sym *symtab; ++ Elf32_Word *symtab_shndx = NULL; ++ Elf_Sym *sort_needed_sym = NULL; ++ Elf_Shdr *sort_needed_sec; ++ uint32_t *sort_needed_loc; ++ void *sym_start; ++ void *sym_end; ++ const char *secstrings; ++ const char *strtab; ++ char *extab_image; ++ int sort_need_index; ++ int symentsize; ++ int shentsize; ++ int idx; ++ int i; ++ unsigned int shnum; ++ unsigned int shstrndx; ++#ifdef MCOUNT_SORT_ENABLED ++ struct elf_mcount_loc mstruct = {0}; ++ uint64_t _start_mcount_loc = 0; ++ uint64_t _stop_mcount_loc = 0; ++#endif ++#ifdef UNWINDER_ORC_ENABLED ++ unsigned int orc_ip_size = 0; ++ unsigned int orc_size = 0; ++ unsigned int orc_num_entries = 0; ++#endif ++ ++ shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr)); ++ shentsize = ehdr_shentsize(ehdr); ++ ++ shstrndx = ehdr_shstrndx(ehdr); ++ if (shstrndx == SHN_XINDEX) ++ shstrndx = shdr_link(shdr_start); ++ string_sec = get_index(shdr_start, shentsize, shstrndx); ++ secstrings = (const char *)ehdr + shdr_offset(string_sec); ++ ++ shnum = ehdr_shnum(ehdr); ++ if (shnum == SHN_UNDEF) ++ shnum = shdr_size(shdr_start); ++ ++ for (i = 0; i < shnum; i++) { ++ Elf_Shdr *shdr = get_index(shdr_start, shentsize, i); ++ ++ idx = shdr_name(shdr); ++ if (!strcmp(secstrings + idx, "__ex_table")) ++ extab_sec = shdr; ++ if (!strcmp(secstrings + idx, ".symtab")) ++ symtab_sec = shdr; ++ if (!strcmp(secstrings + idx, ".strtab")) ++ strtab_sec = shdr; ++ ++ if (shdr_type(shdr) == SHT_SYMTAB_SHNDX) ++ symtab_shndx = (Elf32_Word *)((const char *)ehdr + ++ shdr_offset(shdr)); ++ ++#ifdef MCOUNT_SORT_ENABLED ++ /* locate the .init.data section in vmlinux */ ++ if (!strcmp(secstrings + idx, ".init.data")) { ++ get_mcount_loc(&_start_mcount_loc, &_stop_mcount_loc); ++ mstruct.ehdr = ehdr; ++ mstruct.init_data_sec = shdr; ++ mstruct.start_mcount_loc = _start_mcount_loc; ++ mstruct.stop_mcount_loc = _stop_mcount_loc; ++ } ++#endif ++ ++#ifdef UNWINDER_ORC_ENABLED ++ /* locate the ORC unwind tables */ ++ if (!strcmp(secstrings + idx, ".orc_unwind_ip")) { ++ orc_ip_size = shdr_size(shdr); ++ g_orc_ip_table = (int *)((void *)ehdr + ++ shdr_offset(shdr)); ++ } ++ if (!strcmp(secstrings + idx, ".orc_unwind")) { ++ orc_size = shdr_size(shdr); ++ g_orc_table = (struct orc_entry *)((void *)ehdr + ++ shdr_offset(shdr)); ++ } ++#endif ++ } /* for loop */ ++ ++#ifdef UNWINDER_ORC_ENABLED ++ if (!g_orc_ip_table || !g_orc_table) { ++ fprintf(stderr, ++ "incomplete ORC unwind tables in file: %s\n", fname); ++ goto out; ++ } ++ ++ orc_num_entries = orc_ip_size / sizeof(int); ++ if (orc_ip_size % sizeof(int) != 0 || ++ orc_size % sizeof(struct orc_entry) != 0 || ++ orc_num_entries != orc_size / sizeof(struct orc_entry)) { ++ fprintf(stderr, ++ "inconsistent ORC unwind table entries in file: %s\n", ++ fname); ++ goto out; ++ } ++ ++ /* create thread to sort ORC unwind tables concurrently */ ++ if (pthread_create(&orc_sort_thread, NULL, ++ sort_orctable, &orc_ip_size)) { ++ fprintf(stderr, ++ "pthread_create orc_sort_thread failed '%s': %s\n", ++ strerror(errno), fname); ++ goto out; ++ } ++#endif ++ ++#ifdef MCOUNT_SORT_ENABLED ++ if (!mstruct.init_data_sec || !_start_mcount_loc || !_stop_mcount_loc) { ++ fprintf(stderr, ++ "incomplete mcount's sort in file: %s\n", ++ fname); ++ goto out; ++ } ++ ++ /* create thread to sort mcount_loc concurrently */ ++ if (pthread_create(&mcount_sort_thread, NULL, &sort_mcount_loc, &mstruct)) { ++ fprintf(stderr, ++ "pthread_create mcount_sort_thread failed '%s': %s\n", ++ strerror(errno), fname); ++ goto out; ++ } ++#endif ++ if (!extab_sec) { ++ fprintf(stderr, "no __ex_table in file: %s\n", fname); ++ goto out; ++ } ++ ++ if (!symtab_sec) { ++ fprintf(stderr, "no .symtab in file: %s\n", fname); ++ goto out; ++ } ++ ++ if (!strtab_sec) { ++ fprintf(stderr, "no .strtab in file: %s\n", fname); ++ goto out; ++ } ++ ++ extab_image = (void *)ehdr + shdr_offset(extab_sec); ++ strtab = (const char *)ehdr + shdr_offset(strtab_sec); ++ symtab = (const Elf_Sym *)((const char *)ehdr + shdr_offset(symtab_sec)); ++ ++ if (custom_sort) { ++ custom_sort(extab_image, shdr_size(extab_sec)); ++ } else { ++ int num_entries = shdr_size(extab_sec) / extable_ent_size; ++ qsort(extab_image, num_entries, ++ extable_ent_size, compare_extable); ++ } ++ ++ /* find the flag main_extable_sort_needed */ ++ sym_start = (void *)ehdr + shdr_offset(symtab_sec); ++ sym_end = sym_start + shdr_size(symtab_sec); ++ symentsize = shdr_entsize(symtab_sec); ++ ++ for (sym = sym_start; (void *)sym + symentsize < sym_end; ++ sym = (void *)sym + symentsize) { ++ if (sym_type(sym) != STT_OBJECT) ++ continue; ++ if (!strcmp(strtab + sym_name(sym), ++ "main_extable_sort_needed")) { ++ sort_needed_sym = sym; ++ break; ++ } ++ } ++ ++ if (!sort_needed_sym) { ++ fprintf(stderr, ++ "no main_extable_sort_needed symbol in file: %s\n", ++ fname); ++ goto out; ++ } ++ ++ sort_need_index = get_secindex(sym_shndx(sym), ++ ((void *)sort_needed_sym - (void *)symtab) / symentsize, ++ symtab_shndx); ++ sort_needed_sec = get_index(shdr_start, shentsize, sort_need_index); ++ sort_needed_loc = (void *)ehdr + ++ shdr_offset(sort_needed_sec) + ++ sym_value(sort_needed_sym) - shdr_addr(sort_needed_sec); ++ ++ /* extable has been sorted, clear the flag */ ++ w(0, sort_needed_loc); ++ rc = 0; ++ ++out: ++#ifdef UNWINDER_ORC_ENABLED ++ if (orc_sort_thread) { ++ void *retval = NULL; ++ /* wait for ORC tables sort done */ ++ rc = pthread_join(orc_sort_thread, &retval); ++ if (rc) { ++ fprintf(stderr, ++ "pthread_join failed '%s': %s\n", ++ strerror(errno), fname); ++ } else if (retval) { ++ rc = -1; ++ fprintf(stderr, ++ "failed to sort ORC tables '%s': %s\n", ++ (char *)retval, fname); ++ } ++ } ++#endif ++ ++#ifdef MCOUNT_SORT_ENABLED ++ if (mcount_sort_thread) { ++ void *retval = NULL; ++ /* wait for mcount sort done */ ++ rc = pthread_join(mcount_sort_thread, &retval); ++ if (rc) { ++ fprintf(stderr, ++ "pthread_join failed '%s': %s\n", ++ strerror(errno), fname); ++ } else if (retval) { ++ rc = -1; ++ fprintf(stderr, ++ "failed to sort mcount '%s': %s\n", ++ (char *)retval, fname); ++ } ++ } ++#endif ++ return rc; ++} + + static int compare_relative_table(const void *a, const void *b) + { +@@ -399,7 +812,6 @@ static void sort_relative_table_with_data(char *extab_image, int image_size) + + static int do_file(char const *const fname, void *addr) + { +- int rc = -1; + Elf_Ehdr *ehdr = addr; + table_sort_t custom_sort = NULL; + +@@ -462,29 +874,64 @@ static int do_file(char const *const fname, void *addr) + r2(&ehdr->e32.e_shentsize) != sizeof(Elf32_Shdr)) { + fprintf(stderr, + "unrecognized ET_EXEC/ET_DYN file: %s\n", fname); +- break; ++ return -1; + } +- rc = do_sort_32(ehdr, fname, custom_sort); ++ ++ compare_extable = compare_extable_32; ++ ehdr_shoff = ehdr32_shoff; ++ ehdr_shentsize = ehdr32_shentsize; ++ ehdr_shstrndx = ehdr32_shstrndx; ++ ehdr_shnum = ehdr32_shnum; ++ shdr_addr = shdr32_addr; ++ shdr_offset = shdr32_offset; ++ shdr_link = shdr32_link; ++ shdr_size = shdr32_size; ++ shdr_name = shdr32_name; ++ shdr_type = shdr32_type; ++ shdr_entsize = shdr32_entsize; ++ sym_type = sym32_type; ++ sym_name = sym32_name; ++ sym_value = sym32_value; ++ sym_shndx = sym32_shndx; ++ long_size = 4; ++ extable_ent_size = 8; + break; + case ELFCLASS64: +- { + if (r2(&ehdr->e64.e_ehsize) != sizeof(Elf64_Ehdr) || + r2(&ehdr->e64.e_shentsize) != sizeof(Elf64_Shdr)) { + fprintf(stderr, + "unrecognized ET_EXEC/ET_DYN file: %s\n", + fname); +- break; +- } +- rc = do_sort_64(ehdr, fname, custom_sort); ++ return -1; + } ++ ++ compare_extable = compare_extable_64; ++ ehdr_shoff = ehdr64_shoff; ++ ehdr_shentsize = ehdr64_shentsize; ++ ehdr_shstrndx = ehdr64_shstrndx; ++ ehdr_shnum = ehdr64_shnum; ++ shdr_addr = shdr64_addr; ++ shdr_offset = shdr64_offset; ++ shdr_link = shdr64_link; ++ shdr_size = shdr64_size; ++ shdr_name = shdr64_name; ++ shdr_type = shdr64_type; ++ shdr_entsize = shdr64_entsize; ++ sym_type = sym64_type; ++ sym_name = sym64_name; ++ sym_value = sym64_value; ++ sym_shndx = sym64_shndx; ++ long_size = 8; ++ extable_ent_size = 16; ++ + break; + default: + fprintf(stderr, "unrecognized ELF class %d %s\n", + ehdr->e32.e_ident[EI_CLASS], fname); +- break; ++ return -1; + } + +- return rc; ++ return do_sort(ehdr, fname, custom_sort); + } + + int main(int argc, char *argv[]) +diff --git a/scripts/sorttable.h b/scripts/sorttable.h +deleted file mode 100644 +index 17a8541a10d6..000000000000 +--- a/scripts/sorttable.h ++++ /dev/null +@@ -1,485 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/* +- * sorttable.h +- * +- * Added ORC unwind tables sort support and other updates: +- * Copyright (C) 1999-2019 Alibaba Group Holding Limited. by: +- * Shile Zhang +- * +- * Copyright 2011 - 2012 Cavium, Inc. +- * +- * Some of code was taken out of arch/x86/kernel/unwind_orc.c, written by: +- * Copyright (C) 2017 Josh Poimboeuf +- * +- * Some of this code was taken out of recordmcount.h written by: +- * +- * Copyright 2009 John F. Reiser . All rights reserved. +- * Copyright 2010 Steven Rostedt , Red Hat Inc. +- */ +- +-#undef extable_ent_size +-#undef compare_extable +-#undef get_mcount_loc +-#undef sort_mcount_loc +-#undef elf_mcount_loc +-#undef do_sort +-#undef ehdr_shoff +-#undef ehdr_shentsize +-#undef ehdr_shstrndx +-#undef ehdr_shnum +-#undef shdr_addr +-#undef shdr_offset +-#undef shdr_link +-#undef shdr_size +-#undef shdr_name +-#undef shdr_type +-#undef shdr_entsize +-#undef sym_type +-#undef sym_name +-#undef sym_value +-#undef sym_shndx +-#undef long_size +- +-#ifdef SORTTABLE_64 +-# define extable_ent_size 16 +-# define compare_extable compare_extable_64 +-# define get_mcount_loc get_mcount_loc_64 +-# define sort_mcount_loc sort_mcount_loc_64 +-# define elf_mcount_loc elf_mcount_loc_64 +-# define do_sort do_sort_64 +-# define ehdr_shoff ehdr64_shoff +-# define ehdr_shentsize ehdr64_shentsize +-# define ehdr_shstrndx ehdr64_shstrndx +-# define ehdr_shnum ehdr64_shnum +-# define shdr_addr shdr64_addr +-# define shdr_offset shdr64_offset +-# define shdr_link shdr64_link +-# define shdr_size shdr64_size +-# define shdr_name shdr64_name +-# define shdr_type shdr64_type +-# define shdr_entsize shdr64_entsize +-# define sym_type sym64_type +-# define sym_name sym64_name +-# define sym_value sym64_value +-# define sym_shndx sym64_shndx +-# define long_size 8 +-#else +-# define extable_ent_size 8 +-# define compare_extable compare_extable_32 +-# define get_mcount_loc get_mcount_loc_32 +-# define sort_mcount_loc sort_mcount_loc_32 +-# define elf_mcount_loc elf_mcount_loc_32 +-# define do_sort do_sort_32 +-# define ehdr_shoff ehdr32_shoff +-# define ehdr_shentsize ehdr32_shentsize +-# define ehdr_shstrndx ehdr32_shstrndx +-# define ehdr_shnum ehdr32_shnum +-# define shdr_addr shdr32_addr +-# define shdr_offset shdr32_offset +-# define shdr_link shdr32_link +-# define shdr_size shdr32_size +-# define shdr_name shdr32_name +-# define shdr_type shdr32_type +-# define shdr_entsize shdr32_entsize +-# define sym_type sym32_type +-# define sym_name sym32_name +-# define sym_value sym32_value +-# define sym_shndx sym32_shndx +-# define long_size 4 +-#endif +- +-#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) +-/* ORC unwinder only support X86_64 */ +-#include +- +-#define ERRSTR_MAXSZ 256 +- +-char g_err[ERRSTR_MAXSZ]; +-int *g_orc_ip_table; +-struct orc_entry *g_orc_table; +- +-pthread_t orc_sort_thread; +- +-static inline unsigned long orc_ip(const int *ip) +-{ +- return (unsigned long)ip + *ip; +-} +- +-static int orc_sort_cmp(const void *_a, const void *_b) +-{ +- struct orc_entry *orc_a, *orc_b; +- const int *a = g_orc_ip_table + *(int *)_a; +- const int *b = g_orc_ip_table + *(int *)_b; +- unsigned long a_val = orc_ip(a); +- unsigned long b_val = orc_ip(b); +- +- if (a_val > b_val) +- return 1; +- if (a_val < b_val) +- return -1; +- +- /* +- * The "weak" section terminator entries need to always be on the left +- * to ensure the lookup code skips them in favor of real entries. +- * These terminator entries exist to handle any gaps created by +- * whitelisted .o files which didn't get objtool generation. +- */ +- orc_a = g_orc_table + (a - g_orc_ip_table); +- orc_b = g_orc_table + (b - g_orc_ip_table); +- if (orc_a->type == ORC_TYPE_UNDEFINED && orc_b->type == ORC_TYPE_UNDEFINED) +- return 0; +- return orc_a->type == ORC_TYPE_UNDEFINED ? -1 : 1; +-} +- +-static void *sort_orctable(void *arg) +-{ +- int i; +- int *idxs = NULL; +- int *tmp_orc_ip_table = NULL; +- struct orc_entry *tmp_orc_table = NULL; +- unsigned int *orc_ip_size = (unsigned int *)arg; +- unsigned int num_entries = *orc_ip_size / sizeof(int); +- unsigned int orc_size = num_entries * sizeof(struct orc_entry); +- +- idxs = (int *)malloc(*orc_ip_size); +- if (!idxs) { +- snprintf(g_err, ERRSTR_MAXSZ, "malloc idxs: %s", +- strerror(errno)); +- pthread_exit(g_err); +- } +- +- tmp_orc_ip_table = (int *)malloc(*orc_ip_size); +- if (!tmp_orc_ip_table) { +- snprintf(g_err, ERRSTR_MAXSZ, "malloc tmp_orc_ip_table: %s", +- strerror(errno)); +- pthread_exit(g_err); +- } +- +- tmp_orc_table = (struct orc_entry *)malloc(orc_size); +- if (!tmp_orc_table) { +- snprintf(g_err, ERRSTR_MAXSZ, "malloc tmp_orc_table: %s", +- strerror(errno)); +- pthread_exit(g_err); +- } +- +- /* initialize indices array, convert ip_table to absolute address */ +- for (i = 0; i < num_entries; i++) { +- idxs[i] = i; +- tmp_orc_ip_table[i] = g_orc_ip_table[i] + i * sizeof(int); +- } +- memcpy(tmp_orc_table, g_orc_table, orc_size); +- +- qsort(idxs, num_entries, sizeof(int), orc_sort_cmp); +- +- for (i = 0; i < num_entries; i++) { +- if (idxs[i] == i) +- continue; +- +- /* convert back to relative address */ +- g_orc_ip_table[i] = tmp_orc_ip_table[idxs[i]] - i * sizeof(int); +- g_orc_table[i] = tmp_orc_table[idxs[i]]; +- } +- +- free(idxs); +- free(tmp_orc_ip_table); +- free(tmp_orc_table); +- pthread_exit(NULL); +-} +-#endif +- +-#ifdef MCOUNT_SORT_ENABLED +-pthread_t mcount_sort_thread; +- +-struct elf_mcount_loc { +- Elf_Ehdr *ehdr; +- Elf_Shdr *init_data_sec; +- uint64_t start_mcount_loc; +- uint64_t stop_mcount_loc; +-}; +- +-/* Sort the addresses stored between __start_mcount_loc to __stop_mcount_loc in vmlinux */ +-static void *sort_mcount_loc(void *arg) +-{ +- struct elf_mcount_loc *emloc = (struct elf_mcount_loc *)arg; +- uint64_t offset = emloc->start_mcount_loc - shdr_addr(emloc->init_data_sec) +- + shdr_offset(emloc->init_data_sec); +- uint64_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc; +- unsigned char *start_loc = (void *)emloc->ehdr + offset; +- +- qsort(start_loc, count/long_size, long_size, compare_extable); +- return NULL; +-} +- +-/* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */ +-static void get_mcount_loc(uint64_t *_start, uint64_t *_stop) +-{ +- FILE *file_start, *file_stop; +- char start_buff[20]; +- char stop_buff[20]; +- int len = 0; +- +- file_start = popen(" grep start_mcount System.map | awk '{print $1}' ", "r"); +- if (!file_start) { +- fprintf(stderr, "get start_mcount_loc error!"); +- return; +- } +- +- file_stop = popen(" grep stop_mcount System.map | awk '{print $1}' ", "r"); +- if (!file_stop) { +- fprintf(stderr, "get stop_mcount_loc error!"); +- pclose(file_start); +- return; +- } +- +- while (fgets(start_buff, sizeof(start_buff), file_start) != NULL) { +- len = strlen(start_buff); +- start_buff[len - 1] = '\0'; +- } +- *_start = strtoul(start_buff, NULL, 16); +- +- while (fgets(stop_buff, sizeof(stop_buff), file_stop) != NULL) { +- len = strlen(stop_buff); +- stop_buff[len - 1] = '\0'; +- } +- *_stop = strtoul(stop_buff, NULL, 16); +- +- pclose(file_start); +- pclose(file_stop); +-} +-#endif +-static int do_sort(Elf_Ehdr *ehdr, +- char const *const fname, +- table_sort_t custom_sort) +-{ +- int rc = -1; +- Elf_Shdr *shdr_start; +- Elf_Shdr *strtab_sec = NULL; +- Elf_Shdr *symtab_sec = NULL; +- Elf_Shdr *extab_sec = NULL; +- Elf_Shdr *string_sec; +- Elf_Sym *sym; +- const Elf_Sym *symtab; +- Elf32_Word *symtab_shndx = NULL; +- Elf_Sym *sort_needed_sym = NULL; +- Elf_Shdr *sort_needed_sec; +- uint32_t *sort_needed_loc; +- void *sym_start; +- void *sym_end; +- const char *secstrings; +- const char *strtab; +- char *extab_image; +- int sort_need_index; +- int symentsize; +- int shentsize; +- int idx; +- int i; +- unsigned int shnum; +- unsigned int shstrndx; +-#ifdef MCOUNT_SORT_ENABLED +- struct elf_mcount_loc mstruct = {0}; +- uint64_t _start_mcount_loc = 0; +- uint64_t _stop_mcount_loc = 0; +-#endif +-#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) +- unsigned int orc_ip_size = 0; +- unsigned int orc_size = 0; +- unsigned int orc_num_entries = 0; +-#endif +- +- shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr)); +- shentsize = ehdr_shentsize(ehdr); +- +- shstrndx = ehdr_shstrndx(ehdr); +- if (shstrndx == SHN_XINDEX) +- shstrndx = shdr_link(shdr_start); +- string_sec = get_index(shdr_start, shentsize, shstrndx); +- secstrings = (const char *)ehdr + shdr_offset(string_sec); +- +- shnum = ehdr_shnum(ehdr); +- if (shnum == SHN_UNDEF) +- shnum = shdr_size(shdr_start); +- +- for (i = 0; i < shnum; i++) { +- Elf_Shdr *shdr = get_index(shdr_start, shentsize, i); +- +- idx = shdr_name(shdr); +- if (!strcmp(secstrings + idx, "__ex_table")) +- extab_sec = shdr; +- if (!strcmp(secstrings + idx, ".symtab")) +- symtab_sec = shdr; +- if (!strcmp(secstrings + idx, ".strtab")) +- strtab_sec = shdr; +- +- if (shdr_type(shdr) == SHT_SYMTAB_SHNDX) +- symtab_shndx = (Elf32_Word *)((const char *)ehdr + +- shdr_offset(shdr)); +- +-#ifdef MCOUNT_SORT_ENABLED +- /* locate the .init.data section in vmlinux */ +- if (!strcmp(secstrings + idx, ".init.data")) { +- get_mcount_loc(&_start_mcount_loc, &_stop_mcount_loc); +- mstruct.ehdr = ehdr; +- mstruct.init_data_sec = shdr; +- mstruct.start_mcount_loc = _start_mcount_loc; +- mstruct.stop_mcount_loc = _stop_mcount_loc; +- } +-#endif +- +-#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) +- /* locate the ORC unwind tables */ +- if (!strcmp(secstrings + idx, ".orc_unwind_ip")) { +- orc_ip_size = shdr_size(shdr); +- g_orc_ip_table = (int *)((void *)ehdr + +- shdr_offset(shdr)); +- } +- if (!strcmp(secstrings + idx, ".orc_unwind")) { +- orc_size = shdr_size(shdr); +- g_orc_table = (struct orc_entry *)((void *)ehdr + +- shdr_offset(shdr)); +- } +-#endif +- } /* for loop */ +- +-#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) +- if (!g_orc_ip_table || !g_orc_table) { +- fprintf(stderr, +- "incomplete ORC unwind tables in file: %s\n", fname); +- goto out; +- } +- +- orc_num_entries = orc_ip_size / sizeof(int); +- if (orc_ip_size % sizeof(int) != 0 || +- orc_size % sizeof(struct orc_entry) != 0 || +- orc_num_entries != orc_size / sizeof(struct orc_entry)) { +- fprintf(stderr, +- "inconsistent ORC unwind table entries in file: %s\n", +- fname); +- goto out; +- } +- +- /* create thread to sort ORC unwind tables concurrently */ +- if (pthread_create(&orc_sort_thread, NULL, +- sort_orctable, &orc_ip_size)) { +- fprintf(stderr, +- "pthread_create orc_sort_thread failed '%s': %s\n", +- strerror(errno), fname); +- goto out; +- } +-#endif +- +-#ifdef MCOUNT_SORT_ENABLED +- if (!mstruct.init_data_sec || !_start_mcount_loc || !_stop_mcount_loc) { +- fprintf(stderr, +- "incomplete mcount's sort in file: %s\n", +- fname); +- goto out; +- } +- +- /* create thread to sort mcount_loc concurrently */ +- if (pthread_create(&mcount_sort_thread, NULL, &sort_mcount_loc, &mstruct)) { +- fprintf(stderr, +- "pthread_create mcount_sort_thread failed '%s': %s\n", +- strerror(errno), fname); +- goto out; +- } +-#endif +- if (!extab_sec) { +- fprintf(stderr, "no __ex_table in file: %s\n", fname); +- goto out; +- } +- +- if (!symtab_sec) { +- fprintf(stderr, "no .symtab in file: %s\n", fname); +- goto out; +- } +- +- if (!strtab_sec) { +- fprintf(stderr, "no .strtab in file: %s\n", fname); +- goto out; +- } +- +- extab_image = (void *)ehdr + shdr_offset(extab_sec); +- strtab = (const char *)ehdr + shdr_offset(strtab_sec); +- symtab = (const Elf_Sym *)((const char *)ehdr + shdr_offset(symtab_sec)); +- +- if (custom_sort) { +- custom_sort(extab_image, shdr_size(extab_sec)); +- } else { +- int num_entries = shdr_size(extab_sec) / extable_ent_size; +- qsort(extab_image, num_entries, +- extable_ent_size, compare_extable); +- } +- +- /* find the flag main_extable_sort_needed */ +- sym_start = (void *)ehdr + shdr_offset(symtab_sec); +- sym_end = sym_start + shdr_size(symtab_sec); +- symentsize = shdr_entsize(symtab_sec); +- +- for (sym = sym_start; (void *)sym + symentsize < sym_end; +- sym = (void *)sym + symentsize) { +- if (sym_type(sym) != STT_OBJECT) +- continue; +- if (!strcmp(strtab + sym_name(sym), +- "main_extable_sort_needed")) { +- sort_needed_sym = sym; +- break; +- } +- } +- +- if (!sort_needed_sym) { +- fprintf(stderr, +- "no main_extable_sort_needed symbol in file: %s\n", +- fname); +- goto out; +- } +- +- sort_need_index = get_secindex(sym_shndx(sym), +- ((void *)sort_needed_sym - (void *)symtab) / symentsize, +- symtab_shndx); +- sort_needed_sec = get_index(shdr_start, shentsize, sort_need_index); +- sort_needed_loc = (void *)ehdr + +- shdr_offset(sort_needed_sec) + +- sym_value(sort_needed_sym) - shdr_addr(sort_needed_sec); +- +- /* extable has been sorted, clear the flag */ +- w(0, sort_needed_loc); +- rc = 0; +- +-out: +-#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) +- if (orc_sort_thread) { +- void *retval = NULL; +- /* wait for ORC tables sort done */ +- rc = pthread_join(orc_sort_thread, &retval); +- if (rc) { +- fprintf(stderr, +- "pthread_join failed '%s': %s\n", +- strerror(errno), fname); +- } else if (retval) { +- rc = -1; +- fprintf(stderr, +- "failed to sort ORC tables '%s': %s\n", +- (char *)retval, fname); +- } +- } +-#endif +- +-#ifdef MCOUNT_SORT_ENABLED +- if (mcount_sort_thread) { +- void *retval = NULL; +- /* wait for mcount sort done */ +- rc = pthread_join(mcount_sort_thread, &retval); +- if (rc) { +- fprintf(stderr, +- "pthread_join failed '%s': %s\n", +- strerror(errno), fname); +- } else if (retval) { +- rc = -1; +- fprintf(stderr, +- "failed to sort mcount '%s': %s\n", +- (char *)retval, fname); +- } +- } +-#endif +- return rc; +-} +-- +2.50.1 (Apple Git-155) + diff --git a/1338-scripts-sorttable-get-start-stop-mcount-loc-from-elf-file-di.patch b/1338-scripts-sorttable-get-start-stop-mcount-loc-from-elf-file-di.patch new file mode 100644 index 000000000..12e798419 --- /dev/null +++ b/1338-scripts-sorttable-get-start-stop-mcount-loc-from-elf-file-di.patch @@ -0,0 +1,196 @@ +From 0e6fd4e629cfcaf1a4f04e2c9e39e8b5776890a2 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:36:32 +0200 +Subject: [PATCH] scripts/sorttable: Get start/stop_mcount_loc from ELF file + directly + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 4acda8edefa1ce66d3de845f1c12745721cd14c3 +Author: Steven Rostedt +Date: Sun Jan 5 11:22:25 2025 -0500 + + scripts/sorttable: Get start/stop_mcount_loc from ELF file directly + + The get_mcount_loc() does a cheesy trick to find the start_mcount_loc and + stop_mcount_loc values. That trick is: + + file_start = popen(" grep start_mcount System.map | awk '{print $1}' ", "r"); + + and + + file_stop = popen(" grep stop_mcount System.map | awk '{print $1}' ", "r"); + + Those values are stored in the Elf symbol table. Use that to capture those + values. Using the symbol table is more efficient and more robust. The + above could fail if another variable had "start_mcount" or "stop_mcount" + as part of its name. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Link: https://lore.kernel.org/20250105162346.817157047@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index ff9b60fc0dd8..656c1e9b5ad9 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -472,42 +472,41 @@ static void *sort_mcount_loc(void *arg) + } + + /* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */ +-static void get_mcount_loc(uint64_t *_start, uint64_t *_stop) ++static void get_mcount_loc(struct elf_mcount_loc *emloc, Elf_Shdr *symtab_sec, ++ const char *strtab) + { +- FILE *file_start, *file_stop; +- char start_buff[20]; +- char stop_buff[20]; +- int len = 0; ++ Elf_Sym *sym, *end_sym; ++ int symentsize = shdr_entsize(symtab_sec); ++ int found = 0; ++ ++ sym = (void *)emloc->ehdr + shdr_offset(symtab_sec); ++ end_sym = (void *)sym + shdr_size(symtab_sec); ++ ++ while (sym < end_sym) { ++ if (!strcmp(strtab + sym_name(sym), "__start_mcount_loc")) { ++ emloc->start_mcount_loc = sym_value(sym); ++ if (++found == 2) ++ break; ++ } else if (!strcmp(strtab + sym_name(sym), "__stop_mcount_loc")) { ++ emloc->stop_mcount_loc = sym_value(sym); ++ if (++found == 2) ++ break; ++ } ++ sym = (void *)sym + symentsize; ++ } + +- file_start = popen(" grep start_mcount System.map | awk '{print $1}' ", "r"); +- if (!file_start) { ++ if (!emloc->start_mcount_loc) { + fprintf(stderr, "get start_mcount_loc error!"); + return; + } + +- file_stop = popen(" grep stop_mcount System.map | awk '{print $1}' ", "r"); +- if (!file_stop) { ++ if (!emloc->stop_mcount_loc) { + fprintf(stderr, "get stop_mcount_loc error!"); +- pclose(file_start); + return; + } +- +- while (fgets(start_buff, sizeof(start_buff), file_start) != NULL) { +- len = strlen(start_buff); +- start_buff[len - 1] = '\0'; +- } +- *_start = strtoul(start_buff, NULL, 16); +- +- while (fgets(stop_buff, sizeof(stop_buff), file_stop) != NULL) { +- len = strlen(stop_buff); +- stop_buff[len - 1] = '\0'; +- } +- *_stop = strtoul(stop_buff, NULL, 16); +- +- pclose(file_start); +- pclose(file_stop); + } + #endif ++ + static int do_sort(Elf_Ehdr *ehdr, + char const *const fname, + table_sort_t custom_sort) +@@ -538,8 +537,6 @@ static int do_sort(Elf_Ehdr *ehdr, + unsigned int shstrndx; + #ifdef MCOUNT_SORT_ENABLED + struct elf_mcount_loc mstruct = {0}; +- uint64_t _start_mcount_loc = 0; +- uint64_t _stop_mcount_loc = 0; + #endif + #ifdef UNWINDER_ORC_ENABLED + unsigned int orc_ip_size = 0; +@@ -577,13 +574,8 @@ static int do_sort(Elf_Ehdr *ehdr, + + #ifdef MCOUNT_SORT_ENABLED + /* locate the .init.data section in vmlinux */ +- if (!strcmp(secstrings + idx, ".init.data")) { +- get_mcount_loc(&_start_mcount_loc, &_stop_mcount_loc); +- mstruct.ehdr = ehdr; ++ if (!strcmp(secstrings + idx, ".init.data")) + mstruct.init_data_sec = shdr; +- mstruct.start_mcount_loc = _start_mcount_loc; +- mstruct.stop_mcount_loc = _stop_mcount_loc; +- } + #endif + + #ifdef UNWINDER_ORC_ENABLED +@@ -627,23 +619,6 @@ static int do_sort(Elf_Ehdr *ehdr, + goto out; + } + #endif +- +-#ifdef MCOUNT_SORT_ENABLED +- if (!mstruct.init_data_sec || !_start_mcount_loc || !_stop_mcount_loc) { +- fprintf(stderr, +- "incomplete mcount's sort in file: %s\n", +- fname); +- goto out; +- } +- +- /* create thread to sort mcount_loc concurrently */ +- if (pthread_create(&mcount_sort_thread, NULL, &sort_mcount_loc, &mstruct)) { +- fprintf(stderr, +- "pthread_create mcount_sort_thread failed '%s': %s\n", +- strerror(errno), fname); +- goto out; +- } +-#endif + if (!extab_sec) { + fprintf(stderr, "no __ex_table in file: %s\n", fname); + goto out; +@@ -663,6 +638,26 @@ static int do_sort(Elf_Ehdr *ehdr, + strtab = (const char *)ehdr + shdr_offset(strtab_sec); + symtab = (const Elf_Sym *)((const char *)ehdr + shdr_offset(symtab_sec)); + ++#ifdef MCOUNT_SORT_ENABLED ++ mstruct.ehdr = ehdr; ++ get_mcount_loc(&mstruct, symtab_sec, strtab); ++ ++ if (!mstruct.init_data_sec || !mstruct.start_mcount_loc || !mstruct.stop_mcount_loc) { ++ fprintf(stderr, ++ "incomplete mcount's sort in file: %s\n", ++ fname); ++ goto out; ++ } ++ ++ /* create thread to sort mcount_loc concurrently */ ++ if (pthread_create(&mcount_sort_thread, NULL, &sort_mcount_loc, &mstruct)) { ++ fprintf(stderr, ++ "pthread_create mcount_sort_thread failed '%s': %s\n", ++ strerror(errno), fname); ++ goto out; ++ } ++#endif ++ + if (custom_sort) { + custom_sort(extab_image, shdr_size(extab_sec)); + } else { +-- +2.50.1 (Apple Git-155) + diff --git a/1339-scripts-sorttable-use-a-structure-of-function-pointers-for-e.patch b/1339-scripts-sorttable-use-a-structure-of-function-pointers-for-e.patch new file mode 100644 index 000000000..d865eab1c --- /dev/null +++ b/1339-scripts-sorttable-use-a-structure-of-function-pointers-for-e.patch @@ -0,0 +1,327 @@ +From 1ca873e533dd7f3f3abd7518a7c356fa7bb6ef91 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:52:42 +0200 +Subject: [PATCH] scripts/sorttable: Use a structure of function pointers for + elf helpers + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 1e5f6771c247b28135307058d2cfe3b0153733dc +Author: Steven Rostedt +Date: Fri Jan 10 07:54:59 2025 -0500 + + scripts/sorttable: Use a structure of function pointers for elf helpers + + Instead of having a series of function pointers that gets assigned to the + Elf64 or Elf32 versions, put them all into a single structure and use + that. Add the helper function that chooses the structure into the macros + that build the different versions of the elf functions. + + Link: https://lore.kernel.org/all/CAHk-=wiafEyX7UgOeZgvd6fvuByE5WXUPh9599kwOc_d-pdeug@mail.gmail.com/ + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Cc: Stephen Rothwell + Link: https://lore.kernel.org/20250110075459.13d4b94c@gandalf.local.home + Suggested-by: Linus Torvalds + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 656c1e9b5ad9..9f41575afd7a 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -85,6 +85,25 @@ static uint64_t (*r8)(const uint64_t *); + static void (*w)(uint32_t, uint32_t *); + typedef void (*table_sort_t)(char *, int); + ++static struct elf_funcs { ++ int (*compare_extable)(const void *a, const void *b); ++ uint64_t (*ehdr_shoff)(Elf_Ehdr *ehdr); ++ uint16_t (*ehdr_shstrndx)(Elf_Ehdr *ehdr); ++ uint16_t (*ehdr_shentsize)(Elf_Ehdr *ehdr); ++ uint16_t (*ehdr_shnum)(Elf_Ehdr *ehdr); ++ uint64_t (*shdr_addr)(Elf_Shdr *shdr); ++ uint64_t (*shdr_offset)(Elf_Shdr *shdr); ++ uint64_t (*shdr_size)(Elf_Shdr *shdr); ++ uint64_t (*shdr_entsize)(Elf_Shdr *shdr); ++ uint32_t (*shdr_link)(Elf_Shdr *shdr); ++ uint32_t (*shdr_name)(Elf_Shdr *shdr); ++ uint32_t (*shdr_type)(Elf_Shdr *shdr); ++ uint8_t (*sym_type)(Elf_Sym *sym); ++ uint32_t (*sym_name)(Elf_Sym *sym); ++ uint64_t (*sym_value)(Elf_Sym *sym); ++ uint16_t (*sym_shndx)(Elf_Sym *sym); ++} e; ++ + static uint64_t ehdr64_shoff(Elf_Ehdr *ehdr) + { + return r8(&ehdr->e64.e_shoff); +@@ -95,6 +114,11 @@ static uint64_t ehdr32_shoff(Elf_Ehdr *ehdr) + return r(&ehdr->e32.e_shoff); + } + ++static uint64_t ehdr_shoff(Elf_Ehdr *ehdr) ++{ ++ return e.ehdr_shoff(ehdr); ++} ++ + #define EHDR_HALF(fn_name) \ + static uint16_t ehdr64_##fn_name(Elf_Ehdr *ehdr) \ + { \ +@@ -104,6 +128,11 @@ static uint16_t ehdr64_##fn_name(Elf_Ehdr *ehdr) \ + static uint16_t ehdr32_##fn_name(Elf_Ehdr *ehdr) \ + { \ + return r2(&ehdr->e32.e_##fn_name); \ ++} \ ++ \ ++static uint16_t ehdr_##fn_name(Elf_Ehdr *ehdr) \ ++{ \ ++ return e.ehdr_##fn_name(ehdr); \ + } + + EHDR_HALF(shentsize) +@@ -119,6 +148,11 @@ static uint32_t shdr64_##fn_name(Elf_Shdr *shdr) \ + static uint32_t shdr32_##fn_name(Elf_Shdr *shdr) \ + { \ + return r(&shdr->e32.sh_##fn_name); \ ++} \ ++ \ ++static uint32_t shdr_##fn_name(Elf_Shdr *shdr) \ ++{ \ ++ return e.shdr_##fn_name(shdr); \ + } + + #define SHDR_ADDR(fn_name) \ +@@ -130,6 +164,11 @@ static uint64_t shdr64_##fn_name(Elf_Shdr *shdr) \ + static uint64_t shdr32_##fn_name(Elf_Shdr *shdr) \ + { \ + return r(&shdr->e32.sh_##fn_name); \ ++} \ ++ \ ++static uint64_t shdr_##fn_name(Elf_Shdr *shdr) \ ++{ \ ++ return e.shdr_##fn_name(shdr); \ + } + + #define SHDR_WORD(fn_name) \ +@@ -141,6 +180,10 @@ static uint32_t shdr64_##fn_name(Elf_Shdr *shdr) \ + static uint32_t shdr32_##fn_name(Elf_Shdr *shdr) \ + { \ + return r(&shdr->e32.sh_##fn_name); \ ++} \ ++static uint32_t shdr_##fn_name(Elf_Shdr *shdr) \ ++{ \ ++ return e.shdr_##fn_name(shdr); \ + } + + SHDR_ADDR(addr) +@@ -161,6 +204,11 @@ static uint64_t sym64_##fn_name(Elf_Sym *sym) \ + static uint64_t sym32_##fn_name(Elf_Sym *sym) \ + { \ + return r(&sym->e32.st_##fn_name); \ ++} \ ++ \ ++static uint64_t sym_##fn_name(Elf_Sym *sym) \ ++{ \ ++ return e.sym_##fn_name(sym); \ + } + + #define SYM_WORD(fn_name) \ +@@ -172,6 +220,11 @@ static uint32_t sym64_##fn_name(Elf_Sym *sym) \ + static uint32_t sym32_##fn_name(Elf_Sym *sym) \ + { \ + return r(&sym->e32.st_##fn_name); \ ++} \ ++ \ ++static uint32_t sym_##fn_name(Elf_Sym *sym) \ ++{ \ ++ return e.sym_##fn_name(sym); \ + } + + #define SYM_HALF(fn_name) \ +@@ -183,6 +236,11 @@ static uint16_t sym64_##fn_name(Elf_Sym *sym) \ + static uint16_t sym32_##fn_name(Elf_Sym *sym) \ + { \ + return r2(&sym->e32.st_##fn_name); \ ++} \ ++ \ ++static uint16_t sym_##fn_name(Elf_Sym *sym) \ ++{ \ ++ return e.sym_##fn_name(sym); \ + } + + static uint8_t sym64_type(Elf_Sym *sym) +@@ -195,6 +253,11 @@ static uint8_t sym32_type(Elf_Sym *sym) + return ELF32_ST_TYPE(sym->e32.st_info); + } + ++static uint8_t sym_type(Elf_Sym *sym) ++{ ++ return e.sym_type(sym); ++} ++ + SYM_ADDR(value) + SYM_WORD(name) + SYM_HALF(shndx) +@@ -322,29 +385,16 @@ static int compare_extable_64(const void *a, const void *b) + return av > bv; + } + ++static int compare_extable(const void *a, const void *b) ++{ ++ return e.compare_extable(a, b); ++} ++ + static inline void *get_index(void *start, int entsize, int index) + { + return start + (entsize * index); + } + +- +-static int (*compare_extable)(const void *a, const void *b); +-static uint64_t (*ehdr_shoff)(Elf_Ehdr *ehdr); +-static uint16_t (*ehdr_shstrndx)(Elf_Ehdr *ehdr); +-static uint16_t (*ehdr_shentsize)(Elf_Ehdr *ehdr); +-static uint16_t (*ehdr_shnum)(Elf_Ehdr *ehdr); +-static uint64_t (*shdr_addr)(Elf_Shdr *shdr); +-static uint64_t (*shdr_offset)(Elf_Shdr *shdr); +-static uint64_t (*shdr_size)(Elf_Shdr *shdr); +-static uint64_t (*shdr_entsize)(Elf_Shdr *shdr); +-static uint32_t (*shdr_link)(Elf_Shdr *shdr); +-static uint32_t (*shdr_name)(Elf_Shdr *shdr); +-static uint32_t (*shdr_type)(Elf_Shdr *shdr); +-static uint8_t (*sym_type)(Elf_Sym *sym); +-static uint32_t (*sym_name)(Elf_Sym *sym); +-static uint64_t (*sym_value)(Elf_Sym *sym); +-static uint16_t (*sym_shndx)(Elf_Sym *sym); +- + static int extable_ent_size; + static int long_size; + +@@ -864,7 +914,30 @@ static int do_file(char const *const fname, void *addr) + } + + switch (ehdr->e32.e_ident[EI_CLASS]) { +- case ELFCLASS32: ++ case ELFCLASS32: { ++ struct elf_funcs efuncs = { ++ .compare_extable = compare_extable_32, ++ .ehdr_shoff = ehdr32_shoff, ++ .ehdr_shentsize = ehdr32_shentsize, ++ .ehdr_shstrndx = ehdr32_shstrndx, ++ .ehdr_shnum = ehdr32_shnum, ++ .shdr_addr = shdr32_addr, ++ .shdr_offset = shdr32_offset, ++ .shdr_link = shdr32_link, ++ .shdr_size = shdr32_size, ++ .shdr_name = shdr32_name, ++ .shdr_type = shdr32_type, ++ .shdr_entsize = shdr32_entsize, ++ .sym_type = sym32_type, ++ .sym_name = sym32_name, ++ .sym_value = sym32_value, ++ .sym_shndx = sym32_shndx, ++ }; ++ ++ e = efuncs; ++ long_size = 4; ++ extable_ent_size = 8; ++ + if (r2(&ehdr->e32.e_ehsize) != sizeof(Elf32_Ehdr) || + r2(&ehdr->e32.e_shentsize) != sizeof(Elf32_Shdr)) { + fprintf(stderr, +@@ -872,26 +945,32 @@ static int do_file(char const *const fname, void *addr) + return -1; + } + +- compare_extable = compare_extable_32; +- ehdr_shoff = ehdr32_shoff; +- ehdr_shentsize = ehdr32_shentsize; +- ehdr_shstrndx = ehdr32_shstrndx; +- ehdr_shnum = ehdr32_shnum; +- shdr_addr = shdr32_addr; +- shdr_offset = shdr32_offset; +- shdr_link = shdr32_link; +- shdr_size = shdr32_size; +- shdr_name = shdr32_name; +- shdr_type = shdr32_type; +- shdr_entsize = shdr32_entsize; +- sym_type = sym32_type; +- sym_name = sym32_name; +- sym_value = sym32_value; +- sym_shndx = sym32_shndx; +- long_size = 4; +- extable_ent_size = 8; ++ } + break; +- case ELFCLASS64: ++ case ELFCLASS64: { ++ struct elf_funcs efuncs = { ++ .compare_extable = compare_extable_64, ++ .ehdr_shoff = ehdr64_shoff, ++ .ehdr_shentsize = ehdr64_shentsize, ++ .ehdr_shstrndx = ehdr64_shstrndx, ++ .ehdr_shnum = ehdr64_shnum, ++ .shdr_addr = shdr64_addr, ++ .shdr_offset = shdr64_offset, ++ .shdr_link = shdr64_link, ++ .shdr_size = shdr64_size, ++ .shdr_name = shdr64_name, ++ .shdr_type = shdr64_type, ++ .shdr_entsize = shdr64_entsize, ++ .sym_type = sym64_type, ++ .sym_name = sym64_name, ++ .sym_value = sym64_value, ++ .sym_shndx = sym64_shndx, ++ }; ++ ++ e = efuncs; ++ long_size = 8; ++ extable_ent_size = 16; ++ + if (r2(&ehdr->e64.e_ehsize) != sizeof(Elf64_Ehdr) || + r2(&ehdr->e64.e_shentsize) != sizeof(Elf64_Shdr)) { + fprintf(stderr, +@@ -900,25 +979,7 @@ static int do_file(char const *const fname, void *addr) + return -1; + } + +- compare_extable = compare_extable_64; +- ehdr_shoff = ehdr64_shoff; +- ehdr_shentsize = ehdr64_shentsize; +- ehdr_shstrndx = ehdr64_shstrndx; +- ehdr_shnum = ehdr64_shnum; +- shdr_addr = shdr64_addr; +- shdr_offset = shdr64_offset; +- shdr_link = shdr64_link; +- shdr_size = shdr64_size; +- shdr_name = shdr64_name; +- shdr_type = shdr64_type; +- shdr_entsize = shdr64_entsize; +- sym_type = sym64_type; +- sym_name = sym64_name; +- sym_value = sym64_value; +- sym_shndx = sym64_shndx; +- long_size = 8; +- extable_ent_size = 16; +- ++ } + break; + default: + fprintf(stderr, "unrecognized ELF class %d %s\n", +-- +2.50.1 (Apple Git-155) + diff --git a/1340-arm64-scripts-sorttable-implement-sorting-mcount-loc-at-boot.patch b/1340-arm64-scripts-sorttable-implement-sorting-mcount-loc-at-boot.patch new file mode 100644 index 000000000..92d2fdeed --- /dev/null +++ b/1340-arm64-scripts-sorttable-implement-sorting-mcount-loc-at-boot.patch @@ -0,0 +1,373 @@ +From 4b605df96d9249250134a81573caa2ab136e0ce8 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:55:45 +0200 +Subject: [PATCH] arm64: scripts/sorttable: Implement sorting mcount_loc at + boot for arm64 + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +Conflicts: Context change from missing commit a762e9267dca ("ftrace: +Add CONFIG_HAVE_FTRACE_GRAPH_FUNC") + +commit b3d09d06e052e1d754645acea4e4d1e96f81c934 +Author: Steven Rostedt +Date: Tue Feb 18 14:59:19 2025 -0500 + + arm64: scripts/sorttable: Implement sorting mcount_loc at boot for arm64 + + The mcount_loc section holds the addresses of the functions that get + patched by ftrace when enabling function callbacks. It can contain tens of + thousands of entries. These addresses must be sorted. If they are not + sorted at compile time, they are sorted at boot. Sorting at boot does take + some time and does have a small impact on boot performance. + + x86 and arm32 have the addresses in the mcount_loc section of the ELF + file. But for arm64, the section just contains zeros. The .rela.dyn + Elf_Rela section holds the addresses and they get patched at boot during + the relocation phase. + + In order to sort these addresses, the Elf_Rela needs to be updated instead + of the location in the binary that holds the mcount_loc section. Have the + sorttable code, allocate an array to hold the functions, load the + addresses from the Elf_Rela entries, sort them, then put them back in + order into the Elf_rela entries so that they will be sorted at boot up + without having to sort them during boot up. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Cc: Heiko Carstens + Cc: Will Deacon + Cc: Vasily Gorbik + Cc: Alexander Gordeev + Link: https://lore.kernel.org/20250218200022.373319428@goodmis.org + Acked-by: Catalin Marinas + Signed-off-by: Steven Rostedt (Google) + +(cherry picked from commit b3d09d06e052e1d754645acea4e4d1e96f81c934) +Assisted-by: Patchpal +Signed-off-by: Jerome Marchand + +diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig +index 534daec407ef..add6005c5e8b 100644 +--- a/arch/arm64/Kconfig ++++ b/arch/arm64/Kconfig +@@ -219,6 +219,7 @@ config ARM64 + if DYNAMIC_FTRACE_WITH_ARGS + select HAVE_SAMPLE_FTRACE_DIRECT + select HAVE_SAMPLE_FTRACE_DIRECT_MULTI ++ select HAVE_BUILDTIME_MCOUNT_SORT + select HAVE_EFFICIENT_UNALIGNED_ACCESS + select HAVE_GUP_FAST + select HAVE_FTRACE_MCOUNT_RECORD +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 9f41575afd7a..4a34c275123e 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -79,10 +80,16 @@ typedef union { + Elf64_Sym e64; + } Elf_Sym; + ++typedef union { ++ Elf32_Rela e32; ++ Elf64_Rela e64; ++} Elf_Rela; ++ + static uint32_t (*r)(const uint32_t *); + static uint16_t (*r2)(const uint16_t *); + static uint64_t (*r8)(const uint64_t *); + static void (*w)(uint32_t, uint32_t *); ++static void (*w8)(uint64_t, uint64_t *); + typedef void (*table_sort_t)(char *, int); + + static struct elf_funcs { +@@ -102,6 +109,10 @@ static struct elf_funcs { + uint32_t (*sym_name)(Elf_Sym *sym); + uint64_t (*sym_value)(Elf_Sym *sym); + uint16_t (*sym_shndx)(Elf_Sym *sym); ++ uint64_t (*rela_offset)(Elf_Rela *rela); ++ uint64_t (*rela_info)(Elf_Rela *rela); ++ uint64_t (*rela_addend)(Elf_Rela *rela); ++ void (*rela_write_addend)(Elf_Rela *rela, uint64_t val); + } e; + + static uint64_t ehdr64_shoff(Elf_Ehdr *ehdr) +@@ -262,6 +273,38 @@ SYM_ADDR(value) + SYM_WORD(name) + SYM_HALF(shndx) + ++#define __maybe_unused __attribute__((__unused__)) ++ ++#define RELA_ADDR(fn_name) \ ++static uint64_t rela64_##fn_name(Elf_Rela *rela) \ ++{ \ ++ return r8((uint64_t *)&rela->e64.r_##fn_name); \ ++} \ ++ \ ++static uint64_t rela32_##fn_name(Elf_Rela *rela) \ ++{ \ ++ return r((uint32_t *)&rela->e32.r_##fn_name); \ ++} \ ++ \ ++static uint64_t __maybe_unused rela_##fn_name(Elf_Rela *rela) \ ++{ \ ++ return e.rela_##fn_name(rela); \ ++} ++ ++RELA_ADDR(offset) ++RELA_ADDR(info) ++RELA_ADDR(addend) ++ ++static void rela64_write_addend(Elf_Rela *rela, uint64_t val) ++{ ++ w8(val, (uint64_t *)&rela->e64.r_addend); ++} ++ ++static void rela32_write_addend(Elf_Rela *rela, uint64_t val) ++{ ++ w(val, (uint32_t *)&rela->e32.r_addend); ++} ++ + /* + * Get the whole file as a programming convenience in order to avoid + * malloc+lseek+read+free of many pieces. If successful, then mmap +@@ -341,6 +384,16 @@ static void wle(uint32_t val, uint32_t *x) + put_unaligned_le32(val, x); + } + ++static void w8be(uint64_t val, uint64_t *x) ++{ ++ put_unaligned_be64(val, x); ++} ++ ++static void w8le(uint64_t val, uint64_t *x) ++{ ++ put_unaligned_le64(val, x); ++} ++ + /* + * Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of + * the way to -256..-1, to avoid conflicting with real section +@@ -398,13 +451,12 @@ static inline void *get_index(void *start, int entsize, int index) + static int extable_ent_size; + static int long_size; + ++#define ERRSTR_MAXSZ 256 + + #ifdef UNWINDER_ORC_ENABLED + /* ORC unwinder only support X86_64 */ + #include + +-#define ERRSTR_MAXSZ 256 +- + static char g_err[ERRSTR_MAXSZ]; + static int *g_orc_ip_table; + static struct orc_entry *g_orc_table; +@@ -499,7 +551,19 @@ static void *sort_orctable(void *arg) + #endif + + #ifdef MCOUNT_SORT_ENABLED ++ ++/* Only used for sorting mcount table */ ++static void rela_write_addend(Elf_Rela *rela, uint64_t val) ++{ ++ e.rela_write_addend(rela, val); ++} ++ + static pthread_t mcount_sort_thread; ++static bool sort_reloc; ++ ++static long rela_type; ++ ++static char m_err[ERRSTR_MAXSZ]; + + struct elf_mcount_loc { + Elf_Ehdr *ehdr; +@@ -508,6 +572,103 @@ struct elf_mcount_loc { + uint64_t stop_mcount_loc; + }; + ++/* Sort the relocations not the address itself */ ++static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size) ++{ ++ Elf_Shdr *shdr_start; ++ Elf_Rela *rel; ++ unsigned int shnum; ++ unsigned int count; ++ int shentsize; ++ void *vals; ++ void *ptr; ++ ++ shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr)); ++ shentsize = ehdr_shentsize(ehdr); ++ ++ vals = malloc(long_size * size); ++ if (!vals) { ++ snprintf(m_err, ERRSTR_MAXSZ, "Failed to allocate sort array"); ++ pthread_exit(m_err); ++ return NULL; ++ } ++ ++ ptr = vals; ++ ++ shnum = ehdr_shnum(ehdr); ++ if (shnum == SHN_UNDEF) ++ shnum = shdr_size(shdr_start); ++ ++ for (int i = 0; i < shnum; i++) { ++ Elf_Shdr *shdr = get_index(shdr_start, shentsize, i); ++ void *end; ++ ++ if (shdr_type(shdr) != SHT_RELA) ++ continue; ++ ++ rel = (void *)ehdr + shdr_offset(shdr); ++ end = (void *)rel + shdr_size(shdr); ++ ++ for (; (void *)rel < end; rel = (void *)rel + shdr_entsize(shdr)) { ++ uint64_t offset = rela_offset(rel); ++ ++ if (offset >= start_loc && offset < start_loc + size) { ++ if (ptr + long_size > vals + size) { ++ free(vals); ++ snprintf(m_err, ERRSTR_MAXSZ, ++ "Too many relocations"); ++ pthread_exit(m_err); ++ return NULL; ++ } ++ ++ /* Make sure this has the correct type */ ++ if (rela_info(rel) != rela_type) { ++ free(vals); ++ snprintf(m_err, ERRSTR_MAXSZ, ++ "rela has type %lx but expected %lx\n", ++ (long)rela_info(rel), rela_type); ++ pthread_exit(m_err); ++ return NULL; ++ } ++ ++ if (long_size == 4) ++ *(uint32_t *)ptr = rela_addend(rel); ++ else ++ *(uint64_t *)ptr = rela_addend(rel); ++ ptr += long_size; ++ } ++ } ++ } ++ count = ptr - vals; ++ qsort(vals, count / long_size, long_size, compare_extable); ++ ++ ptr = vals; ++ for (int i = 0; i < shnum; i++) { ++ Elf_Shdr *shdr = get_index(shdr_start, shentsize, i); ++ void *end; ++ ++ if (shdr_type(shdr) != SHT_RELA) ++ continue; ++ ++ rel = (void *)ehdr + shdr_offset(shdr); ++ end = (void *)rel + shdr_size(shdr); ++ ++ for (; (void *)rel < end; rel = (void *)rel + shdr_entsize(shdr)) { ++ uint64_t offset = rela_offset(rel); ++ ++ if (offset >= start_loc && offset < start_loc + size) { ++ if (long_size == 4) ++ rela_write_addend(rel, *(uint32_t *)ptr); ++ else ++ rela_write_addend(rel, *(uint64_t *)ptr); ++ ptr += long_size; ++ } ++ } ++ } ++ free(vals); ++ return NULL; ++} ++ + /* Sort the addresses stored between __start_mcount_loc to __stop_mcount_loc in vmlinux */ + static void *sort_mcount_loc(void *arg) + { +@@ -517,6 +678,9 @@ static void *sort_mcount_loc(void *arg) + uint64_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc; + unsigned char *start_loc = (void *)emloc->ehdr + offset; + ++ if (sort_reloc) ++ return sort_relocs(emloc->ehdr, emloc->start_mcount_loc, count); ++ + qsort(start_loc, count/long_size, long_size, compare_extable); + return NULL; + } +@@ -866,12 +1030,14 @@ static int do_file(char const *const fname, void *addr) + r2 = r2le; + r8 = r8le; + w = wle; ++ w8 = w8le; + break; + case ELFDATA2MSB: + r = rbe; + r2 = r2be; + r8 = r8be; + w = wbe; ++ w8 = w8be; + break; + default: + fprintf(stderr, "unrecognized ELF data encoding %d: %s\n", +@@ -887,8 +1053,13 @@ static int do_file(char const *const fname, void *addr) + } + + switch (r2(&ehdr->e32.e_machine)) { +- case EM_386: + case EM_AARCH64: ++#ifdef MCOUNT_SORT_ENABLED ++ sort_reloc = true; ++ rela_type = 0x403; ++#endif ++ /* fallthrough */ ++ case EM_386: + case EM_LOONGARCH: + case EM_RISCV: + case EM_S390: +@@ -932,6 +1103,10 @@ static int do_file(char const *const fname, void *addr) + .sym_name = sym32_name, + .sym_value = sym32_value, + .sym_shndx = sym32_shndx, ++ .rela_offset = rela32_offset, ++ .rela_info = rela32_info, ++ .rela_addend = rela32_addend, ++ .rela_write_addend = rela32_write_addend, + }; + + e = efuncs; +@@ -965,6 +1140,10 @@ static int do_file(char const *const fname, void *addr) + .sym_name = sym64_name, + .sym_value = sym64_value, + .sym_shndx = sym64_shndx, ++ .rela_offset = rela64_offset, ++ .rela_info = rela64_info, ++ .rela_addend = rela64_addend, ++ .rela_write_addend = rela64_write_addend, + }; + + e = efuncs; +-- +2.50.1 (Apple Git-155) + diff --git a/1341-scripts-sorttable-have-mcount-rela-sort-use-direct-values.patch b/1341-scripts-sorttable-have-mcount-rela-sort-use-direct-values.patch new file mode 100644 index 000000000..54846d32e --- /dev/null +++ b/1341-scripts-sorttable-have-mcount-rela-sort-use-direct-values.patch @@ -0,0 +1,103 @@ +From 93c8c28c2ce439d598d5fd49d10a4bd5cb0401ea Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:57:27 +0200 +Subject: [PATCH] scripts/sorttable: Have mcount rela sort use direct values + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit a0265659322540d656727b9e132edfb6f06b6c1a +Author: Steven Rostedt +Date: Tue Feb 18 14:59:20 2025 -0500 + + scripts/sorttable: Have mcount rela sort use direct values + + The mcount_loc sorting for when the values are stored in the Elf_Rela + entries uses the compare_extable() function to do the compares in the + qsort(). That function does handle byte swapping if the machine being + compiled for is a different endian than the host machine. But the + sort_relocs() function sorts an array that pulled in the values from the + Elf_Rela section and has already done the swapping. + + Create two new compare functions that will sort the direct values. One + will sort 32 bit values and the other will sort the 64 bit value. One of + these will be assigned to a compare_values function pointer and that will + be used for sorting the Elf_Rela mcount values. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Cc: Heiko Carstens + Cc: Catalin Marinas + Cc: Will Deacon + Cc: Vasily Gorbik + Cc: Alexander Gordeev + Link: https://lore.kernel.org/20250218200022.538888594@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 4a34c275123e..f62a91d8af0a 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -552,6 +552,28 @@ static void *sort_orctable(void *arg) + + #ifdef MCOUNT_SORT_ENABLED + ++static int compare_values_64(const void *a, const void *b) ++{ ++ uint64_t av = *(uint64_t *)a; ++ uint64_t bv = *(uint64_t *)b; ++ ++ if (av < bv) ++ return -1; ++ return av > bv; ++} ++ ++static int compare_values_32(const void *a, const void *b) ++{ ++ uint32_t av = *(uint32_t *)a; ++ uint32_t bv = *(uint32_t *)b; ++ ++ if (av < bv) ++ return -1; ++ return av > bv; ++} ++ ++static int (*compare_values)(const void *a, const void *b); ++ + /* Only used for sorting mcount table */ + static void rela_write_addend(Elf_Rela *rela, uint64_t val) + { +@@ -583,6 +605,8 @@ static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size) + void *vals; + void *ptr; + ++ compare_values = long_size == 4 ? compare_values_32 : compare_values_64; ++ + shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr)); + shentsize = ehdr_shentsize(ehdr); + +@@ -640,7 +664,7 @@ static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size) + } + } + count = ptr - vals; +- qsort(vals, count / long_size, long_size, compare_extable); ++ qsort(vals, count / long_size, long_size, compare_values); + + ptr = vals; + for (int i = 0; i < shnum; i++) { +-- +2.50.1 (Apple Git-155) + diff --git a/1342-scripts-sorttable-always-use-an-array-for-the-mcount-loc-sor.patch b/1342-scripts-sorttable-always-use-an-array-for-the-mcount-loc-sor.patch new file mode 100644 index 000000000..a7ac286da --- /dev/null +++ b/1342-scripts-sorttable-always-use-an-array-for-the-mcount-loc-sor.patch @@ -0,0 +1,239 @@ +From d39567e1f737a3f1959b0597eeeba7bdf25d0249 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:57:34 +0200 +Subject: [PATCH] scripts/sorttable: Always use an array for the mcount_loc + sorting + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 5fb964f5ba53afda0e2b6dbc00b8205461ffe04a +Author: Steven Rostedt +Date: Tue Feb 18 14:59:21 2025 -0500 + + scripts/sorttable: Always use an array for the mcount_loc sorting + + The sorting of the mcount_loc section is done directly to the section for + x86 and arm32 but it uses a separate array for arm64 as arm64 has the + values for the mcount_loc stored in the rela sections of the vmlinux ELF + file. + + In order to use the same code to remove weak functions, always use a + separate array to do the sorting. This requires splitting up the filling + of the array into one function and the placing the contents of the array + back into the rela sections or into the mcount_loc section into a separate + file. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Cc: Heiko Carstens + Cc: Catalin Marinas + Cc: Will Deacon + Cc: Vasily Gorbik + Cc: Alexander Gordeev + Link: https://lore.kernel.org/20250218200022.710676551@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index f62a91d8af0a..ec02a2852efb 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -594,31 +594,19 @@ struct elf_mcount_loc { + uint64_t stop_mcount_loc; + }; + +-/* Sort the relocations not the address itself */ +-static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size) ++/* Fill the array with the content of the relocs */ ++static int fill_relocs(void *ptr, uint64_t size, Elf_Ehdr *ehdr, uint64_t start_loc) + { + Elf_Shdr *shdr_start; + Elf_Rela *rel; + unsigned int shnum; +- unsigned int count; ++ unsigned int count = 0; + int shentsize; +- void *vals; +- void *ptr; +- +- compare_values = long_size == 4 ? compare_values_32 : compare_values_64; ++ void *array_end = ptr + size; + + shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr)); + shentsize = ehdr_shentsize(ehdr); + +- vals = malloc(long_size * size); +- if (!vals) { +- snprintf(m_err, ERRSTR_MAXSZ, "Failed to allocate sort array"); +- pthread_exit(m_err); +- return NULL; +- } +- +- ptr = vals; +- + shnum = ehdr_shnum(ehdr); + if (shnum == SHN_UNDEF) + shnum = shdr_size(shdr_start); +@@ -637,22 +625,18 @@ static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size) + uint64_t offset = rela_offset(rel); + + if (offset >= start_loc && offset < start_loc + size) { +- if (ptr + long_size > vals + size) { +- free(vals); ++ if (ptr + long_size > array_end) { + snprintf(m_err, ERRSTR_MAXSZ, + "Too many relocations"); +- pthread_exit(m_err); +- return NULL; ++ return -1; + } + + /* Make sure this has the correct type */ + if (rela_info(rel) != rela_type) { +- free(vals); + snprintf(m_err, ERRSTR_MAXSZ, + "rela has type %lx but expected %lx\n", + (long)rela_info(rel), rela_type); +- pthread_exit(m_err); +- return NULL; ++ return -1; + } + + if (long_size == 4) +@@ -660,13 +644,28 @@ static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size) + else + *(uint64_t *)ptr = rela_addend(rel); + ptr += long_size; ++ count++; + } + } + } +- count = ptr - vals; +- qsort(vals, count / long_size, long_size, compare_values); ++ return count; ++} ++ ++/* Put the sorted vals back into the relocation elements */ ++static void replace_relocs(void *ptr, uint64_t size, Elf_Ehdr *ehdr, uint64_t start_loc) ++{ ++ Elf_Shdr *shdr_start; ++ Elf_Rela *rel; ++ unsigned int shnum; ++ int shentsize; ++ ++ shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr)); ++ shentsize = ehdr_shentsize(ehdr); ++ ++ shnum = ehdr_shnum(ehdr); ++ if (shnum == SHN_UNDEF) ++ shnum = shdr_size(shdr_start); + +- ptr = vals; + for (int i = 0; i < shnum; i++) { + Elf_Shdr *shdr = get_index(shdr_start, shentsize, i); + void *end; +@@ -689,8 +688,32 @@ static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size) + } + } + } +- free(vals); +- return NULL; ++} ++ ++static int fill_addrs(void *ptr, uint64_t size, void *addrs) ++{ ++ void *end = ptr + size; ++ int count = 0; ++ ++ for (; ptr < end; ptr += long_size, addrs += long_size, count++) { ++ if (long_size == 4) ++ *(uint32_t *)ptr = r(addrs); ++ else ++ *(uint64_t *)ptr = r8(addrs); ++ } ++ return count; ++} ++ ++static void replace_addrs(void *ptr, uint64_t size, void *addrs) ++{ ++ void *end = ptr + size; ++ ++ for (; ptr < end; ptr += long_size, addrs += long_size) { ++ if (long_size == 4) ++ w(*(uint32_t *)ptr, addrs); ++ else ++ w8(*(uint64_t *)ptr, addrs); ++ } + } + + /* Sort the addresses stored between __start_mcount_loc to __stop_mcount_loc in vmlinux */ +@@ -699,14 +722,49 @@ static void *sort_mcount_loc(void *arg) + struct elf_mcount_loc *emloc = (struct elf_mcount_loc *)arg; + uint64_t offset = emloc->start_mcount_loc - shdr_addr(emloc->init_data_sec) + + shdr_offset(emloc->init_data_sec); +- uint64_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc; ++ uint64_t size = emloc->stop_mcount_loc - emloc->start_mcount_loc; + unsigned char *start_loc = (void *)emloc->ehdr + offset; ++ Elf_Ehdr *ehdr = emloc->ehdr; ++ void *e_msg = NULL; ++ void *vals; ++ int count; ++ ++ vals = malloc(long_size * size); ++ if (!vals) { ++ snprintf(m_err, ERRSTR_MAXSZ, "Failed to allocate sort array"); ++ pthread_exit(m_err); ++ } + + if (sort_reloc) +- return sort_relocs(emloc->ehdr, emloc->start_mcount_loc, count); ++ count = fill_relocs(vals, size, ehdr, emloc->start_mcount_loc); ++ else ++ count = fill_addrs(vals, size, start_loc); ++ ++ if (count < 0) { ++ e_msg = m_err; ++ goto out; ++ } ++ ++ if (count != size / long_size) { ++ snprintf(m_err, ERRSTR_MAXSZ, "Expected %u mcount elements but found %u\n", ++ (int)(size / long_size), count); ++ e_msg = m_err; ++ goto out; ++ } ++ ++ compare_values = long_size == 4 ? compare_values_32 : compare_values_64; ++ ++ qsort(vals, count, long_size, compare_values); ++ ++ if (sort_reloc) ++ replace_relocs(vals, size, ehdr, emloc->start_mcount_loc); ++ else ++ replace_addrs(vals, size, start_loc); ++ ++out: ++ free(vals); + +- qsort(start_loc, count/long_size, long_size, compare_extable); +- return NULL; ++ pthread_exit(e_msg); + } + + /* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */ +-- +2.50.1 (Apple Git-155) + diff --git a/1343-scripts-sorttable-zero-out-weak-functions-in-mcount-loc-tabl.patch b/1343-scripts-sorttable-zero-out-weak-functions-in-mcount-loc-tabl.patch new file mode 100644 index 000000000..f681ccbb4 --- /dev/null +++ b/1343-scripts-sorttable-zero-out-weak-functions-in-mcount-loc-tabl.patch @@ -0,0 +1,316 @@ +From 8cde64d5091653560e13e7cd34a385258372db58 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 1 Jun 2026 15:57:38 +0200 +Subject: [PATCH] scripts/sorttable: Zero out weak functions in mcount_loc + table + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit ef378c3b8233855497a414b9d67bf22592c928a4 +Author: Steven Rostedt +Date: Tue Feb 18 14:59:22 2025 -0500 + + scripts/sorttable: Zero out weak functions in mcount_loc table + + When a function is annotated as "weak" and is overridden, the code is not + removed. If it is traced, the fentry/mcount location in the weak function + will be referenced by the "__mcount_loc" section. This will then be added + to the available_filter_functions list. Since only the address of the + functions are listed, to find the name to show, a search of kallsyms is + used. + + Since kallsyms will return the function by simply finding the function + that the address is after but before the next function, an address of a + weak function will show up as the function before it. This is because + kallsyms does not save names of weak functions. This has caused issues in + the past, as now the traced weak function will be listed in + available_filter_functions with the name of the function before it. + + At best, this will cause the previous function's name to be listed twice. + At worse, if the previous function was marked notrace, it will now show up + as a function that can be traced. Note that it only shows up that it can + be traced but will not be if enabled, which causes confusion. + + https://lore.kernel.org/all/20220412094923.0abe90955e5db486b7bca279@kernel.org/ + + The commit b39181f7c6907 ("ftrace: Add FTRACE_MCOUNT_MAX_OFFSET to avoid + adding weak function") was a workaround to this by checking the function + address before printing its name. If the address was too far from the + function given by the name then instead of printing the name it would + print: __ftrace_invalid_address___ + + The real issue is that these invalid addresses are listed in the ftrace + table look up which available_filter_functions is derived from. A place + holder must be listed in that file because set_ftrace_filter may take a + series of indexes into that file instead of names to be able to do O(1) + lookups to enable filtering (many tools use this method). + + Even if kallsyms saved the size of the function, it does not remove the + need of having these place holders. The real solution is to not add a weak + function into the ftrace table in the first place. + + To solve this, the sorttable.c code that sorts the mcount regions during + the build is modified to take a "nm -S vmlinux" input, sort it, and any + function listed in the mcount_loc section that is not within a boundary of + the function list given by nm is considered a weak function and is zeroed + out. + + Note, this does not mean they will remain zero when booting as KASLR + will still shift those addresses. To handle this, the entries in the + mcount_loc section will be ignored if they are zero or match the + kaslr_offset() value. + + Before: + + ~# grep __ftrace_invalid_address___ /sys/kernel/tracing/available_filter_functions | wc -l + 551 + + After: + + ~# grep __ftrace_invalid_address___ /sys/kernel/tracing/available_filter_functions | wc -l + 0 + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Cc: Heiko Carstens + Cc: Catalin Marinas + Cc: Will Deacon + Cc: Vasily Gorbik + Cc: Alexander Gordeev + Link: https://lore.kernel.org/20250218200022.883095980@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c +index 57b8fad660cf..792c57d1f0b9 100644 +--- a/kernel/trace/ftrace.c ++++ b/kernel/trace/ftrace.c +@@ -7052,6 +7052,7 @@ static int ftrace_process_locs(struct module *mod, + unsigned long count; + unsigned long *p; + unsigned long addr; ++ unsigned long kaslr; + unsigned long flags = 0; /* Shut up gcc */ + int ret = -ENOMEM; + +@@ -7100,6 +7101,9 @@ static int ftrace_process_locs(struct module *mod, + ftrace_pages->next = start_pg; + } + ++ /* For zeroed locations that were shifted for core kernel */ ++ kaslr = !mod ? kaslr_offset() : 0; ++ + p = start; + pg = start_pg; + while (p < end) { +@@ -7111,7 +7115,7 @@ static int ftrace_process_locs(struct module *mod, + * object files to satisfy alignments. + * Skip any NULL pointers. + */ +- if (!addr) { ++ if (!addr || addr == kaslr) { + skipped++; + continue; + } +diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh +index cdf16029fc58..8619ae612997 100755 +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -174,12 +174,14 @@ mksysmap() + + sorttable() + { +- ${objtree}/scripts/sorttable ${1} ++ ${NM} -S ${1} > .tmp_vmlinux.nm-sort ++ ${objtree}/scripts/sorttable -s .tmp_vmlinux.nm-sort ${1} + } + + cleanup() + { + rm -f .btf.* ++ rm -f .tmp_vmlinux.nm-sort + rm -f System.map + rm -f vmlinux + rm -f vmlinux.map +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index ec02a2852efb..23c7e0e6c024 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -580,6 +580,98 @@ static void rela_write_addend(Elf_Rela *rela, uint64_t val) + e.rela_write_addend(rela, val); + } + ++struct func_info { ++ uint64_t addr; ++ uint64_t size; ++}; ++ ++/* List of functions created by: nm -S vmlinux */ ++static struct func_info *function_list; ++static int function_list_size; ++ ++/* Allocate functions in 1k blocks */ ++#define FUNC_BLK_SIZE 1024 ++#define FUNC_BLK_MASK (FUNC_BLK_SIZE - 1) ++ ++static int add_field(uint64_t addr, uint64_t size) ++{ ++ struct func_info *fi; ++ int fsize = function_list_size; ++ ++ if (!(fsize & FUNC_BLK_MASK)) { ++ fsize += FUNC_BLK_SIZE; ++ fi = realloc(function_list, fsize * sizeof(struct func_info)); ++ if (!fi) ++ return -1; ++ function_list = fi; ++ } ++ fi = &function_list[function_list_size++]; ++ fi->addr = addr; ++ fi->size = size; ++ return 0; ++} ++ ++/* Only return match if the address lies inside the function size */ ++static int cmp_func_addr(const void *K, const void *A) ++{ ++ uint64_t key = *(const uint64_t *)K; ++ const struct func_info *a = A; ++ ++ if (key < a->addr) ++ return -1; ++ return key >= a->addr + a->size; ++} ++ ++/* Find the function in function list that is bounded by the function size */ ++static int find_func(uint64_t key) ++{ ++ return bsearch(&key, function_list, function_list_size, ++ sizeof(struct func_info), cmp_func_addr) != NULL; ++} ++ ++static int cmp_funcs(const void *A, const void *B) ++{ ++ const struct func_info *a = A; ++ const struct func_info *b = B; ++ ++ if (a->addr < b->addr) ++ return -1; ++ return a->addr > b->addr; ++} ++ ++static int parse_symbols(const char *fname) ++{ ++ FILE *fp; ++ char addr_str[20]; /* Only need 17, but round up to next int size */ ++ char size_str[20]; ++ char type; ++ ++ fp = fopen(fname, "r"); ++ if (!fp) { ++ perror(fname); ++ return -1; ++ } ++ ++ while (fscanf(fp, "%16s %16s %c %*s\n", addr_str, size_str, &type) == 3) { ++ uint64_t addr; ++ uint64_t size; ++ ++ /* Only care about functions */ ++ if (type != 't' && type != 'T' && type != 'W') ++ continue; ++ ++ addr = strtoull(addr_str, NULL, 16); ++ size = strtoull(size_str, NULL, 16); ++ if (add_field(addr, size) < 0) ++ return -1; ++ } ++ fclose(fp); ++ ++ qsort(function_list, function_list_size, sizeof(struct func_info), cmp_funcs); ++ ++ return 0; ++} ++ + static pthread_t mcount_sort_thread; + static bool sort_reloc; + +@@ -752,6 +844,21 @@ static void *sort_mcount_loc(void *arg) + goto out; + } + ++ /* zero out any locations not found by function list */ ++ if (function_list_size) { ++ for (void *ptr = vals; ptr < vals + size; ptr += long_size) { ++ uint64_t key; ++ ++ key = long_size == 4 ? r((uint32_t *)ptr) : r8((uint64_t *)ptr); ++ if (!find_func(key)) { ++ if (long_size == 4) ++ *(uint32_t *)ptr = 0; ++ else ++ *(uint64_t *)ptr = 0; ++ } ++ } ++ } ++ + compare_values = long_size == 4 ? compare_values_32 : compare_values_64; + + qsort(vals, count, long_size, compare_values); +@@ -801,6 +908,8 @@ static void get_mcount_loc(struct elf_mcount_loc *emloc, Elf_Shdr *symtab_sec, + return; + } + } ++#else /* MCOUNT_SORT_ENABLED */ ++static inline int parse_symbols(const char *fname) { return 0; } + #endif + + static int do_sort(Elf_Ehdr *ehdr, +@@ -1256,14 +1365,29 @@ int main(int argc, char *argv[]) + int i, n_error = 0; /* gcc-4.3.0 false positive complaint */ + size_t size = 0; + void *addr = NULL; ++ int c; ++ ++ while ((c = getopt(argc, argv, "s:")) >= 0) { ++ switch (c) { ++ case 's': ++ if (parse_symbols(optarg) < 0) { ++ fprintf(stderr, "Could not parse %s\n", optarg); ++ return -1; ++ } ++ break; ++ default: ++ fprintf(stderr, "usage: sorttable [-s nm-file] vmlinux...\n"); ++ return 0; ++ } ++ } + +- if (argc < 2) { ++ if ((argc - optind) < 1) { + fprintf(stderr, "usage: sorttable vmlinux...\n"); + return 0; + } + + /* Process each file in turn, allowing deep failure. */ +- for (i = 1; i < argc; i++) { ++ for (i = optind; i < argc; i++) { + addr = mmap_file(argv[i], &size); + if (!addr) { + ++n_error; +-- +2.50.1 (Apple Git-155) + diff --git a/1344-ftrace-update-the-mcount-loc-check-of-skipped-entries.patch b/1344-ftrace-update-the-mcount-loc-check-of-skipped-entries.patch new file mode 100644 index 000000000..810e50aac --- /dev/null +++ b/1344-ftrace-update-the-mcount-loc-check-of-skipped-entries.patch @@ -0,0 +1,78 @@ +From fb85259106c9afdbf7808aecc128d41aacab22ea Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Tue, 2 Jun 2026 15:40:50 +0200 +Subject: [PATCH] ftrace: Update the mcount_loc check of skipped entries + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 4a3efc6baff931da9a85c6d2e42c87bd9a827399 +Author: Steven Rostedt +Date: Tue Feb 18 14:59:23 2025 -0500 + + ftrace: Update the mcount_loc check of skipped entries + + Now that weak functions turn into skipped entries, update the check to + make sure the amount that was allocated would fit both the entries that + were allocated as well as those that were skipped. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Cc: Heiko Carstens + Cc: Catalin Marinas + Cc: Will Deacon + Cc: Vasily Gorbik + Cc: Alexander Gordeev + Link: https://lore.kernel.org/20250218200023.055162048@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c +index 792c57d1f0b9..4956ad9f9a20 100644 +--- a/kernel/trace/ftrace.c ++++ b/kernel/trace/ftrace.c +@@ -7159,7 +7159,28 @@ static int ftrace_process_locs(struct module *mod, + + /* We should have used all pages unless we skipped some */ + if (pg_unuse) { +- WARN_ON(!skipped); ++ unsigned long pg_remaining, remaining = 0; ++ unsigned long skip; ++ ++ /* Count the number of entries unused and compare it to skipped. */ ++ pg_remaining = (ENTRIES_PER_PAGE << pg->order) - pg->index; ++ ++ if (!WARN(skipped < pg_remaining, "Extra allocated pages for ftrace")) { ++ ++ skip = skipped - pg_remaining; ++ ++ for (pg = pg_unuse; pg; pg = pg->next) ++ remaining += 1 << pg->order; ++ ++ skip = DIV_ROUND_UP(skip, ENTRIES_PER_PAGE); ++ ++ /* ++ * Check to see if the number of pages remaining would ++ * just fit the number of entries skipped. ++ */ ++ WARN(skip != remaining, "Extra allocated pages for ftrace: %lu with %lu skipped", ++ remaining, skipped); ++ } + /* Need to synchronize with ftrace_location_range() */ + synchronize_rcu(); + ftrace_free_pages(pg_unuse); +-- +2.50.1 (Apple Git-155) + diff --git a/1345-ftrace-have-ftrace-pages-output-reflect-freed-pages.patch b/1345-ftrace-have-ftrace-pages-output-reflect-freed-pages.patch new file mode 100644 index 000000000..0f6099acd --- /dev/null +++ b/1345-ftrace-have-ftrace-pages-output-reflect-freed-pages.patch @@ -0,0 +1,115 @@ +From d705caa45a6f9061420687f3d7d86f28e35881e2 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Tue, 2 Jun 2026 15:40:54 +0200 +Subject: [PATCH] ftrace: Have ftrace pages output reflect freed pages + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 264143c4e54412095f4b615e65bf736fc3c60af0 +Author: Steven Rostedt +Date: Tue Feb 18 14:59:24 2025 -0500 + + ftrace: Have ftrace pages output reflect freed pages + + The amount of memory that ftrace uses to save the descriptors to manage + the functions it can trace is shown at output. But if there are a lot of + functions that are skipped because they were weak or the architecture + added holes into the tables, then the extra pages that were allocated are + freed. But these freed pages are not reflected in the numbers shown, and + they can even be inconsistent with what is reported: + + ftrace: allocating 57482 entries in 225 pages + ftrace: allocated 224 pages with 3 groups + + The above shows the number of original entries that are in the mcount_loc + section and the pages needed to save them (225), but the second output + reflects the number of pages that were actually used. The two should be + consistent as: + + ftrace: allocating 56739 entries in 224 pages + ftrace: allocated 224 pages with 3 groups + + The above also shows the accurate number of entires that were actually + stored and does not include the entries that were removed. + + Cc: bpf + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Peter Zijlstra + Cc: Linus Torvalds + Cc: Masahiro Yamada + Cc: Nathan Chancellor + Cc: Nicolas Schier + Cc: Zheng Yejian + Cc: Martin Kelly + Cc: Christophe Leroy + Cc: Josh Poimboeuf + Cc: Heiko Carstens + Cc: Catalin Marinas + Cc: Will Deacon + Cc: Vasily Gorbik + Cc: Alexander Gordeev + Link: https://lore.kernel.org/20250218200023.221100846@goodmis.org + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c +index 4956ad9f9a20..5e131970a70e 100644 +--- a/kernel/trace/ftrace.c ++++ b/kernel/trace/ftrace.c +@@ -7054,6 +7054,7 @@ static int ftrace_process_locs(struct module *mod, + unsigned long addr; + unsigned long kaslr; + unsigned long flags = 0; /* Shut up gcc */ ++ unsigned long pages; + int ret = -ENOMEM; + + count = end - start; +@@ -7061,6 +7062,8 @@ static int ftrace_process_locs(struct module *mod, + if (!count) + return 0; + ++ pages = DIV_ROUND_UP(count, ENTRIES_PER_PAGE); ++ + /* + * Sorting mcount in vmlinux at build time depend on + * CONFIG_BUILDTIME_MCOUNT_SORT, while mcount loc in +@@ -7172,6 +7175,8 @@ static int ftrace_process_locs(struct module *mod, + for (pg = pg_unuse; pg; pg = pg->next) + remaining += 1 << pg->order; + ++ pages -= remaining; ++ + skip = DIV_ROUND_UP(skip, ENTRIES_PER_PAGE); + + /* +@@ -7185,6 +7190,13 @@ static int ftrace_process_locs(struct module *mod, + synchronize_rcu(); + ftrace_free_pages(pg_unuse); + } ++ ++ if (!mod) { ++ count -= skipped; ++ pr_info("ftrace: allocating %ld entries in %ld pages\n", ++ count, pages); ++ } ++ + return ret; + } + +@@ -7836,9 +7848,6 @@ void __init ftrace_init(void) + goto failed; + } + +- pr_info("ftrace: allocating %ld entries in %ld pages\n", +- count, DIV_ROUND_UP(count, ENTRIES_PER_PAGE)); +- + ret = ftrace_process_locs(NULL, + __start_mcount_loc, + __stop_mcount_loc); +-- +2.50.1 (Apple Git-155) + diff --git a/1346-ftrace-do-not-over-allocate-ftrace-memory.patch b/1346-ftrace-do-not-over-allocate-ftrace-memory.patch new file mode 100644 index 000000000..5f2d5c4ef --- /dev/null +++ b/1346-ftrace-do-not-over-allocate-ftrace-memory.patch @@ -0,0 +1,166 @@ +From 7a2ec4e50744d195d6a66868ef6f3fb39f15b8b7 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Tue, 2 Jun 2026 15:56:18 +0200 +Subject: [PATCH] ftrace: Do not over-allocate ftrace memory + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit be55257fab181b93af38f8c4b1b3cb453a78d742 +Author: Guenter Roeck +Date: Tue Jan 13 07:22:42 2026 -0800 + + ftrace: Do not over-allocate ftrace memory + + The pg_remaining calculation in ftrace_process_locs() assumes that + ENTRIES_PER_PAGE multiplied by 2^order equals the actual capacity of the + allocated page group. However, ENTRIES_PER_PAGE is PAGE_SIZE / ENTRY_SIZE + (integer division). When PAGE_SIZE is not a multiple of ENTRY_SIZE (e.g. + 4096 / 24 = 170 with remainder 16), high-order allocations (like 256 pages) + have significantly more capacity than 256 * 170. This leads to pg_remaining + being underestimated, which in turn makes skip (derived from skipped - + pg_remaining) larger than expected, causing the WARN(skip != remaining) + to trigger. + + Extra allocated pages for ftrace: 2 with 654 skipped + WARNING: CPU: 0 PID: 0 at kernel/trace/ftrace.c:7295 ftrace_process_locs+0x5bf/0x5e0 + + A similar problem in ftrace_allocate_records() can result in allocating + too many pages. This can trigger the second warning in + ftrace_process_locs(). + + Extra allocated pages for ftrace + WARNING: CPU: 0 PID: 0 at kernel/trace/ftrace.c:7276 ftrace_process_locs+0x548/0x580 + + Use the actual capacity of a page group to determine the number of pages + to allocate. Have ftrace_allocate_pages() return the number of allocated + pages to avoid having to calculate it. Use the actual page group capacity + when validating the number of unused pages due to skipped entries. + Drop the definition of ENTRIES_PER_PAGE since it is no longer used. + + Cc: stable@vger.kernel.org + Fixes: 4a3efc6baff93 ("ftrace: Update the mcount_loc check of skipped entries") + Link: https://patch.msgid.link/20260113152243.3557219-1-linux@roeck-us.net + Signed-off-by: Guenter Roeck + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c +index 5e131970a70e..877f943832c9 100644 +--- a/kernel/trace/ftrace.c ++++ b/kernel/trace/ftrace.c +@@ -1122,7 +1122,6 @@ struct ftrace_page { + }; + + #define ENTRY_SIZE sizeof(struct dyn_ftrace) +-#define ENTRIES_PER_PAGE (PAGE_SIZE / ENTRY_SIZE) + + static struct ftrace_page *ftrace_pages_start; + static struct ftrace_page *ftrace_pages; +@@ -3754,7 +3753,8 @@ static int ftrace_update_code(struct module *mod, struct ftrace_page *new_pgs) + return 0; + } + +-static int ftrace_allocate_records(struct ftrace_page *pg, int count) ++static int ftrace_allocate_records(struct ftrace_page *pg, int count, ++ unsigned long *num_pages) + { + int order; + int pages; +@@ -3764,7 +3764,7 @@ static int ftrace_allocate_records(struct ftrace_page *pg, int count) + return -EINVAL; + + /* We want to fill as much as possible, with no empty pages */ +- pages = DIV_ROUND_UP(count, ENTRIES_PER_PAGE); ++ pages = DIV_ROUND_UP(count * ENTRY_SIZE, PAGE_SIZE); + order = fls(pages) - 1; + + again: +@@ -3779,6 +3779,7 @@ static int ftrace_allocate_records(struct ftrace_page *pg, int count) + } + + ftrace_number_of_pages += 1 << order; ++ *num_pages += 1 << order; + ftrace_number_of_groups++; + + cnt = (PAGE_SIZE << order) / ENTRY_SIZE; +@@ -3807,12 +3808,14 @@ static void ftrace_free_pages(struct ftrace_page *pages) + } + + static struct ftrace_page * +-ftrace_allocate_pages(unsigned long num_to_init) ++ftrace_allocate_pages(unsigned long num_to_init, unsigned long *num_pages) + { + struct ftrace_page *start_pg; + struct ftrace_page *pg; + int cnt; + ++ *num_pages = 0; ++ + if (!num_to_init) + return NULL; + +@@ -3826,7 +3829,7 @@ ftrace_allocate_pages(unsigned long num_to_init) + * waste as little space as possible. + */ + for (;;) { +- cnt = ftrace_allocate_records(pg, num_to_init); ++ cnt = ftrace_allocate_records(pg, num_to_init, num_pages); + if (cnt < 0) + goto free_pages; + +@@ -7062,8 +7065,6 @@ static int ftrace_process_locs(struct module *mod, + if (!count) + return 0; + +- pages = DIV_ROUND_UP(count, ENTRIES_PER_PAGE); +- + /* + * Sorting mcount in vmlinux at build time depend on + * CONFIG_BUILDTIME_MCOUNT_SORT, while mcount loc in +@@ -7076,7 +7077,7 @@ static int ftrace_process_locs(struct module *mod, + test_is_sorted(start, count); + } + +- start_pg = ftrace_allocate_pages(count); ++ start_pg = ftrace_allocate_pages(count, &pages); + if (!start_pg) + return -ENOMEM; + +@@ -7163,27 +7164,27 @@ static int ftrace_process_locs(struct module *mod, + /* We should have used all pages unless we skipped some */ + if (pg_unuse) { + unsigned long pg_remaining, remaining = 0; +- unsigned long skip; ++ long skip; + + /* Count the number of entries unused and compare it to skipped. */ +- pg_remaining = (ENTRIES_PER_PAGE << pg->order) - pg->index; ++ pg_remaining = (PAGE_SIZE << pg->order) / ENTRY_SIZE - pg->index; + + if (!WARN(skipped < pg_remaining, "Extra allocated pages for ftrace")) { + + skip = skipped - pg_remaining; + +- for (pg = pg_unuse; pg; pg = pg->next) ++ for (pg = pg_unuse; pg && skip > 0; pg = pg->next) { + remaining += 1 << pg->order; ++ skip -= (PAGE_SIZE << pg->order) / ENTRY_SIZE; ++ } + + pages -= remaining; + +- skip = DIV_ROUND_UP(skip, ENTRIES_PER_PAGE); +- + /* + * Check to see if the number of pages remaining would + * just fit the number of entries skipped. + */ +- WARN(skip != remaining, "Extra allocated pages for ftrace: %lu with %lu skipped", ++ WARN(pg || skip > 0, "Extra allocated pages for ftrace: %lu with %lu skipped", + remaining, skipped); + } + /* Need to synchronize with ftrace_location_range() */ +-- +2.50.1 (Apple Git-155) + diff --git a/1347-ftrace-test-mcount-loc-addr-before-calling-ftrace-call-addr.patch b/1347-ftrace-test-mcount-loc-addr-before-calling-ftrace-call-addr.patch new file mode 100644 index 000000000..bee66c4c1 --- /dev/null +++ b/1347-ftrace-test-mcount-loc-addr-before-calling-ftrace-call-addr.patch @@ -0,0 +1,66 @@ +From 611153910e7701fddefdbc91f75953b89e698331 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Tue, 2 Jun 2026 15:56:21 +0200 +Subject: [PATCH] ftrace: Test mcount_loc addr before calling + ftrace_call_addr() + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 6eeca746fa5f1dd03c6ee05cb03f5eb1ddda1c81 +Author: Steven Rostedt +Date: Tue Feb 25 13:20:05 2025 -0500 + + ftrace: Test mcount_loc addr before calling ftrace_call_addr() + + The addresses in the mcount_loc can be zeroed and then moved by KASLR + making them invalid addresses. ftrace_call_addr() for ARM 64 expects a + valid address to kernel text. If the addr read from the mcount_loc section + is invalid, it must not call ftrace_call_addr(). Move the addr check + before calling ftrace_call_addr() in ftrace_process_locs(). + + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Masahiro Yamada + Cc: Catalin Marinas + Cc: Will Deacon + Cc: Mark Brown + Link: https://lore.kernel.org/20250225182054.290128736@goodmis.org + Fixes: ef378c3b8233 ("scripts/sorttable: Zero out weak functions in mcount_loc table") + Reported-by: Nathan Chancellor + Reported-by: "Arnd Bergmann" + Tested-by: Nathan Chancellor + Closes: https://lore.kernel.org/all/20250225025631.GA271248@ax162/ + Closes: https://lore.kernel.org/all/91523154-072b-437b-bbdc-0b70e9783fd0@app.fastmail.com/ + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c +index 877f943832c9..59069b58fcd4 100644 +--- a/kernel/trace/ftrace.c ++++ b/kernel/trace/ftrace.c +@@ -7112,7 +7112,9 @@ static int ftrace_process_locs(struct module *mod, + pg = start_pg; + while (p < end) { + unsigned long end_offset; +- addr = ftrace_call_adjust(*p++); ++ ++ addr = *p++; ++ + /* + * Some architecture linkers will pad between + * the different mcount_loc sections of different +@@ -7124,6 +7126,8 @@ static int ftrace_process_locs(struct module *mod, + continue; + } + ++ addr = ftrace_call_adjust(addr); ++ + end_offset = (pg->index+1) * sizeof(pg->records[0]); + if (end_offset > PAGE_SIZE << pg->order) { + /* We should have allocated enough */ +-- +2.50.1 (Apple Git-155) + diff --git a/1348-ftrace-check-against-is-kernel-text-instead-of-kaslr-offset.patch b/1348-ftrace-check-against-is-kernel-text-instead-of-kaslr-offset.patch new file mode 100644 index 000000000..8e6c1e91e --- /dev/null +++ b/1348-ftrace-check-against-is-kernel-text-instead-of-kaslr-offset.patch @@ -0,0 +1,85 @@ +From 4460650d8c24b8689e42d47abeff7fe520f911a3 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Tue, 2 Jun 2026 15:56:25 +0200 +Subject: [PATCH] ftrace: Check against is_kernel_text() instead of + kaslr_offset() + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit da0f622b344be769ed61e7c1caf95cd0cdb47964 +Author: Steven Rostedt +Date: Tue Feb 25 13:20:06 2025 -0500 + + ftrace: Check against is_kernel_text() instead of kaslr_offset() + + As kaslr_offset() is architecture dependent and also may not be defined by + all architectures, when zeroing out unused weak functions, do not check + against kaslr_offset(), but instead check if the address is within the + kernel text sections. If KASLR added a shift to the zeroed out function, + it would still not be located in the kernel text. This is a more robust + way to test if the text is valid or not. + + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Masahiro Yamada + Cc: Catalin Marinas + Cc: Will Deacon + Cc: "Arnd Bergmann" + Link: https://lore.kernel.org/20250225182054.471759017@goodmis.org + Fixes: ef378c3b8233 ("scripts/sorttable: Zero out weak functions in mcount_loc table") + Reported-by: Nathan Chancellor + Reported-by: Mark Brown + Tested-by: Nathan Chancellor + Closes: https://lore.kernel.org/all/20250224180805.GA1536711@ax162/ + Closes: https://lore.kernel.org/all/5225b07b-a9b2-4558-9d5f-aa60b19f6317@sirena.org.uk/ + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c +index 59069b58fcd4..63ed9688f491 100644 +--- a/kernel/trace/ftrace.c ++++ b/kernel/trace/ftrace.c +@@ -7055,7 +7055,6 @@ static int ftrace_process_locs(struct module *mod, + unsigned long count; + unsigned long *p; + unsigned long addr; +- unsigned long kaslr; + unsigned long flags = 0; /* Shut up gcc */ + unsigned long pages; + int ret = -ENOMEM; +@@ -7105,9 +7104,6 @@ static int ftrace_process_locs(struct module *mod, + ftrace_pages->next = start_pg; + } + +- /* For zeroed locations that were shifted for core kernel */ +- kaslr = !mod ? kaslr_offset() : 0; +- + p = start; + pg = start_pg; + while (p < end) { +@@ -7121,7 +7117,18 @@ static int ftrace_process_locs(struct module *mod, + * object files to satisfy alignments. + * Skip any NULL pointers. + */ +- if (!addr || addr == kaslr) { ++ if (!addr) { ++ skipped++; ++ continue; ++ } ++ ++ /* ++ * If this is core kernel, make sure the address is in core ++ * or inittext, as weak functions get zeroed and KASLR can ++ * move them to something other than zero. It just will not ++ * move it to an area where kernel text is. ++ */ ++ if (!mod && !(is_kernel_text(addr) || is_kernel_inittext(addr))) { + skipped++; + continue; + } +-- +2.50.1 (Apple Git-155) + diff --git a/1349-scripts-sorttable-use-normal-sort-if-theres-no-relocs-in-the.patch b/1349-scripts-sorttable-use-normal-sort-if-theres-no-relocs-in-the.patch new file mode 100644 index 000000000..641a18ea1 --- /dev/null +++ b/1349-scripts-sorttable-use-normal-sort-if-theres-no-relocs-in-the.patch @@ -0,0 +1,67 @@ +From 5c8bd640b0ee1f1e0d7137f122996180f9bd5590 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Tue, 2 Jun 2026 15:56:27 +0200 +Subject: [PATCH] scripts/sorttable: Use normal sort if theres no relocs in the + mcount section + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 46514b3c2c17c67cefe84b0c1a59e0aaf6093131 +Author: Steven Rostedt +Date: Tue Feb 25 13:20:07 2025 -0500 + + scripts/sorttable: Use normal sort if theres no relocs in the mcount section + + When ARM 64 is compiled with gcc, the mcount_loc section will be filled + with zeros and the addresses will be located in the Elf_Rela sections. To + sort the mcount_loc section, the addresses from the Elf_Rela need to be + placed into an array and that is sorted. + + But when ARM 64 is compiled with clang, it does it the same way as other + architectures and leaves the addresses as is in the mcount_loc section. + + To handle both cases, ARM 64 will first try to sort the Elf_Rela section, + and if it doesn't find any functions, it will then fall back to the + sorting of the addresses in the mcount_loc section itself. + + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Masahiro Yamada + Cc: Catalin Marinas + Cc: Will Deacon + Cc: Mark Brown + Link: https://lore.kernel.org/20250225182054.648398403@goodmis.org + Fixes: b3d09d06e052 ("arm64: scripts/sorttable: Implement sorting mcount_loc at boot for arm64") + Reported-by: "Arnd Bergmann" + Tested-by: Nathan Chancellor + Closes: https://lore.kernel.org/all/893cd8f1-8585-4d25-bf0f-4197bf872465@app.fastmail.com/ + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 23c7e0e6c024..07ad8116bc8d 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -827,9 +827,14 @@ static void *sort_mcount_loc(void *arg) + pthread_exit(m_err); + } + +- if (sort_reloc) ++ if (sort_reloc) { + count = fill_relocs(vals, size, ehdr, emloc->start_mcount_loc); +- else ++ /* gcc may use relocs to save the addresses, but clang does not. */ ++ if (!count) { ++ count = fill_addrs(vals, size, start_loc); ++ sort_reloc = 0; ++ } ++ } else + count = fill_addrs(vals, size, start_loc); + + if (count < 0) { +-- +2.50.1 (Apple Git-155) + diff --git a/1350-scripts-sorttable-allow-matches-to-functions-before-function.patch b/1350-scripts-sorttable-allow-matches-to-functions-before-function.patch new file mode 100644 index 000000000..6ec1be00b --- /dev/null +++ b/1350-scripts-sorttable-allow-matches-to-functions-before-function.patch @@ -0,0 +1,73 @@ +From 7d08b5aa28b2bdd1d1bedbd6e8722bf3799dce71 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Tue, 2 Jun 2026 15:56:31 +0200 +Subject: [PATCH] scripts/sorttable: Allow matches to functions before function + entry + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit dc208c69c033d3caba0509da1ae065d2b5ff165f +Author: Steven Rostedt +Date: Tue Feb 25 13:20:08 2025 -0500 + + scripts/sorttable: Allow matches to functions before function entry + + ARM 64 uses -fpatchable-function-entry=4,2 which adds padding before the + function and the addresses in the mcount_loc point there instead of the + function entry that is returned by nm. In order to find a function from nm + to make sure it's not an unused weak function, the entries in the + mcount_loc section needs to match the entries from nm. Since it can be an + instruction before the entry, add a before_func variable that ARM 64 can + set to 8, and if the mcount_loc entry is within 8 bytes of the nm function + entry, then it will be considered a match. + + Cc: Masami Hiramatsu + Cc: Mark Rutland + Cc: Mathieu Desnoyers + Cc: Andrew Morton + Cc: Masahiro Yamada + Cc: Catalin Marinas + Cc: Will Deacon + Cc: "Arnd Bergmann" + Cc: Mark Brown + Link: https://lore.kernel.org/20250225182054.815536219@goodmis.org + Fixes: ef378c3b82338 ("scripts/sorttable: Zero out weak functions in mcount_loc table") + Tested-by: Nathan Chancellor + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 07ad8116bc8d..7b4b3714b1af 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -611,13 +611,16 @@ static int add_field(uint64_t addr, uint64_t size) + return 0; + } + ++/* Used for when mcount/fentry is before the function entry */ ++static int before_func; ++ + /* Only return match if the address lies inside the function size */ + static int cmp_func_addr(const void *K, const void *A) + { + uint64_t key = *(const uint64_t *)K; + const struct func_info *a = A; + +- if (key < a->addr) ++ if (key + before_func < a->addr) + return -1; + return key >= a->addr + a->size; + } +@@ -1253,6 +1256,8 @@ static int do_file(char const *const fname, void *addr) + #ifdef MCOUNT_SORT_ENABLED + sort_reloc = true; + rela_type = 0x403; ++ /* arm64 uses patchable function entry placing before function */ ++ before_func = 8; + #endif + /* fallthrough */ + case EM_386: +-- +2.50.1 (Apple Git-155) + diff --git a/1351-scripts-sorttable-fix-endianness-handling-in-build-time-mcou.patch b/1351-scripts-sorttable-fix-endianness-handling-in-build-time-mcou.patch new file mode 100644 index 000000000..e7882bc56 --- /dev/null +++ b/1351-scripts-sorttable-fix-endianness-handling-in-build-time-mcou.patch @@ -0,0 +1,55 @@ +From 3436470d40a0906b91d65287931fc22524334dba Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Tue, 2 Jun 2026 15:56:34 +0200 +Subject: [PATCH] scripts/sorttable: Fix endianness handling in build-time + mcount sort + +JIRA: https://redhat.atlassian.net/browse/RHEL-180193 + +commit 023f124a64174c47e18340ded7e2a39b96eb9523 +Author: Vasily Gorbik +Date: Wed Apr 2 03:15:35 2025 +0200 + + scripts/sorttable: Fix endianness handling in build-time mcount sort + + Kernel cross-compilation with BUILDTIME_MCOUNT_SORT produces zeroed + mcount values if the build-host endianness does not match the ELF + file endianness. + + The mcount values array is converted from ELF file + endianness to build-host endianness during initialization in + fill_relocs()/fill_addrs(). Avoid extra conversion of these values during + weak-function zeroing; otherwise, they do not match nm-parsed addresses + and all mcount values are zeroed out. + + Cc: Masami Hiramatsu + Cc: Catalin Marinas + Cc: Nathan Chancellor + Cc: Heiko Carstens + Cc: Alexander Gordeev + Link: https://lore.kernel.org/patch.git-dca31444b0f1.your-ad-here.call-01743554658-ext-8692@work.hours + Fixes: ef378c3b8233 ("scripts/sorttable: Zero out weak functions in mcount_loc table") + Reported-by: Ilya Leoshkevich + Reported-by: Ihor Solodrai + Closes: https://lore.kernel.org/all/your-ad-here.call-01743522822-ext-4975@work.hours/ + Signed-off-by: Vasily Gorbik + Signed-off-by: Steven Rostedt (Google) + +Signed-off-by: Jerome Marchand + +diff --git a/scripts/sorttable.c b/scripts/sorttable.c +index 7b4b3714b1af..deed676bfe38 100644 +--- a/scripts/sorttable.c ++++ b/scripts/sorttable.c +@@ -857,7 +857,7 @@ static void *sort_mcount_loc(void *arg) + for (void *ptr = vals; ptr < vals + size; ptr += long_size) { + uint64_t key; + +- key = long_size == 4 ? r((uint32_t *)ptr) : r8((uint64_t *)ptr); ++ key = long_size == 4 ? *(uint32_t *)ptr : *(uint64_t *)ptr; + if (!find_func(key)) { + if (long_size == 4) + *(uint32_t *)ptr = 0; +-- +2.50.1 (Apple Git-155) + diff --git a/kernel.spec b/kernel.spec index 633c2aedd..ec559cb91 100644 --- a/kernel.spec +++ b/kernel.spec @@ -176,13 +176,13 @@ Summary: The Linux kernel %define specrpmversion 6.12.0 %define specversion 6.12.0 %define patchversion 6.12 -%define pkgrelease 211.7.1 +%define pkgrelease 211.22.1 %define kversion 6 %define tarfile_release 6.12.0-211.7.1.el10_2 # This is needed to do merge window version magic %define patchlevel 12 # This allows pkg_release to have configurable %%{?dist} tag -%define specrelease 211.20.1%{?buildid}%{?dist} +%define specrelease 211.22.1%{?buildid}%{?dist} # This defines the kabi tarball version %define kabiversion 6.12.0-211.7.1.el10_2 @@ -1322,6 +1322,70 @@ Patch1284: 1284-smb-client-fix-oob-reads-parsing-symlink-error-response.patch Patch1285: 1285-sched-deadline-fix-dl-server-time-accounting.patch Patch1286: 1286-redhat-configs-automotive-disable-config-io-uring.patch Patch1287: 1287-rhel-kabi-dpll-adaptation.patch +Patch1288: 1288-net-bonding-fix-use-after-free-in-bond-xmit-broadcast.patch +Patch1289: 1289-s390-pci-avoid-deadlock-between-pci-error-recovery-and-mlx5-.patch +Patch1290: 1290-ice-fix-stats-array-overflow-when-vf-requests-more-queues.patch +Patch1291: 1291-s390-dasd-fix-gendisk-parent-after-copy-pair-swap.patch +Patch1292: 1292-s390-dasd-move-quiesce-state-with-pprc-swap.patch +Patch1293: 1293-s390-dasd-copy-detected-format-information-to-secondary-devi.patch +Patch1294: 1294-nouveau-gsp-drop-warn-on-in-acpi-probes.patch +Patch1295: 1295-can-raw-fix-ro-uniq-use-after-free-in-raw-rcv.patch +Patch1296: 1296-erofs-add-gfp-noio-in-the-bio-completion-if-needed.patch +Patch1297: 1297-alsa-6fire-fix-use-after-free-on-disconnect.patch +Patch1298: 1298-ip6-tunnel-clear-skb2-cb-in-ip4ip6-err.patch +Patch1299: 1299-ipv6-rpl-reserve-mac-len-headroom-when-recompressed-srh-grow.patch +Patch1300: 1300-fs-constify-file-ptr-in-backing-file-accessor-helpers.patch +Patch1301: 1301-lsm-split-the-notifier-code-out-into-lsm-notifier-c.patch +Patch1302: 1302-lsm-split-the-init-code-out-into-lsm-init-c.patch +Patch1303: 1303-lsm-consolidate-lsm-allowed-and-prepare-lsm-into-lsm-prepare.patch +Patch1304: 1304-lsm-introduce-looping-macros-for-the-initialization-code.patch +Patch1305: 1305-lsm-integrate-report-lsm-order-code-into-caller.patch +Patch1306: 1306-lsm-integrate-lsm-early-cred-and-lsm-early-task-into-caller.patch +Patch1307: 1307-lsm-rename-ordered-lsm-init-to-lsm-init-ordered.patch +Patch1308: 1308-lsm-replace-the-name-field-with-a-pointer-to-the-lsm-id-stru.patch +Patch1309: 1309-lsm-rename-the-lsm-order-variables-for-consistency.patch +Patch1310: 1310-lsm-rework-lsm-active-cnt-and-lsm-idlist.patch +Patch1311: 1311-lsm-get-rid-of-the-lsm-names-list-and-do-some-cleanup.patch +Patch1312: 1312-lsm-rework-the-lsm-enable-disable-setter-getter-functions.patch +Patch1313: 1313-lsm-rename-exists-ordered-lsm-to-lsm-order-exists.patch +Patch1314: 1314-lsm-rename-rework-append-ordered-lsm-into-lsm-order-append.patch +Patch1315: 1315-lsm-rename-rework-ordered-lsm-parse-to-lsm-order-parse.patch +Patch1316: 1316-lsm-cleanup-the-lsm-blob-size-code.patch +Patch1317: 1317-lsm-cleanup-initialize-lsm-and-rename-to-lsm-init-single.patch +Patch1318: 1318-lsm-fold-lsm-init-ordered-into-security-init.patch +Patch1319: 1319-lsm-add-tweak-function-header-comment-blocks-in-lsm-init-c.patch +Patch1320: 1320-lsm-cleanup-the-debug-and-console-output-in-lsm-init-c.patch +Patch1321: 1321-fs-prepare-for-adding-lsm-blob-to-backing-file.patch +Patch1322: 1322-lsm-add-backing-file-lsm-hooks.patch +Patch1323: 1323-selinux-fix-overlayfs-mmap-and-mprotect-access-checks.patch +Patch1324: 1324-scripts-sorttable-fix-orc-sort-cmp-to-maintain-symmetry-and-.patch +Patch1325: 1325-scripts-sorttable-remove-unused-macro-defines.patch +Patch1326: 1326-scripts-sorttable-remove-unused-write-functions.patch +Patch1327: 1327-scripts-sorttable-remove-unneeded-elf-rel.patch +Patch1328: 1328-scripts-sorttable-have-the-orc-code-use-the-r-functions-to-r.patch +Patch1329: 1329-scripts-sorttable-make-compare-extable-into-two-functions.patch +Patch1330: 1330-scripts-sorttable-convert-elf-ehdr-to-union.patch +Patch1331: 1331-scripts-sorttable-replace-elf-shdr-macro-with-a-union.patch +Patch1332: 1332-scripts-sorttable-convert-elf-sym-macro-over-to-a-union.patch +Patch1333: 1333-scripts-sorttable-add-helper-functions-for-elf-ehdr.patch +Patch1334: 1334-scripts-sorttable-add-helper-functions-for-elf-shdr.patch +Patch1335: 1335-scripts-sorttable-add-helper-functions-for-elf-sym.patch +Patch1336: 1336-scripts-sorttable-use-uint64-t-for-mcount-sorting.patch +Patch1337: 1337-scripts-sorttable-move-code-from-sorttable-h-into-sorttable-.patch +Patch1338: 1338-scripts-sorttable-get-start-stop-mcount-loc-from-elf-file-di.patch +Patch1339: 1339-scripts-sorttable-use-a-structure-of-function-pointers-for-e.patch +Patch1340: 1340-arm64-scripts-sorttable-implement-sorting-mcount-loc-at-boot.patch +Patch1341: 1341-scripts-sorttable-have-mcount-rela-sort-use-direct-values.patch +Patch1342: 1342-scripts-sorttable-always-use-an-array-for-the-mcount-loc-sor.patch +Patch1343: 1343-scripts-sorttable-zero-out-weak-functions-in-mcount-loc-tabl.patch +Patch1344: 1344-ftrace-update-the-mcount-loc-check-of-skipped-entries.patch +Patch1345: 1345-ftrace-have-ftrace-pages-output-reflect-freed-pages.patch +Patch1346: 1346-ftrace-do-not-over-allocate-ftrace-memory.patch +Patch1347: 1347-ftrace-test-mcount-loc-addr-before-calling-ftrace-call-addr.patch +Patch1348: 1348-ftrace-check-against-is-kernel-text-instead-of-kaslr-offset.patch +Patch1349: 1349-scripts-sorttable-use-normal-sort-if-theres-no-relocs-in-the.patch +Patch1350: 1350-scripts-sorttable-allow-matches-to-functions-before-function.patch +Patch1351: 1351-scripts-sorttable-fix-endianness-handling-in-build-time-mcou.patch # END OF PATCH DEFINITIONS %description @@ -2366,6 +2430,70 @@ ApplyPatch 1284-smb-client-fix-oob-reads-parsing-symlink-error-response.patch ApplyPatch 1285-sched-deadline-fix-dl-server-time-accounting.patch ApplyPatch 1286-redhat-configs-automotive-disable-config-io-uring.patch ApplyPatch 1287-rhel-kabi-dpll-adaptation.patch +ApplyPatch 1288-net-bonding-fix-use-after-free-in-bond-xmit-broadcast.patch +ApplyPatch 1289-s390-pci-avoid-deadlock-between-pci-error-recovery-and-mlx5-.patch +ApplyPatch 1290-ice-fix-stats-array-overflow-when-vf-requests-more-queues.patch +ApplyPatch 1291-s390-dasd-fix-gendisk-parent-after-copy-pair-swap.patch +ApplyPatch 1292-s390-dasd-move-quiesce-state-with-pprc-swap.patch +ApplyPatch 1293-s390-dasd-copy-detected-format-information-to-secondary-devi.patch +ApplyPatch 1294-nouveau-gsp-drop-warn-on-in-acpi-probes.patch +ApplyPatch 1295-can-raw-fix-ro-uniq-use-after-free-in-raw-rcv.patch +ApplyPatch 1296-erofs-add-gfp-noio-in-the-bio-completion-if-needed.patch +ApplyPatch 1297-alsa-6fire-fix-use-after-free-on-disconnect.patch +ApplyPatch 1298-ip6-tunnel-clear-skb2-cb-in-ip4ip6-err.patch +ApplyPatch 1299-ipv6-rpl-reserve-mac-len-headroom-when-recompressed-srh-grow.patch +ApplyPatch 1300-fs-constify-file-ptr-in-backing-file-accessor-helpers.patch +ApplyPatch 1301-lsm-split-the-notifier-code-out-into-lsm-notifier-c.patch +ApplyPatch 1302-lsm-split-the-init-code-out-into-lsm-init-c.patch +ApplyPatch 1303-lsm-consolidate-lsm-allowed-and-prepare-lsm-into-lsm-prepare.patch +ApplyPatch 1304-lsm-introduce-looping-macros-for-the-initialization-code.patch +ApplyPatch 1305-lsm-integrate-report-lsm-order-code-into-caller.patch +ApplyPatch 1306-lsm-integrate-lsm-early-cred-and-lsm-early-task-into-caller.patch +ApplyPatch 1307-lsm-rename-ordered-lsm-init-to-lsm-init-ordered.patch +ApplyPatch 1308-lsm-replace-the-name-field-with-a-pointer-to-the-lsm-id-stru.patch +ApplyPatch 1309-lsm-rename-the-lsm-order-variables-for-consistency.patch +ApplyPatch 1310-lsm-rework-lsm-active-cnt-and-lsm-idlist.patch +ApplyPatch 1311-lsm-get-rid-of-the-lsm-names-list-and-do-some-cleanup.patch +ApplyPatch 1312-lsm-rework-the-lsm-enable-disable-setter-getter-functions.patch +ApplyPatch 1313-lsm-rename-exists-ordered-lsm-to-lsm-order-exists.patch +ApplyPatch 1314-lsm-rename-rework-append-ordered-lsm-into-lsm-order-append.patch +ApplyPatch 1315-lsm-rename-rework-ordered-lsm-parse-to-lsm-order-parse.patch +ApplyPatch 1316-lsm-cleanup-the-lsm-blob-size-code.patch +ApplyPatch 1317-lsm-cleanup-initialize-lsm-and-rename-to-lsm-init-single.patch +ApplyPatch 1318-lsm-fold-lsm-init-ordered-into-security-init.patch +ApplyPatch 1319-lsm-add-tweak-function-header-comment-blocks-in-lsm-init-c.patch +ApplyPatch 1320-lsm-cleanup-the-debug-and-console-output-in-lsm-init-c.patch +ApplyPatch 1321-fs-prepare-for-adding-lsm-blob-to-backing-file.patch +ApplyPatch 1322-lsm-add-backing-file-lsm-hooks.patch +ApplyPatch 1323-selinux-fix-overlayfs-mmap-and-mprotect-access-checks.patch +ApplyPatch 1324-scripts-sorttable-fix-orc-sort-cmp-to-maintain-symmetry-and-.patch +ApplyPatch 1325-scripts-sorttable-remove-unused-macro-defines.patch +ApplyPatch 1326-scripts-sorttable-remove-unused-write-functions.patch +ApplyPatch 1327-scripts-sorttable-remove-unneeded-elf-rel.patch +ApplyPatch 1328-scripts-sorttable-have-the-orc-code-use-the-r-functions-to-r.patch +ApplyPatch 1329-scripts-sorttable-make-compare-extable-into-two-functions.patch +ApplyPatch 1330-scripts-sorttable-convert-elf-ehdr-to-union.patch +ApplyPatch 1331-scripts-sorttable-replace-elf-shdr-macro-with-a-union.patch +ApplyPatch 1332-scripts-sorttable-convert-elf-sym-macro-over-to-a-union.patch +ApplyPatch 1333-scripts-sorttable-add-helper-functions-for-elf-ehdr.patch +ApplyPatch 1334-scripts-sorttable-add-helper-functions-for-elf-shdr.patch +ApplyPatch 1335-scripts-sorttable-add-helper-functions-for-elf-sym.patch +ApplyPatch 1336-scripts-sorttable-use-uint64-t-for-mcount-sorting.patch +ApplyPatch 1337-scripts-sorttable-move-code-from-sorttable-h-into-sorttable-.patch +ApplyPatch 1338-scripts-sorttable-get-start-stop-mcount-loc-from-elf-file-di.patch +ApplyPatch 1339-scripts-sorttable-use-a-structure-of-function-pointers-for-e.patch +ApplyPatch 1340-arm64-scripts-sorttable-implement-sorting-mcount-loc-at-boot.patch +ApplyPatch 1341-scripts-sorttable-have-mcount-rela-sort-use-direct-values.patch +ApplyPatch 1342-scripts-sorttable-always-use-an-array-for-the-mcount-loc-sor.patch +ApplyPatch 1343-scripts-sorttable-zero-out-weak-functions-in-mcount-loc-tabl.patch +ApplyPatch 1344-ftrace-update-the-mcount-loc-check-of-skipped-entries.patch +ApplyPatch 1345-ftrace-have-ftrace-pages-output-reflect-freed-pages.patch +ApplyPatch 1346-ftrace-do-not-over-allocate-ftrace-memory.patch +ApplyPatch 1347-ftrace-test-mcount-loc-addr-before-calling-ftrace-call-addr.patch +ApplyPatch 1348-ftrace-check-against-is-kernel-text-instead-of-kaslr-offset.patch +ApplyPatch 1349-scripts-sorttable-use-normal-sort-if-theres-no-relocs-in-the.patch +ApplyPatch 1350-scripts-sorttable-allow-matches-to-functions-before-function.patch +ApplyPatch 1351-scripts-sorttable-fix-endianness-handling-in-build-time-mcou.patch # END OF PATCH APPLICATIONS # Any further pre-build tree manipulations happen here. @@ -4870,6 +4998,78 @@ fi\ # # %changelog +* Wed Jun 11 2026 Andrew Lukoshko - 6.12.0-211.22.1 +- Recreate RHEL 6.12.0-211.22.1 from CentOS Stream 10 and upstream stable backports (1288-1352) +- RHEL changelog for 211.21.1..211.22.1 follows: + +* Wed Jun 10 2026 CKI KWF Bot [6.12.0-211.22.1.el10_2] +- scripts/sorttable: Fix endianness handling in build-time mcount sort (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Allow matches to functions before function entry (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Use normal sort if theres no relocs in the mcount section (Jerome Marchand) [RHEL-180932] +- ftrace: Check against is_kernel_text() instead of kaslr_offset() (Jerome Marchand) [RHEL-180932] +- ftrace: Test mcount_loc addr before calling ftrace_call_addr() (Jerome Marchand) [RHEL-180932] +- ftrace: Do not over-allocate ftrace memory (Jerome Marchand) [RHEL-180932] +- ftrace: Have ftrace pages output reflect freed pages (Jerome Marchand) [RHEL-180932] +- ftrace: Update the mcount_loc check of skipped entries (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Zero out weak functions in mcount_loc table (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Always use an array for the mcount_loc sorting (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Have mcount rela sort use direct values (Jerome Marchand) [RHEL-180932] +- arm64: scripts/sorttable: Implement sorting mcount_loc at boot for arm64 (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Use a structure of function pointers for elf helpers (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Get start/stop_mcount_loc from ELF file directly (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Move code from sorttable.h into sorttable.c (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Use uint64_t for mcount sorting (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Add helper functions for Elf_Sym (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Add helper functions for Elf_Shdr (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Add helper functions for Elf_Ehdr (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Convert Elf_Sym MACRO over to a union (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Replace Elf_Shdr Macro with a union (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Convert Elf_Ehdr to union (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Make compare_extable() into two functions (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Have the ORC code use the _r() functions to read (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Remove unneeded Elf_Rel (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Remove unused write functions (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: Remove unused macro defines (Jerome Marchand) [RHEL-180932] +- scripts/sorttable: fix orc_sort_cmp() to maintain symmetry and transitivity (Jerome Marchand) [RHEL-180932] +- selinux: fix overlayfs mmap() and mprotect() access checks (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: add backing_file LSM hooks (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- fs: prepare for adding LSM blob to backing_file (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: cleanup the debug and console output in lsm_init.c (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: add/tweak function header comment blocks in lsm_init.c (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: fold lsm_init_ordered() into security_init() (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: cleanup initialize_lsm() and rename to lsm_init_single() (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: cleanup the LSM blob size code (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: rename/rework ordered_lsm_parse() to lsm_order_parse() (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: rename/rework append_ordered_lsm() into lsm_order_append() (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: rename exists_ordered_lsm() to lsm_order_exists() (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: rework the LSM enable/disable setter/getter functions (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: get rid of the lsm_names list and do some cleanup (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: rework lsm_active_cnt and lsm_idlist[] (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: rename the lsm order variables for consistency (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: replace the name field with a pointer to the lsm_id struct (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: rename ordered_lsm_init() to lsm_init_ordered() (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: integrate lsm_early_cred() and lsm_early_task() into caller (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: integrate report_lsm_order() code into caller (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: introduce looping macros for the initialization code (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: consolidate lsm_allowed() and prepare_lsm() into lsm_prepare() (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: split the init code out into lsm_init.c (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- lsm: split the notifier code out into lsm_notifier.c (Ondrej Mosnacek) [RHEL-179437] {CVE-2026-46054} +- fs: constify file ptr in backing_file accessor helpers (Ondrej Mosnacek) [RHEL-179437] +- ipv6: rpl: reserve mac_len headroom when recompressed SRH grows (Antoine Tenart) [RHEL-178413] {CVE-2026-43501} +- ip6_tunnel: clear skb2->cb[] in ip4ip6_err() (Guillaume Nault) [RHEL-172651] {CVE-2026-43037} +- ALSA: 6fire: fix use-after-free on disconnect (CKI Backport Bot) [RHEL-172973] {CVE-2026-31581} +- erofs: add GFP_NOIO in the bio completion if needed (CKI Backport Bot) [RHEL-171686] {CVE-2026-31467} +- can: raw: fix ro->uniq use-after-free in raw_rcv() (CKI Backport Bot) [RHEL-170764] {CVE-2026-31532} +- nouveau/gsp: drop WARN_ON in ACPI probes (Gary Guo) [RHEL-167788] + +* Thu Jun 04 2026 CKI KWF Bot [6.12.0-211.21.1.el10_2] +- s390/dasd: Copy detected format information to secondary device (Ramesh Chhetri) [RHEL-176473] +- s390/dasd: Move quiesce state with pprc swap (Ramesh Chhetri) [RHEL-176473] +- s390/dasd: Fix gendisk parent after copy pair swap (Ramesh Chhetri) [RHEL-176473] +- ice: fix stats array overflow when VF requests more queues (Michal Schmidt) [RHEL-177518] +- s390/pci: Avoid deadlock between PCI error recovery and mlx5 crdump (Mircea Dragan) [RHEL-166855] +- net: bonding: fix use-after-free in bond_xmit_broadcast() (CKI Backport Bot) [RHEL-168073] {CVE-2026-31419} + * Sun Jun 07 2026 Andrew Lukoshko - 6.12.0-211.20.1 - Recreate RHEL 6.12.0-211.20.1 from CentOS Stream 10 and upstream stable backports (1245-1287) - smb cifs.spnego now shipped by RHEL too; existing ahead-fix 1105 is identical (RHEL's redundant copy omitted)