From: Sasha Levin Date: Sun, 21 Jun 2026 13:47:26 +0000 (-0400) Subject: Fixes for all trees X-Git-Url: http://git.ipfire.org/index.cgi?a=commitdiff_plain;h=06cf96d84b51cfe088597bbfd7824b14580fec6e;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- diff --git a/queue-5.10/batman-adv-tt-prevent-tvlv-entry-number-overflow.patch b/queue-5.10/batman-adv-tt-prevent-tvlv-entry-number-overflow.patch new file mode 100644 index 0000000000..37286302b1 --- /dev/null +++ b/queue-5.10/batman-adv-tt-prevent-tvlv-entry-number-overflow.patch @@ -0,0 +1,80 @@ +From 02012aff754be0708a52368a013eb79fdf2e88e1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:12:35 +0200 +Subject: batman-adv: tt: prevent TVLV entry number overflow + +From: Sven Eckelmann + +commit 99d9958fa10fb684b2a8e2c48a8d704122721420 upstream. + +The helpers to prepare the buffers for the local and global TT based +replies are trying to sum up all TT entries which can be found for each +VLAN. In theory, this sum can be too big for an u16 and therefore overflow. +A too small buffer would then be allocated for the TVLV. + +The too small buffer will be handled gracefully by +batadv_tt_tvlv_generate() and is not causing a buffer overflow - just a +truncated reply. But this overflow shouldn't have happened in the first and +the too small buffer should never have been allocated when an overflow was +detected. + +Cc: stable@kernel.org +Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 20 +++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index fb51088494dcc6..79da90b9cf0659 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -854,11 +854,18 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + u16 total_entries = 0; + u8 *tt_change_ptr; + int vlan_entries; ++ u16 sum_entries; + + spin_lock_bh(&orig_node->vlan_list_lock); + hlist_for_each_entry(vlan, &orig_node->vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); +- total_entries += vlan_entries; ++ ++ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { ++ *tt_len = 0; ++ goto out; ++ } ++ ++ total_entries = sum_entries; + num_vlan++; + } + +@@ -945,15 +952,22 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + struct batadv_softif_vlan *vlan; + size_t change_offset; + u16 num_vlan = 0; +- u16 vlan_entries = 0; + u16 total_entries = 0; + u16 tvlv_len; + u8 *tt_change_ptr; ++ int vlan_entries; ++ u16 sum_entries; + + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); +- total_entries += vlan_entries; ++ ++ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { ++ tvlv_len = 0; ++ goto out; ++ } ++ ++ total_entries = sum_entries; + num_vlan++; + } + +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch b/queue-5.10/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch new file mode 100644 index 0000000000..783b876529 --- /dev/null +++ b/queue-5.10/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch @@ -0,0 +1,62 @@ +From dfe89e43a6a607759308754aedeab2cd1ee7345e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:11:09 +0200 +Subject: batman-adv: tt: reject oversized local TVLV buffers + +From: Sven Eckelmann + +commit 1e9fab756f8395096d5bba7be0c373c4c8f5d165 upstream. + +The commit 3a359bf5c61d ("batman-adv: reject oversized global TT response +buffers") added a check to ensure that a global return buffer size can be +stored in an u16. The same buffer handling also exists for the local data +buffer but was not touched. + +A similar check should be also be in place for the local TVLV buffer. It +doesn't have the similar attack surface because it is only generated from +locally discovered MAC addresses but the dynamic nature could still cause +temporarily to large buffers. + +Cc: stable@kernel.org +Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index fa6dece492d3ab..fb51088494dcc6 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -943,12 +943,12 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + { + struct batadv_tvlv_tt_vlan_data *tt_vlan; + struct batadv_softif_vlan *vlan; ++ size_t change_offset; + u16 num_vlan = 0; + u16 vlan_entries = 0; + u16 total_entries = 0; + u16 tvlv_len; + u8 *tt_change_ptr; +- int change_offset; + + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { +@@ -964,8 +964,10 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + if (*tt_len < 0) + *tt_len = batadv_tt_len(total_entries); + +- tvlv_len = *tt_len; +- tvlv_len += change_offset; ++ if (check_add_overflow(*tt_len, change_offset, &tvlv_len)) { ++ tvlv_len = 0; ++ goto out; ++ } + + *tt_data = kmalloc(tvlv_len, GFP_ATOMIC); + if (!*tt_data) { +-- +2.53.0 + diff --git a/queue-5.10/series b/queue-5.10/series index aa62c35b04..5d89742506 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -16,3 +16,6 @@ net-sched-cls_u32-use-skb_header_pointer_careful.patch drm-amd-display-use-krealloc_array-in-dal_vector_res.patch net-9p-fix-refcount-leak-in-p9_read_work-error-handl.patch netdevsim-fix-memory-leak-of-nsim_dev-fa_cookie.patch +batman-adv-tt-reject-oversized-local-tvlv-buffers.patch +batman-adv-tt-prevent-tvlv-entry-number-overflow.patch +vfio-iommu_type1-replace-kfree-with-kvfree.patch diff --git a/queue-5.10/vfio-iommu_type1-replace-kfree-with-kvfree.patch b/queue-5.10/vfio-iommu_type1-replace-kfree-with-kvfree.patch new file mode 100644 index 0000000000..41bd4f32e1 --- /dev/null +++ b/queue-5.10/vfio-iommu_type1-replace-kfree-with-kvfree.patch @@ -0,0 +1,38 @@ +From 9c332c79d76eecd1dcb9990ad02370af71cd31e0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Dec 2021 01:16:00 -0800 +Subject: vfio/iommu_type1: replace kfree with kvfree + +From: Jiacheng Shi + +[ Upstream commit 2bed2ced40c97b8540ff38df0149e8ecb2bf4c65 ] + +Variables allocated by kvzalloc should not be freed by kfree. +Because they may be allocated by vmalloc. +So we replace kfree with kvfree here. + +Fixes: d6a4c185660c ("vfio iommu: Implementation of ioctl for dirty pages tracking") +Signed-off-by: Jiacheng Shi +Link: https://lore.kernel.org/r/20211212091600.2560-1-billsjc@sjtu.edu.cn +Signed-off-by: Alex Williamson +Signed-off-by: Sasha Levin +--- + drivers/vfio/vfio_iommu_type1.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c +index b2a543e7cac454..eee8d2ab03d683 100644 +--- a/drivers/vfio/vfio_iommu_type1.c ++++ b/drivers/vfio/vfio_iommu_type1.c +@@ -228,7 +228,7 @@ static int vfio_dma_bitmap_alloc(struct vfio_dma *dma, size_t pgsize) + + static void vfio_dma_bitmap_free(struct vfio_dma *dma) + { +- kfree(dma->bitmap); ++ kvfree(dma->bitmap); + dma->bitmap = NULL; + } + +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tt-prevent-tvlv-entry-number-overflow.patch b/queue-5.15/batman-adv-tt-prevent-tvlv-entry-number-overflow.patch new file mode 100644 index 0000000000..4059ce76d6 --- /dev/null +++ b/queue-5.15/batman-adv-tt-prevent-tvlv-entry-number-overflow.patch @@ -0,0 +1,80 @@ +From aa89d06a63f2b16e8af824e5775e32b4e8f1b47e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:03:23 +0200 +Subject: batman-adv: tt: prevent TVLV entry number overflow + +From: Sven Eckelmann + +commit 99d9958fa10fb684b2a8e2c48a8d704122721420 upstream. + +The helpers to prepare the buffers for the local and global TT based +replies are trying to sum up all TT entries which can be found for each +VLAN. In theory, this sum can be too big for an u16 and therefore overflow. +A too small buffer would then be allocated for the TVLV. + +The too small buffer will be handled gracefully by +batadv_tt_tvlv_generate() and is not causing a buffer overflow - just a +truncated reply. But this overflow shouldn't have happened in the first and +the too small buffer should never have been allocated when an overflow was +detected. + +Cc: stable@kernel.org +Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 20 +++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 3849c348ff0344..8ab1257bade048 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -850,11 +850,18 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + u16 total_entries = 0; + u8 *tt_change_ptr; + int vlan_entries; ++ u16 sum_entries; + + spin_lock_bh(&orig_node->vlan_list_lock); + hlist_for_each_entry(vlan, &orig_node->vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); +- total_entries += vlan_entries; ++ ++ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { ++ *tt_len = 0; ++ goto out; ++ } ++ ++ total_entries = sum_entries; + num_vlan++; + } + +@@ -941,15 +948,22 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + struct batadv_softif_vlan *vlan; + size_t change_offset; + u16 num_vlan = 0; +- u16 vlan_entries = 0; + u16 total_entries = 0; + u16 tvlv_len; + u8 *tt_change_ptr; ++ int vlan_entries; ++ u16 sum_entries; + + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); +- total_entries += vlan_entries; ++ ++ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { ++ tvlv_len = 0; ++ goto out; ++ } ++ ++ total_entries = sum_entries; + num_vlan++; + } + +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch b/queue-5.15/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch new file mode 100644 index 0000000000..8c579dcf0c --- /dev/null +++ b/queue-5.15/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch @@ -0,0 +1,62 @@ +From 8bc28e5a230c15fa77b2f45d49ce0036a1cab9a0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:02:01 +0200 +Subject: batman-adv: tt: reject oversized local TVLV buffers + +From: Sven Eckelmann + +commit 1e9fab756f8395096d5bba7be0c373c4c8f5d165 upstream. + +The commit 3a359bf5c61d ("batman-adv: reject oversized global TT response +buffers") added a check to ensure that a global return buffer size can be +stored in an u16. The same buffer handling also exists for the local data +buffer but was not touched. + +A similar check should be also be in place for the local TVLV buffer. It +doesn't have the similar attack surface because it is only generated from +locally discovered MAC addresses but the dynamic nature could still cause +temporarily to large buffers. + +Cc: stable@kernel.org +Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index e4d55b27f2551b..3849c348ff0344 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -939,12 +939,12 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + { + struct batadv_tvlv_tt_vlan_data *tt_vlan; + struct batadv_softif_vlan *vlan; ++ size_t change_offset; + u16 num_vlan = 0; + u16 vlan_entries = 0; + u16 total_entries = 0; + u16 tvlv_len; + u8 *tt_change_ptr; +- int change_offset; + + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { +@@ -960,8 +960,10 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + if (*tt_len < 0) + *tt_len = batadv_tt_len(total_entries); + +- tvlv_len = *tt_len; +- tvlv_len += change_offset; ++ if (check_add_overflow(*tt_len, change_offset, &tvlv_len)) { ++ tvlv_len = 0; ++ goto out; ++ } + + *tt_data = kmalloc(tvlv_len, GFP_ATOMIC); + if (!*tt_data) { +-- +2.53.0 + diff --git a/queue-5.15/iio-light-bh1780-fix-pm-runtime-leak-on-error-path.patch b/queue-5.15/iio-light-bh1780-fix-pm-runtime-leak-on-error-path.patch new file mode 100644 index 0000000000..9c15750fe0 --- /dev/null +++ b/queue-5.15/iio-light-bh1780-fix-pm-runtime-leak-on-error-path.patch @@ -0,0 +1,48 @@ +From 39e0a0b4e6be4402b29ce0b4f88a79a9aaa709f3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 17:34:43 +0300 +Subject: iio: light: bh1780: fix PM runtime leak on error path + +From: Antoniu Miclaus + +commit dd72e6c3cdea05cad24e99710939086f7a113fb5 upstream. + +Move pm_runtime_put_autosuspend() before the error check to ensure +the PM runtime reference count is always decremented after +pm_runtime_get_sync(), regardless of whether the read operation +succeeds or fails. + +Fixes: 1f0477f18306 ("iio: light: new driver for the ROHM BH1780") +Signed-off-by: Antoniu Miclaus +Reviewed-by: Linus Walleij +Cc: +Signed-off-by: Jonathan Cameron +[ moved both pm_runtime_mark_last_busy() and pm_runtime_put_autosuspend() before the error check instead of just pm_runtime_put_autosuspend() ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Elizaveta Tereshkina +Signed-off-by: Sasha Levin +--- + drivers/iio/light/bh1780.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/iio/light/bh1780.c b/drivers/iio/light/bh1780.c +index abbf2e662e7dbc..e0a72ff2ebf8b1 100644 +--- a/drivers/iio/light/bh1780.c ++++ b/drivers/iio/light/bh1780.c +@@ -109,10 +109,10 @@ static int bh1780_read_raw(struct iio_dev *indio_dev, + case IIO_LIGHT: + pm_runtime_get_sync(&bh1780->client->dev); + value = bh1780_read_word(bh1780, BH1780_REG_DLOW); +- if (value < 0) +- return value; + pm_runtime_mark_last_busy(&bh1780->client->dev); + pm_runtime_put_autosuspend(&bh1780->client->dev); ++ if (value < 0) ++ return value; + *val = value; + + return IIO_VAL_INT; +-- +2.53.0 + diff --git a/queue-5.15/series b/queue-5.15/series index eb8c434509..46ff726768 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -7,3 +7,7 @@ drm-amd-display-bound-vbios-record-chain-walk-loops.patch ip6_vti-set-netns_immutable-on-the-fallback-device.patch drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch +batman-adv-tt-reject-oversized-local-tvlv-buffers.patch +batman-adv-tt-prevent-tvlv-entry-number-overflow.patch +iio-light-bh1780-fix-pm-runtime-leak-on-error-path.patch +vfio-iommu_type1-replace-kfree-with-kvfree.patch diff --git a/queue-5.15/vfio-iommu_type1-replace-kfree-with-kvfree.patch b/queue-5.15/vfio-iommu_type1-replace-kfree-with-kvfree.patch new file mode 100644 index 0000000000..af6dd26cc8 --- /dev/null +++ b/queue-5.15/vfio-iommu_type1-replace-kfree-with-kvfree.patch @@ -0,0 +1,38 @@ +From 9641c9aebf8835b18240983f810a8b5af2ac2bf2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Dec 2021 01:16:00 -0800 +Subject: vfio/iommu_type1: replace kfree with kvfree + +From: Jiacheng Shi + +[ Upstream commit 2bed2ced40c97b8540ff38df0149e8ecb2bf4c65 ] + +Variables allocated by kvzalloc should not be freed by kfree. +Because they may be allocated by vmalloc. +So we replace kfree with kvfree here. + +Fixes: d6a4c185660c ("vfio iommu: Implementation of ioctl for dirty pages tracking") +Signed-off-by: Jiacheng Shi +Link: https://lore.kernel.org/r/20211212091600.2560-1-billsjc@sjtu.edu.cn +Signed-off-by: Alex Williamson +Signed-off-by: Sasha Levin +--- + drivers/vfio/vfio_iommu_type1.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c +index 6a89bbec738f62..2bc79eee63d524 100644 +--- a/drivers/vfio/vfio_iommu_type1.c ++++ b/drivers/vfio/vfio_iommu_type1.c +@@ -262,7 +262,7 @@ static int vfio_dma_bitmap_alloc(struct vfio_dma *dma, size_t pgsize) + + static void vfio_dma_bitmap_free(struct vfio_dma *dma) + { +- kfree(dma->bitmap); ++ kvfree(dma->bitmap); + dma->bitmap = NULL; + } + +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tt-prevent-tvlv-entry-number-overflow.patch b/queue-6.1/batman-adv-tt-prevent-tvlv-entry-number-overflow.patch new file mode 100644 index 0000000000..f0cfb1f2b4 --- /dev/null +++ b/queue-6.1/batman-adv-tt-prevent-tvlv-entry-number-overflow.patch @@ -0,0 +1,80 @@ +From 645cecb9c727b252ce4ae685f7fb45b1103e5596 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 21:45:29 +0200 +Subject: batman-adv: tt: prevent TVLV entry number overflow + +From: Sven Eckelmann + +commit 99d9958fa10fb684b2a8e2c48a8d704122721420 upstream. + +The helpers to prepare the buffers for the local and global TT based +replies are trying to sum up all TT entries which can be found for each +VLAN. In theory, this sum can be too big for an u16 and therefore overflow. +A too small buffer would then be allocated for the TVLV. + +The too small buffer will be handled gracefully by +batadv_tt_tvlv_generate() and is not causing a buffer overflow - just a +truncated reply. But this overflow shouldn't have happened in the first and +the too small buffer should never have been allocated when an overflow was +detected. + +Cc: stable@kernel.org +Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 20 +++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index e0503c8f24c353..53d2aadcafa154 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -850,11 +850,18 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + u16 total_entries = 0; + u8 *tt_change_ptr; + int vlan_entries; ++ u16 sum_entries; + + spin_lock_bh(&orig_node->vlan_list_lock); + hlist_for_each_entry(vlan, &orig_node->vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); +- total_entries += vlan_entries; ++ ++ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { ++ *tt_len = 0; ++ goto out; ++ } ++ ++ total_entries = sum_entries; + num_vlan++; + } + +@@ -941,15 +948,22 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + struct batadv_softif_vlan *vlan; + size_t change_offset; + u16 num_vlan = 0; +- u16 vlan_entries = 0; + u16 total_entries = 0; + u16 tvlv_len; + u8 *tt_change_ptr; ++ int vlan_entries; ++ u16 sum_entries; + + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); +- total_entries += vlan_entries; ++ ++ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { ++ tvlv_len = 0; ++ goto out; ++ } ++ ++ total_entries = sum_entries; + num_vlan++; + } + +-- +2.53.0 + diff --git a/queue-6.1/kvm-nvmx-add-a-helper-to-get-highest-pending-from-po.patch b/queue-6.1/kvm-nvmx-add-a-helper-to-get-highest-pending-from-po.patch new file mode 100644 index 0000000000..a974c0d4de --- /dev/null +++ b/queue-6.1/kvm-nvmx-add-a-helper-to-get-highest-pending-from-po.patch @@ -0,0 +1,85 @@ +From 4d698ca25b014b21734bb6df02bf16b7e4b9adfa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 16:31:05 -0400 +Subject: KVM: nVMX: Add a helper to get highest pending from Posted Interrupt + vector + +From: Sean Christopherson + +commit d83c36d822be44db4bad0c43bea99c8908f54117 upstream. + +Add a helper to retrieve the highest pending vector given a Posted +Interrupt descriptor. While the actual operation is straightforward, it's +surprisingly easy to mess up, e.g. if one tries to reuse lapic.c's +find_highest_vector(), which doesn't work with PID.PIR due to the APIC's +IRR and ISR component registers being physically discontiguous (they're +4-byte registers aligned at 16-byte intervals). + +To make PIR handling more consistent with respect to IRR and ISR handling, +return -1 to indicate "no interrupt pending". + +Cc: stable@vger.kernel.org +Link: https://lore.kernel.org/r/20240607172609.3205077-2-seanjc@google.com +Signed-off-by: Sean Christopherson +[ Nicholas Dudar: backport to 6.1.y. 6.1.y defines struct pi_desc in + posted_intr.h and predates the move to , so the helper + and the include go in posted_intr.h. ] +Signed-off-by: Nicholas Dudar +Signed-off-by: Sasha Levin +--- + arch/x86/kvm/vmx/nested.c | 5 +++-- + arch/x86/kvm/vmx/posted_intr.h | 10 ++++++++++ + 2 files changed, 13 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c +index bdc462944cb082..7d8e18dbe8531b 100644 +--- a/arch/x86/kvm/vmx/nested.c ++++ b/arch/x86/kvm/vmx/nested.c +@@ -12,6 +12,7 @@ + #include "mmu.h" + #include "nested.h" + #include "pmu.h" ++#include "posted_intr.h" + #include "sgx.h" + #include "trace.h" + #include "vmx.h" +@@ -3818,8 +3819,8 @@ static int vmx_complete_nested_posted_interrupt(struct kvm_vcpu *vcpu) + if (!pi_test_and_clear_on(vmx->nested.pi_desc)) + return 0; + +- max_irr = find_last_bit((unsigned long *)vmx->nested.pi_desc->pir, 256); +- if (max_irr != 256) { ++ max_irr = pi_find_highest_vector(vmx->nested.pi_desc); ++ if (max_irr > 0) { + vapic_page = vmx->nested.virtual_apic_map.hva; + if (!vapic_page) + goto mmio_needed; +diff --git a/arch/x86/kvm/vmx/posted_intr.h b/arch/x86/kvm/vmx/posted_intr.h +index 26992076552ef1..88cea0dac7204b 100644 +--- a/arch/x86/kvm/vmx/posted_intr.h ++++ b/arch/x86/kvm/vmx/posted_intr.h +@@ -2,6 +2,8 @@ + #ifndef __KVM_X86_VMX_POSTED_INTR_H + #define __KVM_X86_VMX_POSTED_INTR_H + ++#include ++ + #define POSTED_INTR_ON 0 + #define POSTED_INTR_SN 1 + +@@ -103,4 +105,12 @@ int vmx_pi_update_irte(struct kvm *kvm, unsigned int host_irq, + uint32_t guest_irq, bool set); + void vmx_pi_start_assignment(struct kvm *kvm); + ++static inline int pi_find_highest_vector(struct pi_desc *pi_desc) ++{ ++ int vec; ++ ++ vec = find_last_bit((unsigned long *)pi_desc->pir, 256); ++ return vec < 256 ? vec : -1; ++} ++ + #endif /* __KVM_X86_VMX_POSTED_INTR_H */ +-- +2.53.0 + diff --git a/queue-6.1/kvm-nvmx-check-for-pending-posted-interrupts-when-lo.patch b/queue-6.1/kvm-nvmx-check-for-pending-posted-interrupts-when-lo.patch new file mode 100644 index 0000000000..71aba5bc45 --- /dev/null +++ b/queue-6.1/kvm-nvmx-check-for-pending-posted-interrupts-when-lo.patch @@ -0,0 +1,86 @@ +From a17c10283ca19ad7d0a629310f8e27d80f2ec122 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 16:31:06 -0400 +Subject: KVM: nVMX: Check for pending posted interrupts when looking for + nested events + +From: Sean Christopherson + +commit 27c4fa42b11af780d49ce704f7fa67b3c2544df4 upstream. + +Check for pending (and notified!) posted interrupts when checking if L2 +has a pending wake event, as fully posted/notified virtual interrupt is a +valid wake event for HLT. + +Note that KVM must check vmx->nested.pi_pending to avoid prematurely +waking L2, e.g. even if KVM sees a non-zero PID.PIR and PID.0N=1, the +virtual interrupt won't actually be recognized until a notification IRQ is +received by the vCPU or the vCPU does (nested) VM-Enter. + +Fixes: 26844fee6ade ("KVM: x86: never write to memory from kvm_vcpu_check_block()") +Cc: stable@vger.kernel.org +Cc: Maxim Levitsky +Reported-by: Jim Mattson +Closes: https://lore.kernel.org/all/20231207010302.2240506-1-jmattson@google.com +Link: https://lore.kernel.org/r/20240607172609.3205077-5-seanjc@google.com +Signed-off-by: Sean Christopherson +[ Nicholas Dudar: backport to 6.1.y. Prerequisite for the next patch, which + folds its check into the vmx_has_nested_events() body this patch builds. + Applies cleanly. The for_injection path still returns preemption_timer || + mtf, as the previous 6.1.y body did. ] +Signed-off-by: Nicholas Dudar +Signed-off-by: Sasha Levin +--- + arch/x86/kvm/vmx/nested.c | 36 ++++++++++++++++++++++++++++++++++-- + 1 file changed, 34 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c +index 7d8e18dbe8531b..ad07e83d2c1d5b 100644 +--- a/arch/x86/kvm/vmx/nested.c ++++ b/arch/x86/kvm/vmx/nested.c +@@ -3953,8 +3953,40 @@ static bool nested_vmx_preemption_timer_pending(struct kvm_vcpu *vcpu) + + static bool vmx_has_nested_events(struct kvm_vcpu *vcpu, bool for_injection) + { +- return nested_vmx_preemption_timer_pending(vcpu) || +- to_vmx(vcpu)->nested.mtf_pending; ++ struct vcpu_vmx *vmx = to_vmx(vcpu); ++ void *vapic = vmx->nested.virtual_apic_map.hva; ++ int max_irr, vppr; ++ ++ if (nested_vmx_preemption_timer_pending(vcpu) || ++ vmx->nested.mtf_pending) ++ return true; ++ ++ /* ++ * Virtual Interrupt Delivery doesn't require manual injection. Either ++ * the interrupt is already in GUEST_RVI and will be recognized by CPU ++ * at VM-Entry, or there is a KVM_REQ_EVENT pending and KVM will move ++ * the interrupt from the PIR to RVI prior to entering the guest. ++ */ ++ if (for_injection) ++ return false; ++ ++ if (!nested_cpu_has_vid(get_vmcs12(vcpu)) || ++ __vmx_interrupt_blocked(vcpu)) ++ return false; ++ ++ if (!vapic) ++ return false; ++ ++ vppr = *((u32 *)(vapic + APIC_PROCPRI)); ++ ++ if (vmx->nested.pi_pending && vmx->nested.pi_desc && ++ pi_test_on(vmx->nested.pi_desc)) { ++ max_irr = pi_find_highest_vector(vmx->nested.pi_desc); ++ if (max_irr > 0 && (max_irr & 0xf0) > (vppr & 0xf0)) ++ return true; ++ } ++ ++ return false; + } + + /* +-- +2.53.0 + diff --git a/queue-6.1/kvm-nvmx-fold-requested-virtual-interrupt-check-into.patch b/queue-6.1/kvm-nvmx-fold-requested-virtual-interrupt-check-into.patch new file mode 100644 index 0000000000..b1ef12a241 --- /dev/null +++ b/queue-6.1/kvm-nvmx-fold-requested-virtual-interrupt-check-into.patch @@ -0,0 +1,148 @@ +From 92d8cc54210672204f960849fc948f3614ea8702 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 16:31:07 -0400 +Subject: KVM: nVMX: Fold requested virtual interrupt check into + has_nested_events() + +From: Sean Christopherson + +commit 321ef62b0c5f6f57bb8500a2ca5986052675abbf upstream. + +Check for a Requested Virtual Interrupt, i.e. a virtual interrupt that is +pending delivery, in vmx_has_nested_events() and drop the one-off +kvm_x86_ops.guest_apic_has_interrupt() hook. + +In addition to dropping a superfluous hook, this fixes a bug where KVM +would incorrectly treat virtual interrupts _for L2_ as always enabled due +to kvm_arch_interrupt_allowed(), by way of vmx_interrupt_blocked(), +treating IRQs as enabled if L2 is active and vmcs12 is configured to exit +on IRQs, i.e. KVM would treat a virtual interrupt for L2 as a valid wake +event based on L1's IRQ blocking status. + +Cc: stable@vger.kernel.org +Link: https://lore.kernel.org/r/20240607172609.3205077-6-seanjc@google.com +Signed-off-by: Sean Christopherson +[ Nicholas Dudar: backport to 6.1.y. 6.1.y predates the vmx main.c / + x86_ops.h split, so drop .guest_apic_has_interrupt from vmx_x86_ops in + vmx.c rather than vt_x86_ops in main.c. The function is static in vmx.c, so + upstream's x86_ops.h prototype removal does not apply. 6.1.y keeps the + current hwapic_isr_update signature. ] +Signed-off-by: Nicholas Dudar +Signed-off-by: Sasha Levin +--- + arch/x86/include/asm/kvm-x86-ops.h | 1 - + arch/x86/include/asm/kvm_host.h | 1 - + arch/x86/kvm/vmx/nested.c | 4 ++++ + arch/x86/kvm/vmx/vmx.c | 21 --------------------- + arch/x86/kvm/x86.c | 10 +--------- + 5 files changed, 5 insertions(+), 32 deletions(-) + +diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h +index c068565fe95474..1cfe83263b213b 100644 +--- a/arch/x86/include/asm/kvm-x86-ops.h ++++ b/arch/x86/include/asm/kvm-x86-ops.h +@@ -81,7 +81,6 @@ KVM_X86_OP(check_apicv_inhibit_reasons) + KVM_X86_OP(refresh_apicv_exec_ctrl) + KVM_X86_OP_OPTIONAL(hwapic_irr_update) + KVM_X86_OP_OPTIONAL(hwapic_isr_update) +-KVM_X86_OP_OPTIONAL_RET0(guest_apic_has_interrupt) + KVM_X86_OP_OPTIONAL(load_eoi_exitmap) + KVM_X86_OP_OPTIONAL(set_virtual_apic_mode) + KVM_X86_OP_OPTIONAL(set_apic_access_page_addr) +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index fe5c0f86ae389d..31395c43416dd7 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -1549,7 +1549,6 @@ struct kvm_x86_ops { + void (*refresh_apicv_exec_ctrl)(struct kvm_vcpu *vcpu); + void (*hwapic_irr_update)(struct kvm_vcpu *vcpu, int max_irr); + void (*hwapic_isr_update)(struct kvm_vcpu *vcpu, int isr); +- bool (*guest_apic_has_interrupt)(struct kvm_vcpu *vcpu); + void (*load_eoi_exitmap)(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap); + void (*set_virtual_apic_mode)(struct kvm_vcpu *vcpu); + void (*set_apic_access_page_addr)(struct kvm_vcpu *vcpu); +diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c +index ad07e83d2c1d5b..f7a790a28b9eee 100644 +--- a/arch/x86/kvm/vmx/nested.c ++++ b/arch/x86/kvm/vmx/nested.c +@@ -3979,6 +3979,10 @@ static bool vmx_has_nested_events(struct kvm_vcpu *vcpu, bool for_injection) + + vppr = *((u32 *)(vapic + APIC_PROCPRI)); + ++ max_irr = vmx_get_rvi(); ++ if ((max_irr & 0xf0) > (vppr & 0xf0)) ++ return true; ++ + if (vmx->nested.pi_pending && vmx->nested.pi_desc && + pi_test_on(vmx->nested.pi_desc)) { + max_irr = pi_find_highest_vector(vmx->nested.pi_desc); +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index e5d162e97f5031..2e6454e4cca4b4 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -4063,26 +4063,6 @@ void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu) + } + } + +-static bool vmx_guest_apic_has_interrupt(struct kvm_vcpu *vcpu) +-{ +- struct vcpu_vmx *vmx = to_vmx(vcpu); +- void *vapic_page; +- u32 vppr; +- int rvi; +- +- if (WARN_ON_ONCE(!is_guest_mode(vcpu)) || +- !nested_cpu_has_vid(get_vmcs12(vcpu)) || +- WARN_ON_ONCE(!vmx->nested.virtual_apic_map.gfn)) +- return false; +- +- rvi = vmx_get_rvi(); +- +- vapic_page = vmx->nested.virtual_apic_map.hva; +- vppr = *((u32 *)(vapic_page + APIC_PROCPRI)); +- +- return ((rvi & 0xf0) > (vppr & 0xf0)); +-} +- + static void vmx_msr_filter_changed(struct kvm_vcpu *vcpu) + { + struct vcpu_vmx *vmx = to_vmx(vcpu); +@@ -8266,7 +8246,6 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { + .check_apicv_inhibit_reasons = vmx_check_apicv_inhibit_reasons, + .hwapic_irr_update = vmx_hwapic_irr_update, + .hwapic_isr_update = vmx_hwapic_isr_update, +- .guest_apic_has_interrupt = vmx_guest_apic_has_interrupt, + .sync_pir_to_irr = vmx_sync_pir_to_irr, + .deliver_interrupt = vmx_deliver_interrupt, + .dy_apicv_has_pending_interrupt = pi_has_pending_interrupt, +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index 10ef8a4353b32e..208a713d7ecd75 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -13046,12 +13046,6 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm, + kvm_page_track_flush_slot(kvm, slot); + } + +-static inline bool kvm_guest_apic_has_interrupt(struct kvm_vcpu *vcpu) +-{ +- return (is_guest_mode(vcpu) && +- static_call(kvm_x86_guest_apic_has_interrupt)(vcpu)); +-} +- + static inline bool kvm_vcpu_has_events(struct kvm_vcpu *vcpu) + { + if (!list_empty_careful(&vcpu->async_pf.done)) +@@ -13077,9 +13071,7 @@ static inline bool kvm_vcpu_has_events(struct kvm_vcpu *vcpu) + static_call(kvm_x86_smi_allowed)(vcpu, false))) + return true; + +- if (kvm_arch_interrupt_allowed(vcpu) && +- (kvm_cpu_has_interrupt(vcpu) || +- kvm_guest_apic_has_interrupt(vcpu))) ++ if (kvm_arch_interrupt_allowed(vcpu) && kvm_cpu_has_interrupt(vcpu)) + return true; + + if (kvm_hv_has_stimer_pending(vcpu)) +-- +2.53.0 + diff --git a/queue-6.1/series b/queue-6.1/series index 33d9e82774..b64168d7f7 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -14,3 +14,7 @@ selftests-bpf-move-get_time_ns-to-testing_helpers.h.patch selftests-bpf-check-for-timeout-in-perf_link-test.patch drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch +batman-adv-tt-prevent-tvlv-entry-number-overflow.patch +kvm-nvmx-add-a-helper-to-get-highest-pending-from-po.patch +kvm-nvmx-check-for-pending-posted-interrupts-when-lo.patch +kvm-nvmx-fold-requested-virtual-interrupt-check-into.patch diff --git a/queue-6.12/eventpoll-drop-vestigial-__-prefix-from-ep_remove_-f.patch b/queue-6.12/eventpoll-drop-vestigial-__-prefix-from-ep_remove_-f.patch new file mode 100644 index 0000000000..bb271bff2e --- /dev/null +++ b/queue-6.12/eventpoll-drop-vestigial-__-prefix-from-ep_remove_-f.patch @@ -0,0 +1,70 @@ +From 851deb0057ba0b1d96a60ebecc260e2696b3a840 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 16:58:44 +0200 +Subject: eventpoll: drop vestigial __ prefix from ep_remove_{file,epi}() + +From: Christian Brauner + +[ Upstream commit 0feaf644f7180c4a91b6b405a881afbfd958f1cf ] + +With __ep_remove() gone, the double-underscore on __ep_remove_file() +and __ep_remove_epi() no longer contrasts with a __-less parent and +just reads as noise. Rename both to ep_remove_file() and +ep_remove_epi(). No functional change. + +Signed-off-by: Christian Brauner (Amutable) +Stable-dep-of: a6dc643c6931 ("eventpoll: fix ep_remove struct eventpoll / struct file UAF") +Signed-off-by: Quentin Schulz +Signed-off-by: Sasha Levin +--- + fs/eventpoll.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/fs/eventpoll.c b/fs/eventpoll.c +index 3ac8a26c3522f6..dc747f382dd954 100644 +--- a/fs/eventpoll.c ++++ b/fs/eventpoll.c +@@ -801,7 +801,7 @@ static void ep_free(struct eventpoll *ep) + * Called with &file->f_lock held, + * returns with it released + */ +-static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, ++static void ep_remove_file(struct eventpoll *ep, struct epitem *epi, + struct file *file) + { + struct epitems_head *to_free = NULL; +@@ -825,7 +825,7 @@ static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, + free_ephead(to_free); + } + +-static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi) ++static bool ep_remove_epi(struct eventpoll *ep, struct epitem *epi) + { + lockdep_assert_held(&ep->mtx); + +@@ -871,9 +871,9 @@ static void ep_remove_safe(struct eventpoll *ep, struct epitem *epi) + spin_unlock(&file->f_lock); + return; + } +- __ep_remove_file(ep, epi, file); ++ ep_remove_file(ep, epi, file); + +- if (__ep_remove_epi(ep, epi)) ++ if (ep_remove_epi(ep, epi)) + WARN_ON_ONCE(ep_refcount_dec_and_test(ep)); + } + +@@ -1118,8 +1118,8 @@ void eventpoll_release_file(struct file *file) + ep_unregister_pollwait(ep, epi); + + spin_lock(&file->f_lock); +- __ep_remove_file(ep, epi, file); +- dispose = __ep_remove_epi(ep, epi); ++ ep_remove_file(ep, epi, file); ++ dispose = ep_remove_epi(ep, epi); + + mutex_unlock(&ep->mtx); + +-- +2.53.0 + diff --git a/queue-6.12/eventpoll-fix-ep_remove-struct-eventpoll-struct-file.patch b/queue-6.12/eventpoll-fix-ep_remove-struct-eventpoll-struct-file.patch new file mode 100644 index 0000000000..48c0ac0bb9 --- /dev/null +++ b/queue-6.12/eventpoll-fix-ep_remove-struct-eventpoll-struct-file.patch @@ -0,0 +1,100 @@ +From 381447b51758bdc1f73a47bb030a8097d5d41077 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 16:58:47 +0200 +Subject: eventpoll: fix ep_remove struct eventpoll / struct file UAF + +From: Christian Brauner + +[ Upstream commit a6dc643c69311677c574a0f17a3f4d66a5f3744b ] + +ep_remove() (via ep_remove_file()) cleared file->f_ep under +file->f_lock but then kept using @file inside the critical section +(is_file_epoll(), hlist_del_rcu() through the head, spin_unlock). +A concurrent __fput() taking the eventpoll_release() fastpath in +that window observed the transient NULL, skipped +eventpoll_release_file() and ran to f_op->release / file_free(). + +For the epoll-watches-epoll case, f_op->release is +ep_eventpoll_release() -> ep_clear_and_put() -> ep_free(), which +kfree()s the watched struct eventpoll. Its embedded ->refs +hlist_head is exactly where epi->fllink.pprev points, so the +subsequent hlist_del_rcu()'s "*pprev = next" scribbles into freed +kmalloc-192 memory. + +In addition, struct file is SLAB_TYPESAFE_BY_RCU, so the slot +backing @file could be recycled by alloc_empty_file() -- +reinitializing f_lock and f_ep -- while ep_remove() is still +nominally inside that lock. The upshot is an attacker-controllable +kmem_cache_free() against the wrong slab cache. + +Pin @file via epi_fget() at the top of ep_remove() and gate the +critical section on the pin succeeding. With the pin held @file +cannot reach refcount zero, which holds __fput() off and +transitively keeps the watched struct eventpoll alive across the +hlist_del_rcu() and the f_lock use, closing both UAFs. + +If the pin fails @file has already reached refcount zero and its +__fput() is in flight. Because we bailed before clearing f_ep, +that path takes the eventpoll_release() slow path into +eventpoll_release_file() and blocks on ep->mtx until the waiter +side's ep_clear_and_put() drops it. The bailed epi's share of +ep->refcount stays intact, so the trailing ep_refcount_dec_and_test() +in ep_clear_and_put() cannot free the eventpoll out from under +eventpoll_release_file(); the orphaned epi is then cleaned up +there. + +A successful pin also proves we are not racing +eventpoll_release_file() on this epi, so drop the now-redundant +re-check of epi->dying under f_lock. The cheap lockless +READ_ONCE(epi->dying) fast-path bailout stays. + +Fixes: 58c9b016e128 ("epoll: use refcount to reduce ep_mutex contention") +Reported-by: Jaeyoung Chung +Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-6-2470f9eec0f5@kernel.org +Signed-off-by: Christian Brauner (Amutable) +Signed-off-by: Quentin Schulz +Signed-off-by: Sasha Levin +--- + fs/eventpoll.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/fs/eventpoll.c b/fs/eventpoll.c +index 2993b76c21f682..22605fbc12ded5 100644 +--- a/fs/eventpoll.c ++++ b/fs/eventpoll.c +@@ -883,22 +883,26 @@ static bool ep_remove_epi(struct eventpoll *ep, struct epitem *epi) + */ + static void ep_remove(struct eventpoll *ep, struct epitem *epi) + { +- struct file *file = epi->ffd.file; ++ struct file *file __free(fput) = NULL; + + lockdep_assert_irqs_enabled(); + lockdep_assert_held(&ep->mtx); + + ep_unregister_pollwait(ep, epi); + +- /* sync with eventpoll_release_file() */ ++ /* cheap sync with eventpoll_release_file() */ + if (unlikely(READ_ONCE(epi->dying))) + return; + +- spin_lock(&file->f_lock); +- if (epi->dying) { +- spin_unlock(&file->f_lock); ++ /* ++ * If we manage to grab a reference it means we're not in ++ * eventpoll_release_file() and aren't going to be. ++ */ ++ file = epi_fget(epi); ++ if (!file) + return; +- } ++ ++ spin_lock(&file->f_lock); + ep_remove_file(ep, epi, file); + + if (ep_remove_epi(ep, epi)) +-- +2.53.0 + diff --git a/queue-6.12/eventpoll-kill-__ep_remove.patch b/queue-6.12/eventpoll-kill-__ep_remove.patch new file mode 100644 index 0000000000..c8cafbfa94 --- /dev/null +++ b/queue-6.12/eventpoll-kill-__ep_remove.patch @@ -0,0 +1,133 @@ +From 51131e558347cdc5cf60f668ec3e6c2a5cfb73f4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 16:58:43 +0200 +Subject: eventpoll: kill __ep_remove() + +From: Christian Brauner + +[ Upstream commit e9e5cd40d7c403e19f21d0f7b8b8ba3a76b58330 ] + +Remove the boolean conditional in __ep_remove() and restructure the code +so the check for racing with eventpoll_release_file() are only done in +the ep_remove_safe() path where they belong. + +Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-3-2470f9eec0f5@kernel.org +Signed-off-by: Christian Brauner (Amutable) +Stable-dep-of: a6dc643c6931 ("eventpoll: fix ep_remove struct eventpoll / struct file UAF") +Signed-off-by: Quentin Schulz +Signed-off-by: Sasha Levin +--- + fs/eventpoll.c | 67 ++++++++++++++++++++++---------------------------- + 1 file changed, 30 insertions(+), 37 deletions(-) + +diff --git a/fs/eventpoll.c b/fs/eventpoll.c +index 1cba4ae4a076bc..3ac8a26c3522f6 100644 +--- a/fs/eventpoll.c ++++ b/fs/eventpoll.c +@@ -797,49 +797,18 @@ static void ep_free(struct eventpoll *ep) + kfree_rcu(ep, rcu); + } + +-static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, struct file *file); +-static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi); +- +-/* +- * Removes a "struct epitem" from the eventpoll RB tree and deallocates +- * all the associated resources. Must be called with "mtx" held. +- * If the dying flag is set, do the removal only if force is true. +- * This prevents ep_clear_and_put() from dropping all the ep references +- * while running concurrently with eventpoll_release_file(). +- * Returns true if the eventpoll can be disposed. +- */ +-static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force) +-{ +- struct file *file = epi->ffd.file; +- +- lockdep_assert_irqs_enabled(); +- +- /* +- * Removes poll wait queue hooks. +- */ +- ep_unregister_pollwait(ep, epi); +- +- /* Remove the current item from the list of epoll hooks */ +- spin_lock(&file->f_lock); +- if (epi->dying && !force) { +- spin_unlock(&file->f_lock); +- return false; +- } +- +- __ep_remove_file(ep, epi, file); +- return __ep_remove_epi(ep, epi); +-} +- + /* + * Called with &file->f_lock held, + * returns with it released + */ +-static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, struct file *file) ++static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, ++ struct file *file) + { + struct epitems_head *to_free = NULL; + struct hlist_head *head = file->f_ep; + + lockdep_assert_held(&ep->mtx); ++ lockdep_assert_held(&file->f_lock); + + if (hlist_is_singular_node(&epi->fllink, head)) { + /* See eventpoll_release() for details. */ +@@ -886,7 +855,25 @@ static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi) + */ + static void ep_remove_safe(struct eventpoll *ep, struct epitem *epi) + { +- if (__ep_remove(ep, epi, false)) ++ struct file *file = epi->ffd.file; ++ ++ lockdep_assert_irqs_enabled(); ++ lockdep_assert_held(&ep->mtx); ++ ++ ep_unregister_pollwait(ep, epi); ++ ++ /* sync with eventpoll_release_file() */ ++ if (unlikely(READ_ONCE(epi->dying))) ++ return; ++ ++ spin_lock(&file->f_lock); ++ if (epi->dying) { ++ spin_unlock(&file->f_lock); ++ return; ++ } ++ __ep_remove_file(ep, epi, file); ++ ++ if (__ep_remove_epi(ep, epi)) + WARN_ON_ONCE(ep_refcount_dec_and_test(ep)); + } + +@@ -1118,7 +1105,7 @@ void eventpoll_release_file(struct file *file) + spin_lock(&file->f_lock); + if (file->f_ep && file->f_ep->first) { + epi = hlist_entry(file->f_ep->first, struct epitem, fllink); +- epi->dying = true; ++ WRITE_ONCE(epi->dying, true); + spin_unlock(&file->f_lock); + + /* +@@ -1127,7 +1114,13 @@ void eventpoll_release_file(struct file *file) + */ + ep = epi->ep; + mutex_lock(&ep->mtx); +- dispose = __ep_remove(ep, epi, true); ++ ++ ep_unregister_pollwait(ep, epi); ++ ++ spin_lock(&file->f_lock); ++ __ep_remove_file(ep, epi, file); ++ dispose = __ep_remove_epi(ep, epi); ++ + mutex_unlock(&ep->mtx); + + if (dispose && ep_refcount_dec_and_test(ep)) +-- +2.53.0 + diff --git a/queue-6.12/eventpoll-move-epi_fget-up.patch b/queue-6.12/eventpoll-move-epi_fget-up.patch new file mode 100644 index 0000000000..1e66f571f0 --- /dev/null +++ b/queue-6.12/eventpoll-move-epi_fget-up.patch @@ -0,0 +1,101 @@ +From 9e9cee9f453d44ebed0952904ef11a4473962a93 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 16:58:46 +0200 +Subject: eventpoll: move epi_fget() up + +From: Christian Brauner + +[ Upstream commit 86e87059e6d1fd5115a31949726450ed03c1073b ] + +We'll need it when removing files so move it up. No functional change. + +Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-5-2470f9eec0f5@kernel.org +Signed-off-by: Christian Brauner (Amutable) +Stable-dep-of: a6dc643c6931 ("eventpoll: fix ep_remove struct eventpoll / struct file UAF") +[file_ref_get(&file->f_ref) from original commit left as + atomic_long_inc_not_zero(&file->f_count) due to v6.12.y missing commit + 90ee6ed776c0 ("fs: port files to file_ref") and its dependent commit + 08ef26ea9ab3 ("fs: add file_ref")] +Signed-off-by: Quentin Schulz +Signed-off-by: Sasha Levin +--- + fs/eventpoll.c | 56 +++++++++++++++++++++++++------------------------- + 1 file changed, 28 insertions(+), 28 deletions(-) + +diff --git a/fs/eventpoll.c b/fs/eventpoll.c +index 27280ba4f3d5be..2993b76c21f682 100644 +--- a/fs/eventpoll.c ++++ b/fs/eventpoll.c +@@ -797,6 +797,34 @@ static void ep_free(struct eventpoll *ep) + kfree_rcu(ep, rcu); + } + ++/* ++ * The ffd.file pointer may be in the process of being torn down due to ++ * being closed, but we may not have finished eventpoll_release() yet. ++ * ++ * Normally, even with the atomic_long_inc_not_zero, the file may have ++ * been free'd and then gotten re-allocated to something else (since ++ * files are not RCU-delayed, they are SLAB_TYPESAFE_BY_RCU). ++ * ++ * But for epoll, users hold the ep->mtx mutex, and as such any file in ++ * the process of being free'd will block in eventpoll_release_file() ++ * and thus the underlying file allocation will not be free'd, and the ++ * file re-use cannot happen. ++ * ++ * For the same reason we can avoid a rcu_read_lock() around the ++ * operation - 'ffd.file' cannot go away even if the refcount has ++ * reached zero (but we must still not call out to ->poll() functions ++ * etc). ++ */ ++static struct file *epi_fget(const struct epitem *epi) ++{ ++ struct file *file; ++ ++ file = epi->ffd.file; ++ if (!atomic_long_inc_not_zero(&file->f_count)) ++ file = NULL; ++ return file; ++} ++ + /* + * Called with &file->f_lock held, + * returns with it released +@@ -989,34 +1017,6 @@ static __poll_t __ep_eventpoll_poll(struct file *file, poll_table *wait, int dep + return res; + } + +-/* +- * The ffd.file pointer may be in the process of being torn down due to +- * being closed, but we may not have finished eventpoll_release() yet. +- * +- * Normally, even with the atomic_long_inc_not_zero, the file may have +- * been free'd and then gotten re-allocated to something else (since +- * files are not RCU-delayed, they are SLAB_TYPESAFE_BY_RCU). +- * +- * But for epoll, users hold the ep->mtx mutex, and as such any file in +- * the process of being free'd will block in eventpoll_release_file() +- * and thus the underlying file allocation will not be free'd, and the +- * file re-use cannot happen. +- * +- * For the same reason we can avoid a rcu_read_lock() around the +- * operation - 'ffd.file' cannot go away even if the refcount has +- * reached zero (but we must still not call out to ->poll() functions +- * etc). +- */ +-static struct file *epi_fget(const struct epitem *epi) +-{ +- struct file *file; +- +- file = epi->ffd.file; +- if (!atomic_long_inc_not_zero(&file->f_count)) +- file = NULL; +- return file; +-} +- + /* + * Differs from ep_eventpoll_poll() in that internal callers already have + * the ep->mtx so we need to start from depth=1, such that mutex_lock_nested() +-- +2.53.0 + diff --git a/queue-6.12/eventpoll-rename-ep_remove_safe-back-to-ep_remove.patch b/queue-6.12/eventpoll-rename-ep_remove_safe-back-to-ep_remove.patch new file mode 100644 index 0000000000..833196b2a5 --- /dev/null +++ b/queue-6.12/eventpoll-rename-ep_remove_safe-back-to-ep_remove.patch @@ -0,0 +1,97 @@ +From 2ed0844a1214b3fa930312190f1ebb882dd4dca3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 16:58:45 +0200 +Subject: eventpoll: rename ep_remove_safe() back to ep_remove() + +From: Christian Brauner + +[ Upstream commit 0bade234723e40e4937be912e105785d6a51464e ] + +The current name is just confusing and doesn't clarify anything. + +Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-4-2470f9eec0f5@kernel.org +Signed-off-by: Christian Brauner (Amutable) +Stable-dep-of: a6dc643c6931 ("eventpoll: fix ep_remove struct eventpoll / struct file UAF") +Signed-off-by: Quentin Schulz +Signed-off-by: Sasha Levin +--- + fs/eventpoll.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/fs/eventpoll.c b/fs/eventpoll.c +index dc747f382dd954..27280ba4f3d5be 100644 +--- a/fs/eventpoll.c ++++ b/fs/eventpoll.c +@@ -853,7 +853,7 @@ static bool ep_remove_epi(struct eventpoll *ep, struct epitem *epi) + /* + * ep_remove variant for callers owing an additional reference to the ep + */ +-static void ep_remove_safe(struct eventpoll *ep, struct epitem *epi) ++static void ep_remove(struct eventpoll *ep, struct epitem *epi) + { + struct file *file = epi->ffd.file; + +@@ -900,7 +900,7 @@ static void ep_clear_and_put(struct eventpoll *ep) + + /* + * Walks through the whole tree and try to free each "struct epitem". +- * Note that ep_remove_safe() will not remove the epitem in case of a ++ * Note that ep_remove() will not remove the epitem in case of a + * racing eventpoll_release_file(); the latter will do the removal. + * At this point we are sure no poll callbacks will be lingering around. + * Since we still own a reference to the eventpoll struct, the loop can't +@@ -909,7 +909,7 @@ static void ep_clear_and_put(struct eventpoll *ep) + for (rbp = rb_first_cached(&ep->rbr); rbp; rbp = next) { + next = rb_next(rbp); + epi = rb_entry(rbp, struct epitem, rbn); +- ep_remove_safe(ep, epi); ++ ep_remove(ep, epi); + cond_resched(); + } + +@@ -1602,21 +1602,21 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, + mutex_unlock(&tep->mtx); + + /* +- * ep_remove_safe() calls in the later error paths can't lead to ++ * ep_remove() calls in the later error paths can't lead to + * ep_free() as the ep file itself still holds an ep reference. + */ + ep_get(ep); + + /* now check if we've created too many backpaths */ + if (unlikely(full_check && reverse_path_check())) { +- ep_remove_safe(ep, epi); ++ ep_remove(ep, epi); + return -EINVAL; + } + + if (epi->event.events & EPOLLWAKEUP) { + error = ep_create_wakeup_source(epi); + if (error) { +- ep_remove_safe(ep, epi); ++ ep_remove(ep, epi); + return error; + } + } +@@ -1640,7 +1640,7 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, + * high memory pressure. + */ + if (unlikely(!epq.epi)) { +- ep_remove_safe(ep, epi); ++ ep_remove(ep, epi); + return -ENOMEM; + } + +@@ -2329,7 +2329,7 @@ int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds, + * The eventpoll itself is still alive: the refcount + * can't go to zero here. + */ +- ep_remove_safe(ep, epi); ++ ep_remove(ep, epi); + error = 0; + } else { + error = -ENOENT; +-- +2.53.0 + diff --git a/queue-6.12/eventpoll-split-__ep_remove.patch b/queue-6.12/eventpoll-split-__ep_remove.patch new file mode 100644 index 0000000000..d74ed266a6 --- /dev/null +++ b/queue-6.12/eventpoll-split-__ep_remove.patch @@ -0,0 +1,83 @@ +From 94d7e05595a78e48b1e0b4c8341c2887bf3d2e74 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 16:58:42 +0200 +Subject: eventpoll: split __ep_remove() + +From: Christian Brauner + +[ Upstream commit 0f7bdfd413000985de09fc39eb9efa1e091a3ce0 ] + +Split __ep_remove() to delineate file removal from epoll item removal. + +Suggested-by: Linus Torvalds +Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-2-2470f9eec0f5@kernel.org +Signed-off-by: Christian Brauner (Amutable) +Stable-dep-of: a6dc643c6931 ("eventpoll: fix ep_remove struct eventpoll / struct file UAF") +Signed-off-by: Quentin Schulz +Signed-off-by: Sasha Levin +--- + fs/eventpoll.c | 27 +++++++++++++++++++++++---- + 1 file changed, 23 insertions(+), 4 deletions(-) + +diff --git a/fs/eventpoll.c b/fs/eventpoll.c +index 8f9dc2f4891ff5..1cba4ae4a076bc 100644 +--- a/fs/eventpoll.c ++++ b/fs/eventpoll.c +@@ -797,6 +797,9 @@ static void ep_free(struct eventpoll *ep) + kfree_rcu(ep, rcu); + } + ++static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, struct file *file); ++static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi); ++ + /* + * Removes a "struct epitem" from the eventpoll RB tree and deallocates + * all the associated resources. Must be called with "mtx" held. +@@ -808,8 +811,6 @@ static void ep_free(struct eventpoll *ep) + static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force) + { + struct file *file = epi->ffd.file; +- struct epitems_head *to_free; +- struct hlist_head *head; + + lockdep_assert_irqs_enabled(); + +@@ -825,8 +826,21 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force) + return false; + } + +- to_free = NULL; +- head = file->f_ep; ++ __ep_remove_file(ep, epi, file); ++ return __ep_remove_epi(ep, epi); ++} ++ ++/* ++ * Called with &file->f_lock held, ++ * returns with it released ++ */ ++static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, struct file *file) ++{ ++ struct epitems_head *to_free = NULL; ++ struct hlist_head *head = file->f_ep; ++ ++ lockdep_assert_held(&ep->mtx); ++ + if (hlist_is_singular_node(&epi->fllink, head)) { + /* See eventpoll_release() for details. */ + WRITE_ONCE(file->f_ep, NULL); +@@ -840,6 +854,11 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force) + hlist_del_rcu(&epi->fllink); + spin_unlock(&file->f_lock); + free_ephead(to_free); ++} ++ ++static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi) ++{ ++ lockdep_assert_held(&ep->mtx); + + rb_erase_cached(&epi->rbn, &ep->rbr); + +-- +2.53.0 + diff --git a/queue-6.12/eventpoll-use-hlist_is_singular_node-in-__ep_remove.patch b/queue-6.12/eventpoll-use-hlist_is_singular_node-in-__ep_remove.patch new file mode 100644 index 0000000000..36d0456ff9 --- /dev/null +++ b/queue-6.12/eventpoll-use-hlist_is_singular_node-in-__ep_remove.patch @@ -0,0 +1,38 @@ +From 0465405e6dd7594206d3acdf685aecefde95ddd5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 16:58:41 +0200 +Subject: eventpoll: use hlist_is_singular_node() in __ep_remove() + +From: Christian Brauner + +[ Upstream commit 3d9fd0abc94d8cd430cc7cd7d37ce5e5aae2cd2b ] + +Replace the open-coded "epi is the only entry in file->f_ep" check +with hlist_is_singular_node(). Same semantics, and the helper avoids +the head-cacheline access in the common false case. + +Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-1-2470f9eec0f5@kernel.org +Signed-off-by: Christian Brauner (Amutable) +Stable-dep-of: a6dc643c6931 ("eventpoll: fix ep_remove struct eventpoll / struct file UAF") +Signed-off-by: Quentin Schulz +Signed-off-by: Sasha Levin +--- + fs/eventpoll.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/eventpoll.c b/fs/eventpoll.c +index a860cb54658a3b..8f9dc2f4891ff5 100644 +--- a/fs/eventpoll.c ++++ b/fs/eventpoll.c +@@ -827,7 +827,7 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force) + + to_free = NULL; + head = file->f_ep; +- if (head->first == &epi->fllink && !epi->fllink.next) { ++ if (hlist_is_singular_node(&epi->fllink, head)) { + /* See eventpoll_release() for details. */ + WRITE_ONCE(file->f_ep, NULL); + if (!is_file_epoll(file)) { +-- +2.53.0 + diff --git a/queue-6.12/iio-light-bh1780-fix-pm-runtime-leak-on-error-path.patch b/queue-6.12/iio-light-bh1780-fix-pm-runtime-leak-on-error-path.patch new file mode 100644 index 0000000000..efcabb7d5a --- /dev/null +++ b/queue-6.12/iio-light-bh1780-fix-pm-runtime-leak-on-error-path.patch @@ -0,0 +1,48 @@ +From b783059f8941aeff714a79e69cb05b1e663b6da1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 17:34:43 +0300 +Subject: iio: light: bh1780: fix PM runtime leak on error path + +From: Antoniu Miclaus + +commit dd72e6c3cdea05cad24e99710939086f7a113fb5 upstream. + +Move pm_runtime_put_autosuspend() before the error check to ensure +the PM runtime reference count is always decremented after +pm_runtime_get_sync(), regardless of whether the read operation +succeeds or fails. + +Fixes: 1f0477f18306 ("iio: light: new driver for the ROHM BH1780") +Signed-off-by: Antoniu Miclaus +Reviewed-by: Linus Walleij +Cc: +Signed-off-by: Jonathan Cameron +[ moved both pm_runtime_mark_last_busy() and pm_runtime_put_autosuspend() before the error check instead of just pm_runtime_put_autosuspend() ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Elizaveta Tereshkina +Signed-off-by: Sasha Levin +--- + drivers/iio/light/bh1780.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/iio/light/bh1780.c b/drivers/iio/light/bh1780.c +index 475f44954f6110..f478f12640d536 100644 +--- a/drivers/iio/light/bh1780.c ++++ b/drivers/iio/light/bh1780.c +@@ -109,10 +109,10 @@ static int bh1780_read_raw(struct iio_dev *indio_dev, + case IIO_LIGHT: + pm_runtime_get_sync(&bh1780->client->dev); + value = bh1780_read_word(bh1780, BH1780_REG_DLOW); +- if (value < 0) +- return value; + pm_runtime_mark_last_busy(&bh1780->client->dev); + pm_runtime_put_autosuspend(&bh1780->client->dev); ++ if (value < 0) ++ return value; + *val = value; + + return IIO_VAL_INT; +-- +2.53.0 + diff --git a/queue-6.12/net-drop-the-lock-in-skb_may_tx_timestamp.patch b/queue-6.12/net-drop-the-lock-in-skb_may_tx_timestamp.patch new file mode 100644 index 0000000000..e18d3ec422 --- /dev/null +++ b/queue-6.12/net-drop-the-lock-in-skb_may_tx_timestamp.patch @@ -0,0 +1,120 @@ +From 0932116d93c9eb0cc6462922ddc0dc47e69a07e1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 17:20:12 +0200 +Subject: net: Drop the lock in skb_may_tx_timestamp() + +From: Sebastian Andrzej Siewior + +[ Upstream commit 983512f3a87fd8dc4c94dfa6b596b6e57df5aad7 ] + +skb_may_tx_timestamp() may acquire sock::sk_callback_lock. The lock must +not be taken in IRQ context, only softirq is okay. A few drivers receive +the timestamp via a dedicated interrupt and complete the TX timestamp +from that handler. This will lead to a deadlock if the lock is already +write-locked on the same CPU. + +Taking the lock can be avoided. The socket (pointed by the skb) will +remain valid until the skb is released. The ->sk_socket and ->file +member will be set to NULL once the user closes the socket which may +happen before the timestamp arrives. +If we happen to observe the pointer while the socket is closing but +before the pointer is set to NULL then we may use it because both +pointer (and the file's cred member) are RCU freed. + +Drop the lock. Use READ_ONCE() to obtain the individual pointer. Add a +matching WRITE_ONCE() where the pointer are cleared. + +Link: https://lore.kernel.org/all/20260205145104.iWinkXHv@linutronix.de +Fixes: b245be1f4db1a ("net-timestamp: no-payload only sysctl") +Signed-off-by: Sebastian Andrzej Siewior +Reviewed-by: Willem de Bruijn +Reviewed-by: Jason Xing +Reviewed-by: Eric Dumazet +Link: https://patch.msgid.link/20260220183858.N4ERjFW6@linutronix.de +Signed-off-by: Paolo Abeni +[adapted sk_set_socket() in include/net/sock.h to fix the conflict from + not having commit 5d6b58c932ec ("net: lockless sock_i_ino()") and the + additional previous changes required by it. + It comes down to just now having the lines of + if (sock) { + WRITE_ONCE(sk->sk_uid, SOCK_INODE(sock)->i_uid); + WRITE_ONCE(sk->sk_ino, SOCK_INODE(sock)->i_ino); + } + below the changed line. + I've tested this on a device running an nfs-root and did some + additional network stress-testing.] +Signed-off-by: Heiko Stuebner +Signed-off-by: Sasha Levin +--- + include/net/sock.h | 2 +- + net/core/skbuff.c | 23 ++++++++++++++++++----- + net/socket.c | 2 +- + 3 files changed, 20 insertions(+), 7 deletions(-) + +diff --git a/include/net/sock.h b/include/net/sock.h +index 0d77a87929f938..dffbaaa7fe493d 100644 +--- a/include/net/sock.h ++++ b/include/net/sock.h +@@ -2040,7 +2040,7 @@ static inline int sk_rx_queue_get(const struct sock *sk) + + static inline void sk_set_socket(struct sock *sk, struct socket *sock) + { +- sk->sk_socket = sock; ++ WRITE_ONCE(sk->sk_socket, sock); + } + + static inline wait_queue_head_t *sk_sleep(struct sock *sk) +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index 4be699bd3a17f7..fede3aa3ddbc10 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -5525,15 +5525,28 @@ static void __skb_complete_tx_timestamp(struct sk_buff *skb, + + static bool skb_may_tx_timestamp(struct sock *sk, bool tsonly) + { +- bool ret; ++ struct socket *sock; ++ struct file *file; ++ bool ret = false; + + if (likely(READ_ONCE(sysctl_tstamp_allow_data) || tsonly)) + return true; + +- read_lock_bh(&sk->sk_callback_lock); +- ret = sk->sk_socket && sk->sk_socket->file && +- file_ns_capable(sk->sk_socket->file, &init_user_ns, CAP_NET_RAW); +- read_unlock_bh(&sk->sk_callback_lock); ++ /* The sk pointer remains valid as long as the skb is. The sk_socket and ++ * file pointer may become NULL if the socket is closed. Both structures ++ * (including file->cred) are RCU freed which means they can be accessed ++ * within a RCU read section. ++ */ ++ rcu_read_lock(); ++ sock = READ_ONCE(sk->sk_socket); ++ if (!sock) ++ goto out; ++ file = READ_ONCE(sock->file); ++ if (!file) ++ goto out; ++ ret = file_ns_capable(file, &init_user_ns, CAP_NET_RAW); ++out: ++ rcu_read_unlock(); + return ret; + } + +diff --git a/net/socket.c b/net/socket.c +index 5c5dd9f6605a94..723bc3a1ba5cdd 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -652,7 +652,7 @@ static void __sock_release(struct socket *sock, struct inode *inode) + iput(SOCK_INODE(sock)); + return; + } +- sock->file = NULL; ++ WRITE_ONCE(sock->file, NULL); + } + + /** +-- +2.53.0 + diff --git a/queue-6.12/series b/queue-6.12/series index fd2301629c..56395f1fcc 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -9,3 +9,12 @@ io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch drm-xe-display-fix-oops-in-suspend-shutdown-without-.patch drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch +eventpoll-use-hlist_is_singular_node-in-__ep_remove.patch +eventpoll-split-__ep_remove.patch +eventpoll-kill-__ep_remove.patch +eventpoll-drop-vestigial-__-prefix-from-ep_remove_-f.patch +eventpoll-rename-ep_remove_safe-back-to-ep_remove.patch +eventpoll-move-epi_fget-up.patch +eventpoll-fix-ep_remove-struct-eventpoll-struct-file.patch +iio-light-bh1780-fix-pm-runtime-leak-on-error-path.patch +net-drop-the-lock-in-skb_may_tx_timestamp.patch diff --git a/queue-6.6/batman-adv-tt-prevent-tvlv-entry-number-overflow.patch b/queue-6.6/batman-adv-tt-prevent-tvlv-entry-number-overflow.patch new file mode 100644 index 0000000000..23491f2e7a --- /dev/null +++ b/queue-6.6/batman-adv-tt-prevent-tvlv-entry-number-overflow.patch @@ -0,0 +1,80 @@ +From 1b2939af68d968ba49ed30d70caa6069408ed018 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:16:39 +0200 +Subject: batman-adv: tt: prevent TVLV entry number overflow + +From: Sven Eckelmann + +commit 99d9958fa10fb684b2a8e2c48a8d704122721420 upstream. + +The helpers to prepare the buffers for the local and global TT based +replies are trying to sum up all TT entries which can be found for each +VLAN. In theory, this sum can be too big for an u16 and therefore overflow. +A too small buffer would then be allocated for the TVLV. + +The too small buffer will be handled gracefully by +batadv_tt_tvlv_generate() and is not causing a buffer overflow - just a +truncated reply. But this overflow shouldn't have happened in the first and +the too small buffer should never have been allocated when an overflow was +detected. + +Cc: stable@kernel.org +Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 20 +++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 4045ddefc29b47..7041cd69e20070 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -850,11 +850,18 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + u16 total_entries = 0; + u8 *tt_change_ptr; + int vlan_entries; ++ u16 sum_entries; + + spin_lock_bh(&orig_node->vlan_list_lock); + hlist_for_each_entry(vlan, &orig_node->vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); +- total_entries += vlan_entries; ++ ++ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { ++ *tt_len = 0; ++ goto out; ++ } ++ ++ total_entries = sum_entries; + num_vlan++; + } + +@@ -941,15 +948,22 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + struct batadv_softif_vlan *vlan; + size_t change_offset; + u16 num_vlan = 0; +- u16 vlan_entries = 0; + u16 total_entries = 0; + u16 tvlv_len; + u8 *tt_change_ptr; ++ int vlan_entries; ++ u16 sum_entries; + + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); +- total_entries += vlan_entries; ++ ++ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { ++ tvlv_len = 0; ++ goto out; ++ } ++ ++ total_entries = sum_entries; + num_vlan++; + } + +-- +2.53.0 + diff --git a/queue-6.6/series b/queue-6.6/series index a07ea87320..3b9cc6db80 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -4,3 +4,4 @@ drm-amd-display-bound-vbios-record-chain-walk-loops.patch ip6_vti-set-netns_immutable-on-the-fallback-device.patch drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch +batman-adv-tt-prevent-tvlv-entry-number-overflow.patch diff --git a/queue-7.0/lockd-fix-test-handling-when-not-all-permissions-are.patch b/queue-7.0/lockd-fix-test-handling-when-not-all-permissions-are.patch new file mode 100644 index 0000000000..baf5b742e7 --- /dev/null +++ b/queue-7.0/lockd-fix-test-handling-when-not-all-permissions-are.patch @@ -0,0 +1,254 @@ +From 56dae8562b2e5c209ec70fd26768e059905c1e51 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 16:47:50 -0400 +Subject: lockd: fix TEST handling when not all permissions are available. + +From: NeilBrown + +[ Upstream commit 0b474240327cebeff08ad429e8ed3cfc6c8ee816 ] + +The F_GETLK fcntl can work with either read access or write access or +both. It can query F_RDLCK and F_WRLCK locks in either case. + +However lockd currently treats F_GETLK similar to F_SETLK in that read +access is required to query an F_RDLCK lock and write access is required +to query a F_WRLCK lock. + +This is wrong and can cause problems - e.g. when qemu accesses a +read-only (e.g. iso) filesystem image over NFS (though why it queries +if it can get a write lock - I don't know. But it does, and this works +with local filesystems). + +So we need TEST requests to be handled differently. To do this: + +- change nlm_do_fopen() to accept O_RDWR as a mode and in that case + succeed if either a O_RDONLY or O_WRONLY file can be opened. +- change nlm_lookup_file() to accept a mode argument from caller, + instead of deducing base on lock time, and pass that on to nlm_do_fopen() +- change nlm4svc_retrieve_args() and nlmsvc_retrieve_args() to detect + TEST requests and pass O_RDWR as a mode to nlm_lookup_file, passing + the same mode as before for other requests. Also set + lock->fl.c.flc_file to whichever file is available for TEST requests. +- change nlmsvc_testlock() to also not calculate the mode, but to use + whatever was stored in lock->fl.c.flc_file. + +This behaviour of lockd - requesting O_WRONLY access to TEST for +exclusive locks - has been present at least since git history began. +However it was hidden until recently because knfsd ignored the access +requested by lockd and required only READ access for all locking +requests (unless the underlying filesystem provided an f_op->open +function which checked access permissions). + +The commit mentioned in Fixes: below changed nfsd_permission() to NOT +override the access request for LOCK requests and this exposed the bug +that we are now fixing. + +Note that there is another issue that this patch does not address. +The flock(.., LOCK_EX) call is permitted on a read-only file descriptor. +Linux NFS maps this to NLM locking as whole-file byte-range locks. +nfsd will see this as though it were fcntl( F_SETLK (F_WRLCK)) and will +now require write access, which it might not be able to get. +It is not clear if this is a problem in practice, or what the best +solution might be. So no attempt is made to address it. + +Reported-by: Tj +Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1128861 +Fixes: 4cc9b9f2bf4d ("nfsd: refine and rename NFSD_MAY_LOCK") +Reviewed-by: Jeff Layton +Signed-off-by: NeilBrown +Signed-off-by: Chuck Lever +Signed-off-by: Sasha Levin +--- + fs/lockd/svc4proc.c | 13 ++++++++++--- + fs/lockd/svclock.c | 4 +--- + fs/lockd/svcproc.c | 15 ++++++++++++--- + fs/lockd/svcsubs.c | 35 +++++++++++++++++++++++++---------- + include/linux/lockd/lockd.h | 2 +- + 5 files changed, 49 insertions(+), 20 deletions(-) + +diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c +index 4b6f18d977343d..75e020a8bfd072 100644 +--- a/fs/lockd/svc4proc.c ++++ b/fs/lockd/svc4proc.c +@@ -26,6 +26,8 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_host *host = NULL; + struct nlm_file *file = NULL; + struct nlm_lock *lock = &argp->lock; ++ bool is_test = (rqstp->rq_proc == NLMPROC_TEST || ++ rqstp->rq_proc == NLMPROC_TEST_MSG); + __be32 error = 0; + + /* nfsd callbacks must have been installed for this procedure */ +@@ -46,15 +48,20 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, + if (filp != NULL) { + int mode = lock_to_openmode(&lock->fl); + ++ if (is_test) ++ mode = O_RDWR; ++ + lock->fl.c.flc_flags = FL_POSIX; + +- error = nlm_lookup_file(rqstp, &file, lock); ++ error = nlm_lookup_file(rqstp, &file, lock, mode); + if (error) + goto no_locks; + *filp = file; +- + /* Set up the missing parts of the file_lock structure */ +- lock->fl.c.flc_file = file->f_file[mode]; ++ if (is_test) ++ lock->fl.c.flc_file = nlmsvc_file_file(file); ++ else ++ lock->fl.c.flc_file = file->f_file[mode]; + lock->fl.c.flc_pid = current->tgid; + lock->fl.fl_start = (loff_t)lock->lock_start; + lock->fl.fl_end = lock->lock_len ? +diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c +index abc65dc79f8543..382bf0d93ed1c1 100644 +--- a/fs/lockd/svclock.c ++++ b/fs/lockd/svclock.c +@@ -619,7 +619,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, + struct nlm_lock *conflock) + { + int error; +- int mode; + __be32 ret; + + dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n", +@@ -637,14 +636,13 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, + goto out; + } + +- mode = lock_to_openmode(&lock->fl); + locks_init_lock(&conflock->fl); + /* vfs_test_lock only uses start, end, and owner, but tests flc_file */ + conflock->fl.c.flc_file = lock->fl.c.flc_file; + conflock->fl.fl_start = lock->fl.fl_start; + conflock->fl.fl_end = lock->fl.fl_end; + conflock->fl.c.flc_owner = lock->fl.c.flc_owner; +- error = vfs_test_lock(file->f_file[mode], &conflock->fl); ++ error = vfs_test_lock(lock->fl.c.flc_file, &conflock->fl); + if (error) { + ret = nlm_lck_denied_nolocks; + goto out; +diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c +index 5817ef272332d9..d98e8d684376b7 100644 +--- a/fs/lockd/svcproc.c ++++ b/fs/lockd/svcproc.c +@@ -55,6 +55,8 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_host *host = NULL; + struct nlm_file *file = NULL; + struct nlm_lock *lock = &argp->lock; ++ bool is_test = (rqstp->rq_proc == NLMPROC_TEST || ++ rqstp->rq_proc == NLMPROC_TEST_MSG); + int mode; + __be32 error = 0; + +@@ -70,15 +72,22 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, + + /* Obtain file pointer. Not used by FREE_ALL call. */ + if (filp != NULL) { +- error = cast_status(nlm_lookup_file(rqstp, &file, lock)); ++ mode = lock_to_openmode(&lock->fl); ++ ++ if (is_test) ++ mode = O_RDWR; ++ ++ error = cast_status(nlm_lookup_file(rqstp, &file, lock, mode)); + if (error != 0) + goto no_locks; + *filp = file; + + /* Set up the missing parts of the file_lock structure */ +- mode = lock_to_openmode(&lock->fl); + lock->fl.c.flc_flags = FL_POSIX; +- lock->fl.c.flc_file = file->f_file[mode]; ++ if (is_test) ++ lock->fl.c.flc_file = nlmsvc_file_file(file); ++ else ++ lock->fl.c.flc_file = file->f_file[mode]; + lock->fl.c.flc_pid = current->tgid; + lock->fl.fl_lmops = &nlmsvc_lock_operations; + nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid); +diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c +index dd0214dcb69503..b83e6ecd5be34f 100644 +--- a/fs/lockd/svcsubs.c ++++ b/fs/lockd/svcsubs.c +@@ -82,18 +82,35 @@ int lock_to_openmode(struct file_lock *lock) + * + * We have to make sure we have the right credential to open + * the file. ++ * ++ * mode can be O_RDONLY(0), O_WRONLY(1) or O_RDWR(2). The latter ++ * means success can be achieved with EITHER O_RDONLY or O_WRONLY. ++ * It does NOT mean both read and write are required. + */ + static __be32 nlm_do_fopen(struct svc_rqst *rqstp, + struct nlm_file *file, int mode) + { +- struct file **fp = &file->f_file[mode]; +- __be32 nfserr; ++ __be32 nfserr = nlm_lck_denied_nolocks; ++ __be32 deferred = 0; ++ struct file **fp; ++ int m; + +- if (*fp) +- return 0; +- nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode); +- if (nfserr) +- dprintk("lockd: open failed (error %d)\n", nfserr); ++ for (m = O_RDONLY ; m <= O_WRONLY ; m++) { ++ if (mode != O_RDWR && mode != m) ++ continue; ++ ++ fp = &file->f_file[m]; ++ if (*fp) ++ return 0; ++ nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, m); ++ if (!nfserr) ++ return 0; ++ if (nfserr == nlm_drop_reply) ++ deferred = nfserr; ++ } ++ if (deferred) ++ return deferred; ++ dprintk("lockd: open failed (error %d)\n", ntohl(nfserr)); + return nfserr; + } + +@@ -103,17 +120,15 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp, + */ + __be32 + nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, +- struct nlm_lock *lock) ++ struct nlm_lock *lock, int mode) + { + struct nlm_file *file; + unsigned int hash; + __be32 nfserr; +- int mode; + + nlm_debug_print_fh("nlm_lookup_file", &lock->fh); + + hash = file_hash(&lock->fh); +- mode = lock_to_openmode(&lock->fl); + + /* Lock file table */ + mutex_lock(&nlm_file_mutex); +diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h +index 330e38776bb20d..fe5cdd4d66f484 100644 +--- a/include/linux/lockd/lockd.h ++++ b/include/linux/lockd/lockd.h +@@ -294,7 +294,7 @@ void nlmsvc_locks_init_private(struct file_lock *, struct nlm_host *, pid_t); + * File handling for the server personality + */ + __be32 nlm_lookup_file(struct svc_rqst *, struct nlm_file **, +- struct nlm_lock *); ++ struct nlm_lock *, int); + void nlm_release_file(struct nlm_file *); + void nlmsvc_put_lockowner(struct nlm_lockowner *); + void nlmsvc_release_lockowner(struct nlm_lock *); +-- +2.53.0 + diff --git a/queue-7.0/series b/queue-7.0/series index b09b947a96..d9a77aef58 100644 --- a/queue-7.0/series +++ b/queue-7.0/series @@ -1,2 +1,3 @@ io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch arm64-entry-fix-arm64-specific-rseq-brokenness.patch +lockd-fix-test-handling-when-not-all-permissions-are.patch