From: Sasha Levin Date: Wed, 22 Jul 2026 00:53:31 +0000 (-0400) Subject: Fixes for all trees X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=1410eadd075811b9f2aa01186a5db310fc54ec89;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- diff --git a/staging-5.10/nvmet-tcp-fix-potential-uaf-when-ddgst-mismatch.patch b/staging-5.10/nvmet-tcp-fix-potential-uaf-when-ddgst-mismatch.patch new file mode 100644 index 0000000000..5ae1fcc4ba --- /dev/null +++ b/staging-5.10/nvmet-tcp-fix-potential-uaf-when-ddgst-mismatch.patch @@ -0,0 +1,53 @@ +From 5a9b47a301f5605ceb0139be5225d5394bae0066 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 09:58:45 -0400 +Subject: nvmet-tcp: Fix potential UAF when ddgst mismatch + +From: Sagi Grimberg + +commit dbbd07d0a7020b80f6a7028e561908f7b83b3d5a upstream. + +Shivam Kumar found via vulnerability testing: +When data digest is enabled on an NVMe/TCP connection and a digest +mismatch occurs on a non-final H2C_DATA PDU during an R2T-based +data transfer, the digest error handler in nvmet_tcp_try_recv_ddgst() +calls nvmet_req_uninit() - which performs percpu_ref_put() on the +submission queue - but does NOT mark the command as completed. It +does not set cqe->status, does not modify rbytes_done, and does not +clear any flag. When the subsequent fatal error triggers queue +teardown, nvmet_tcp_uninit_data_in_cmds() iterates all commands, +checks nvmet_tcp_need_data_in() for each one, and finds that the +already-uninited command still appears to need data (because +rbytes_done < transfer_len and cqe->status == 0). It therefore calls +nvmet_req_uninit() a second time on the same command - a double +percpu_ref_put against a single percpu_ref_get. + +Reported-by: Shivam Kumar +Reviewed-by: Christoph Hellwig +Signed-off-by: Sagi Grimberg +Signed-off-by: Keith Busch +Signed-off-by: Shivam Kumar +Signed-off-by: Sasha Levin +--- + drivers/nvme/target/tcp.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c +index 3d9a830950ef26..27d001fd882168 100644 +--- a/drivers/nvme/target/tcp.c ++++ b/drivers/nvme/target/tcp.c +@@ -1227,8 +1227,10 @@ static int nvmet_tcp_try_recv_ddgst(struct nvmet_tcp_queue *queue) + queue->idx, cmd->req.cmd->common.command_id, + queue->pdu.cmd.hdr.type, le32_to_cpu(cmd->recv_ddgst), + le32_to_cpu(cmd->exp_ddgst)); +- if (!(cmd->flags & NVMET_TCP_F_INIT_FAILED)) ++ if (!(cmd->flags & NVMET_TCP_F_INIT_FAILED)) { ++ cmd->req.cqe->status = NVME_SC_CMD_SEQ_ERROR; + nvmet_req_uninit(&cmd->req); ++ } + nvmet_tcp_free_cmd_buffers(cmd); + nvmet_tcp_fatal_error(queue); + ret = -EPROTO; +-- +2.53.0 + diff --git a/staging-5.10/series b/staging-5.10/series new file mode 100644 index 0000000000..632f68e799 --- /dev/null +++ b/staging-5.10/series @@ -0,0 +1 @@ +nvmet-tcp-fix-potential-uaf-when-ddgst-mismatch.patch diff --git a/staging-5.15/nvmet-tcp-fix-potential-uaf-when-ddgst-mismatch.patch b/staging-5.15/nvmet-tcp-fix-potential-uaf-when-ddgst-mismatch.patch new file mode 100644 index 0000000000..a9d5e70fc2 --- /dev/null +++ b/staging-5.15/nvmet-tcp-fix-potential-uaf-when-ddgst-mismatch.patch @@ -0,0 +1,53 @@ +From bd820d84764444c7ea306ee629d6d89e0adeb844 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 09:54:38 -0400 +Subject: nvmet-tcp: Fix potential UAF when ddgst mismatch + +From: Sagi Grimberg + +commit dbbd07d0a7020b80f6a7028e561908f7b83b3d5a upstream. + +Shivam Kumar found via vulnerability testing: +When data digest is enabled on an NVMe/TCP connection and a digest +mismatch occurs on a non-final H2C_DATA PDU during an R2T-based +data transfer, the digest error handler in nvmet_tcp_try_recv_ddgst() +calls nvmet_req_uninit() - which performs percpu_ref_put() on the +submission queue - but does NOT mark the command as completed. It +does not set cqe->status, does not modify rbytes_done, and does not +clear any flag. When the subsequent fatal error triggers queue +teardown, nvmet_tcp_uninit_data_in_cmds() iterates all commands, +checks nvmet_tcp_need_data_in() for each one, and finds that the +already-uninited command still appears to need data (because +rbytes_done < transfer_len and cqe->status == 0). It therefore calls +nvmet_req_uninit() a second time on the same command - a double +percpu_ref_put against a single percpu_ref_get. + +Reported-by: Shivam Kumar +Reviewed-by: Christoph Hellwig +Signed-off-by: Sagi Grimberg +Signed-off-by: Keith Busch +Signed-off-by: Shivam Kumar +Signed-off-by: Sasha Levin +--- + drivers/nvme/target/tcp.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c +index 95081052dce78f..8fe51218441e46 100644 +--- a/drivers/nvme/target/tcp.c ++++ b/drivers/nvme/target/tcp.c +@@ -1239,8 +1239,10 @@ static int nvmet_tcp_try_recv_ddgst(struct nvmet_tcp_queue *queue) + queue->idx, cmd->req.cmd->common.command_id, + queue->pdu.cmd.hdr.type, le32_to_cpu(cmd->recv_ddgst), + le32_to_cpu(cmd->exp_ddgst)); +- if (!(cmd->flags & NVMET_TCP_F_INIT_FAILED)) ++ if (!(cmd->flags & NVMET_TCP_F_INIT_FAILED)) { ++ cmd->req.cqe->status = NVME_SC_CMD_SEQ_ERROR; + nvmet_req_uninit(&cmd->req); ++ } + nvmet_tcp_free_cmd_buffers(cmd); + nvmet_tcp_fatal_error(queue); + ret = -EPROTO; +-- +2.53.0 + diff --git a/staging-5.15/series b/staging-5.15/series new file mode 100644 index 0000000000..632f68e799 --- /dev/null +++ b/staging-5.15/series @@ -0,0 +1 @@ +nvmet-tcp-fix-potential-uaf-when-ddgst-mismatch.patch diff --git a/staging-6.1/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch b/staging-6.1/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch new file mode 100644 index 0000000000..c814a0c747 --- /dev/null +++ b/staging-6.1/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch @@ -0,0 +1,67 @@ +From 8589d7617aab572407fe8336f38d373d259fce3b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 23:07:15 +0800 +Subject: platform/x86/intel-uncore-freq: Fix current_freq_khz after CPU + hotplug +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guixiong Wei + +commit 6b63520ed14b17bbe9c2103debbd2152dde1fba3 upstream. + +When the last CPU of a legacy uncore die goes offline, +uncore_freq_remove_die_entry() clears control_cpu. During CPU hotplug +re-add, uncore_freq_add_entry() still populates sysfs attributes before +assigning the new control CPU. As a result, the current frequency read +returns -ENXIO and current_freq_khz is omitted from the recreated sysfs +group. + +Assign control_cpu before the initial read paths and before +create_attr_group() so sysfs recreation uses the new online CPU. If +sysfs creation fails, restore control_cpu to -1 to keep the error path +state consistent. + +Fixes: 4d73c6772ab7 ("platform/x86: intel-uncore-freq: Conditionally create attribute for read frequency") +Cc: stable@vger.kernel.org +Acked-by: Srinivas Pandruvada +Link: https://patch.msgid.link/20260602020752.3126-1-weiguixiong@bytedance.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +[weiguixiong: Adapt to legacy uncore_read() and create_attr_group() flow.] +Signed-off-by: Guixiong Wei +Signed-off-by: Sasha Levin +--- + .../intel/uncore-frequency/uncore-frequency-common.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +index dd2e654daf4b17..88316ceb4c6c4d 100644 +--- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c ++++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +@@ -197,13 +197,18 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) + + sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id); + ++ /* ++ * Set the control CPU before any read path so entry recreation after CPU ++ * hotplug can populate read-only attributes from the new online CPU. ++ */ ++ data->control_cpu = cpu; + uncore_read(data, &data->initial_min_freq_khz, &data->initial_max_freq_khz); + + ret = create_attr_group(data, data->name); +- if (!ret) { +- data->control_cpu = cpu; ++ if (ret) ++ data->control_cpu = -1; ++ else + data->valid = true; +- } + + uncore_unlock: + mutex_unlock(&uncore_lock); +-- +2.53.0 + diff --git a/staging-6.1/series b/staging-6.1/series new file mode 100644 index 0000000000..07ddd9b506 --- /dev/null +++ b/staging-6.1/series @@ -0,0 +1 @@ +platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch diff --git a/staging-6.12/net-airoha-fix-skb-priority-underflow-in-airoha_dev_.patch b/staging-6.12/net-airoha-fix-skb-priority-underflow-in-airoha_dev_.patch new file mode 100644 index 0000000000..ed4a5410e2 --- /dev/null +++ b/staging-6.12/net-airoha-fix-skb-priority-underflow-in-airoha_dev_.patch @@ -0,0 +1,60 @@ +From 74123d40df79a3b64e2f07a8070fd7e48d7f45a0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 20 Jun 2026 16:17:44 +0800 +Subject: net: airoha: Fix skb->priority underflow in airoha_dev_select_queue() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Wayen Yan + +[ Upstream commit 86e51aa24686cc95bb35613059e8b94b9b81e3f0 ] + +In airoha_dev_select_queue(), the expression: + + queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; + +implicitly converts to unsigned arithmetic: when skb->priority is 0 +(the default for unclassified traffic), (0u - 1u) wraps to UINT_MAX, +and UINT_MAX % 8 = 7, routing default best-effort packets to the +highest-priority QoS queue. This causes QoS inversion where the +majority of traffic on a PON gateway starves actual high-priority +flows (VoIP, gaming, etc.). + +The "- 1" offset was a leftover from the ETS offload implementation +that has since been removed. The correct mapping is a direct modulo: + + queue = skb->priority % AIROHA_NUM_QOS_QUEUES; + +This maps priority 0 → queue 0 (lowest), priority 7 → queue 7 +(highest), with higher priorities wrapping around. This is the +standard Linux sk_prio → HW queue mapping used by other drivers. + +Fixes: 2b288b81560b ("net: airoha: Introduce ndo_select_queue callback") +Link: https://lore.kernel.org/netdev/178185573207.2378135.3729126358670287878@gmail.com/ +Acked-by: Lorenzo Bianconi +Reviewed-by: Joe Damato +Signed-off-by: Wayen Yan +Link: https://patch.msgid.link/178194366700.2485734.5368768965976693502@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/airoha/airoha_eth.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c +index 07f89e08b5faad..db4b87c17a2864 100644 +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2505,7 +2505,7 @@ static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb, + */ + channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id; + channel = channel % AIROHA_NUM_QOS_CHANNELS; +- queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */ ++ queue = skb->priority % AIROHA_NUM_QOS_QUEUES; + queue = channel * AIROHA_NUM_QOS_QUEUES + queue; + + return queue < dev->num_tx_queues ? queue : 0; +-- +2.53.0 + diff --git a/staging-6.12/net-airoha-move-airoha_eth-driver-in-a-dedicated-fol.patch b/staging-6.12/net-airoha-move-airoha_eth-driver-in-a-dedicated-fol.patch new file mode 100644 index 0000000000..44e260f93f --- /dev/null +++ b/staging-6.12/net-airoha-move-airoha_eth-driver-in-a-dedicated-fol.patch @@ -0,0 +1,127 @@ +From 9909ede86efb0d4132c277d7a0005c3a4adb4f2b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 28 Feb 2025 11:54:09 +0100 +Subject: net: airoha: Move airoha_eth driver in a dedicated folder + +From: Lorenzo Bianconi + +[ Upstream commit fb3dda82fd38ca42140f29b3082324dcdc128293 ] + +The airoha_eth driver has no codebase shared with mtk_eth_soc one. +Moreover, the upcoming features (flowtable hw offloading, PCS, ..) will +not reuse any code from MediaTek driver. Move the Airoha driver in a +dedicated folder. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +Stable-dep-of: 86e51aa24686 ("net: airoha: Fix skb->priority underflow in airoha_dev_select_queue()") +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/Kconfig | 2 ++ + drivers/net/ethernet/Makefile | 1 + + drivers/net/ethernet/airoha/Kconfig | 18 ++++++++++++++++++ + drivers/net/ethernet/airoha/Makefile | 6 ++++++ + .../ethernet/{mediatek => airoha}/airoha_eth.c | 0 + drivers/net/ethernet/mediatek/Kconfig | 8 -------- + drivers/net/ethernet/mediatek/Makefile | 1 - + 7 files changed, 27 insertions(+), 9 deletions(-) + create mode 100644 drivers/net/ethernet/airoha/Kconfig + create mode 100644 drivers/net/ethernet/airoha/Makefile + rename drivers/net/ethernet/{mediatek => airoha}/airoha_eth.c (100%) + +diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig +index 9a542e3c9b05d8..0875b5706b83ac 100644 +--- a/drivers/net/ethernet/Kconfig ++++ b/drivers/net/ethernet/Kconfig +@@ -20,6 +20,8 @@ source "drivers/net/ethernet/actions/Kconfig" + source "drivers/net/ethernet/adaptec/Kconfig" + source "drivers/net/ethernet/aeroflex/Kconfig" + source "drivers/net/ethernet/agere/Kconfig" ++source "drivers/net/ethernet/airoha/Kconfig" ++source "drivers/net/ethernet/mellanox/Kconfig" + source "drivers/net/ethernet/alacritech/Kconfig" + source "drivers/net/ethernet/allwinner/Kconfig" + source "drivers/net/ethernet/alteon/Kconfig" +diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile +index 99fa180dedb805..67182339469a0d 100644 +--- a/drivers/net/ethernet/Makefile ++++ b/drivers/net/ethernet/Makefile +@@ -10,6 +10,7 @@ obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adaptec/ + obj-$(CONFIG_GRETH) += aeroflex/ + obj-$(CONFIG_NET_VENDOR_ADI) += adi/ + obj-$(CONFIG_NET_VENDOR_AGERE) += agere/ ++obj-$(CONFIG_NET_VENDOR_AIROHA) += airoha/ + obj-$(CONFIG_NET_VENDOR_ALACRITECH) += alacritech/ + obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/ + obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/ +diff --git a/drivers/net/ethernet/airoha/Kconfig b/drivers/net/ethernet/airoha/Kconfig +new file mode 100644 +index 00000000000000..b6a131845f13b2 +--- /dev/null ++++ b/drivers/net/ethernet/airoha/Kconfig +@@ -0,0 +1,18 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++config NET_VENDOR_AIROHA ++ bool "Airoha devices" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ help ++ If you have a Airoha SoC with ethernet, say Y. ++ ++if NET_VENDOR_AIROHA ++ ++config NET_AIROHA ++ tristate "Airoha SoC Gigabit Ethernet support" ++ depends on NET_DSA || !NET_DSA ++ select PAGE_POOL ++ help ++ This driver supports the gigabit ethernet MACs in the ++ Airoha SoC family. ++ ++endif #NET_VENDOR_AIROHA +diff --git a/drivers/net/ethernet/airoha/Makefile b/drivers/net/ethernet/airoha/Makefile +new file mode 100644 +index 00000000000000..73a6f3680a4c4c +--- /dev/null ++++ b/drivers/net/ethernet/airoha/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++# ++# Airoha for the Mediatek SoCs built-in ethernet macs ++# ++ ++obj-$(CONFIG_NET_AIROHA) += airoha_eth.o +diff --git a/drivers/net/ethernet/mediatek/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c +similarity index 100% +rename from drivers/net/ethernet/mediatek/airoha_eth.c +rename to drivers/net/ethernet/airoha/airoha_eth.c +diff --git a/drivers/net/ethernet/mediatek/Kconfig b/drivers/net/ethernet/mediatek/Kconfig +index 95c4405b7d7bee..7bfd3f230ff507 100644 +--- a/drivers/net/ethernet/mediatek/Kconfig ++++ b/drivers/net/ethernet/mediatek/Kconfig +@@ -7,14 +7,6 @@ config NET_VENDOR_MEDIATEK + + if NET_VENDOR_MEDIATEK + +-config NET_AIROHA +- tristate "Airoha SoC Gigabit Ethernet support" +- depends on NET_DSA || !NET_DSA +- select PAGE_POOL +- help +- This driver supports the gigabit ethernet MACs in the +- Airoha SoC family. +- + config NET_MEDIATEK_SOC_WED + depends on ARCH_MEDIATEK || COMPILE_TEST + def_bool NET_MEDIATEK_SOC != n +diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile +index ddbb7f4a516cac..03e008fbc859b3 100644 +--- a/drivers/net/ethernet/mediatek/Makefile ++++ b/drivers/net/ethernet/mediatek/Makefile +@@ -11,4 +11,3 @@ mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o + endif + obj-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_ops.o + obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o +-obj-$(CONFIG_NET_AIROHA) += airoha_eth.o +-- +2.53.0 + diff --git a/staging-6.12/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch b/staging-6.12/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch new file mode 100644 index 0000000000..63dfacc55c --- /dev/null +++ b/staging-6.12/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch @@ -0,0 +1,66 @@ +From ee8407aaccb0b1309cbe6bcdc3c76d02c1a41e32 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 23:07:44 +0800 +Subject: platform/x86/intel-uncore-freq: Fix current_freq_khz after CPU + hotplug +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guixiong Wei + +commit 6b63520ed14b17bbe9c2103debbd2152dde1fba3 upstream. + +When the last CPU of a legacy uncore die goes offline, +uncore_freq_remove_die_entry() clears control_cpu. During CPU hotplug +re-add, uncore_freq_add_entry() still populates sysfs attributes before +assigning the new control CPU. As a result, the current frequency read +returns -ENXIO and current_freq_khz is omitted from the recreated sysfs +group. + +Assign control_cpu before the initial read paths and before +create_attr_group() so sysfs recreation uses the new online CPU. If +sysfs creation fails, restore control_cpu to -1 to keep the error path +state consistent. + +Fixes: 4d73c6772ab7 ("platform/x86: intel-uncore-freq: Conditionally create attribute for read frequency") +Cc: stable@vger.kernel.org +Acked-by: Srinivas Pandruvada +Link: https://patch.msgid.link/20260602020752.3126-1-weiguixiong@bytedance.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Guixiong Wei +Signed-off-by: Sasha Levin +--- + .../x86/intel/uncore-frequency/uncore-frequency-common.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +index e22b683a7a434c..2e4e2bcc3c6e71 100644 +--- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c ++++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +@@ -240,15 +240,20 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) + sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id); + } + ++ /* ++ * Set the control CPU before any read path so entry recreation after CPU ++ * hotplug can populate read-only attributes from the new online CPU. ++ */ ++ data->control_cpu = cpu; + uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ); + uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ); + + ret = create_attr_group(data, data->name); + if (ret) { ++ data->control_cpu = -1; + if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) + ida_free(&intel_uncore_ida, data->instance_id); + } else { +- data->control_cpu = cpu; + data->valid = true; + } + +-- +2.53.0 + diff --git a/staging-6.12/series b/staging-6.12/series new file mode 100644 index 0000000000..e3628cf8d9 --- /dev/null +++ b/staging-6.12/series @@ -0,0 +1,3 @@ +platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch +net-airoha-move-airoha_eth-driver-in-a-dedicated-fol.patch +net-airoha-fix-skb-priority-underflow-in-airoha_dev_.patch diff --git a/staging-6.18/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch b/staging-6.18/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch new file mode 100644 index 0000000000..cefc7971af --- /dev/null +++ b/staging-6.18/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch @@ -0,0 +1,66 @@ +From d2b3ca4dd9a5d02ebc39c791138dd5f56831a2b3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 23:07:57 +0800 +Subject: platform/x86/intel-uncore-freq: Fix current_freq_khz after CPU + hotplug +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guixiong Wei + +commit 6b63520ed14b17bbe9c2103debbd2152dde1fba3 upstream. + +When the last CPU of a legacy uncore die goes offline, +uncore_freq_remove_die_entry() clears control_cpu. During CPU hotplug +re-add, uncore_freq_add_entry() still populates sysfs attributes before +assigning the new control CPU. As a result, the current frequency read +returns -ENXIO and current_freq_khz is omitted from the recreated sysfs +group. + +Assign control_cpu before the initial read paths and before +create_attr_group() so sysfs recreation uses the new online CPU. If +sysfs creation fails, restore control_cpu to -1 to keep the error path +state consistent. + +Fixes: 4d73c6772ab7 ("platform/x86: intel-uncore-freq: Conditionally create attribute for read frequency") +Cc: stable@vger.kernel.org +Acked-by: Srinivas Pandruvada +Link: https://patch.msgid.link/20260602020752.3126-1-weiguixiong@bytedance.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Guixiong Wei +Signed-off-by: Sasha Levin +--- + .../x86/intel/uncore-frequency/uncore-frequency-common.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +index 65897fae17dfba..6091b617480ff6 100644 +--- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c ++++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +@@ -274,15 +274,20 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) + sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id); + } + ++ /* ++ * Set the control CPU before any read path so entry recreation after CPU ++ * hotplug can populate read-only attributes from the new online CPU. ++ */ ++ data->control_cpu = cpu; + uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ); + uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ); + + ret = create_attr_group(data, data->name); + if (ret) { ++ data->control_cpu = -1; + if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) + ida_free(&intel_uncore_ida, data->instance_id); + } else { +- data->control_cpu = cpu; + data->valid = true; + } + +-- +2.53.0 + diff --git a/staging-6.18/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch b/staging-6.18/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch new file mode 100644 index 0000000000..2fb0cdbd65 --- /dev/null +++ b/staging-6.18/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch @@ -0,0 +1,419 @@ +From 0ad28e2e40b0d6eb34c62a357edc992edf33f338 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 3 Jul 2026 12:02:38 +0200 +Subject: posix-cpu-timers: Prevent UAF caused by non-leader exec() race + +From: Thomas Gleixner + +commit 920f893f735e92ba3a1cd9256899a186b161928d upstream. + +Wongi and Jungwoo decoded and reported a non-leader exec() related race +which can result in an UAF: + + sys_timer_delete() exec() + posix_cpu_timer_del() + // Observes old leader + p = pid_task(pid, pid_type); de_thread() + switch_leader(); + release_task(old_leader) + __exit_signal(old_leader) + sighand = lock(old_leader, sighand); + posix_cpu_timers*_exit(); + sighand = lock_task_sighand(p) unhash_task(old_leader); + sh = lock(p, sighand) old_leader->sighand = NULL; + unlock(sighand); + (p->sighand == NULL) + unlock(sh) + return NULL; + + // Returns without action + if(!sighand) + return 0; + free_posix_timer(); + +This is "harmless" unless the deleted timer was armed and enqueued in +p->signal because on exec() a TGID targeted timer is inherited. + +As sys_timer_delete() freed the underlying posix timer object +run_posix_cpu_timers() or any timerqueue related add/delete operations on +other timers will access the freed object's timerqueue node, which results +in an UAF. + +There is a similar problem vs. posix_cpu_timer_set(). For regular posix +timers it just transiently returns -ESRCH to user space, but for the use +case in do_cpu_nanosleep() it's the same UAF just that the k_itimer is +allocated on the stack. + +Also posix_cpu_timer_rearm() fails to rearm the timer, which means it stops +to expire. + +While debating solutions Frederic pointed out another problem: + + posix_cpu_timer_del(tmr) + __exit_signal(p) + posix_cpu_timers*_exit(p); + unhash_task(p); + p->sighand = NULL; + sh = lock_task_sighand(p) + sighand = p->sighand; + if (!sighand) + return NULL; + lock(sighand); + + if (!sh) + WARN_ON_ONCE(timer_queued(tmr)); + +On weakly ordered architectures it is not guaranteed that +posix_cpu_timer_del() will observe the stores in posix_cpu_timers*_exit() +when p->sighand is observed as NULL, which means the WARN() can be a false +positive. + +Solve these issues by: + + 1) Changing the store in __exit_signal() to smp_store_release(). + + 2) Adding a smp_acquire__after_ctrl_dep() into the !sighand path + of lock_task_sighand(). + + 3) Creating a helper function for looking up the task and locking sighand + which does not return when sighand == NULL. Instead it retries the + task lookup and only if that fails it gives up. + + 4) Using that helper in the three affected functions. + +observes all preceeding stores, i.e. the stores in posix_cpu_timers*_exit() +and the ones in unhash_task(). + +gracefully. When the task lookup returns the old leader, but sighand == +NULL then it retries. In the non-leader exec() case the subsequent task +lookup will observe the new leader due to #1/#2. In normal exit() scenarios +the subsequent lookup fails. + +When the task lookup fails, the function also checks whether the timer is +still enqueued and issues a warning if that's the case. Unfortunately there +is nothing which can be done about it, but as the task is already not +longer visible the timer should not be accessed anymore. This check also +requires memory ordering, which is not provided when the first lookup +fails. To achieve that the check is preceeded by a smp_rmb() which pairs +with the smp_wmb() in write_seqlock() in __exit_signal(). That ensures that +the stores in posix_cpu_timers*_exit() are visible. + +The history of the non-leader exec() issue goes back to the early days of +posix CPU timers, which stored a pointer to the group leader task in the +timer. That obviously fails when a non-leader exec() switches the leader. +commit e0a70217107e ("posix-cpu-timers: workaround to suppress the problems +with mt exec") added a temporary workaround for that in 2010 which survived +about 10 years. The fix for the workaround changed the task pointer to a +pid pointer, but failed to see the subtle race described above. So the +Fixes tag picks that commit, which seems to be halfways accurate. + +Thanks to Frederic Weissbecker, Oleg Nesterov and Peter Zijlstra for +review, feedback and suggestions and to Wongi and Jungwoo for the excellent +bug report and analysis! + +Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task") +Reported-by: Wongi Lee +Reported-by: Jungwoo Lee +Signed-off-by: Thomas Gleixner +Reviewed-by: Oleg Nesterov +Cc: stable@vger.kernel.org +Signed-off-by: Salvatore Bonaccorso +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + kernel/exit.c | 7 +- + kernel/signal.c | 10 +- + kernel/time/posix-cpu-timers.c | 173 ++++++++++++++++++++++----------- + 3 files changed, 130 insertions(+), 60 deletions(-) + +diff --git a/kernel/exit.c b/kernel/exit.c +index c832946823f488..a32bc65569a508 100644 +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -212,7 +212,12 @@ static void __exit_signal(struct release_task_post *post, struct task_struct *ts + __unhash_process(post, tsk, group_dead); + write_sequnlock(&sig->stats_lock); + +- tsk->sighand = NULL; ++ /* ++ * Ensure that all preceeding state is visible. Pairs with ++ * the smp_acquire__after_ctrl_dep() in the sighand == NULL ++ * path of lock_task_sighand(). ++ */ ++ smp_store_release(&tsk->sighand, NULL); + spin_unlock(&sighand->siglock); + + __cleanup_sighand(sighand); +diff --git a/kernel/signal.c b/kernel/signal.c +index 810098300ecdcb..e1843b71efff13 100644 +--- a/kernel/signal.c ++++ b/kernel/signal.c +@@ -1364,8 +1364,16 @@ struct sighand_struct *__lock_task_sighand(struct task_struct *tsk, + rcu_read_lock(); + for (;;) { + sighand = rcu_dereference(tsk->sighand); +- if (unlikely(sighand == NULL)) ++ if (unlikely(sighand == NULL)) { ++ /* ++ * Pairs with the smp_store_release() in ++ * __exit_signal(). It ensures that all state ++ * modifications to the task preceeding the store are ++ * visible to the callers of lock_task_sighand(). ++ */ ++ smp_acquire__after_ctrl_dep(); + break; ++ } + + /* + * This sighand can be already freed and even reused, but +diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c +index d14523fbc146cd..2d64972fd0804b 100644 +--- a/kernel/time/posix-cpu-timers.c ++++ b/kernel/time/posix-cpu-timers.c +@@ -461,6 +461,109 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p) + trigger_base_recalc_expires(timer, p); + } + ++/* ++ * Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand. ++ * ++ * This can race with the reaping of the task: ++ * ++ * CPU0 CPU1 ++ * ++ * // Finds task ++ * p = pid_task(pid, pid_type); __exit_signal(p) ++ * lock(p, sighand); ++ * posix_cpu_timers*_exit(); ++ * sighand = lock_task_sighand(p); unhash_task(p); ++ * p->sighand = NULL; ++ * unlock(sighand); ++ * ++ * In this case sighand is NULL, which means the task and the associated timer ++ * queue cannot be longer accessed safely. ++ * ++ * __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is ++ * dead it also invokes posix_cpu_timers_group_exit(). These functions delete ++ * all pending timers from the related timer queues. The POSIX timers (k_itimer) ++ * themself are still accessible, but not longer connected to the task. ++ * ++ * exec() works slightly differently. The task which exec()'s terminates all ++ * other threads in the thread group and runs __exit_signal() on them. As the ++ * thread group is not dead they only clean up the per task timers via ++ * posix_cpu_timers_exit(). ++ * ++ * As the TGID on exec() stays the same per process timers stay queued, if they ++ * are armed. This works without a problem when exec() is done by the thread ++ * group leader. If a non-leader thread exec()'s this can end up in the ++ * following scenario: ++ * ++ * CPU0 CPU1 ++ * // Returns old leader ++ * p = pid_task(pid, pid_type); de_thread() ++ * switch_leader() ++ * release_task(old leader) ++ * __exit_signal() ++ * old_leader->sighand = NULL; ++ * // Returns NULL ++ * sighand = lock_task_sighand(p) ++ * ++ * That's problematic for several functions: ++ * ++ * - posix_cpu_timer_del(): If the timer is still enqueued on the task the ++ * underlying k_itimer will be freed which results in a UAF in ++ * run_posix_cpu_timers() or on timerqueue related add/delete operations. ++ * If the timer is not enqueued, the failure is harmless ++ * ++ * - posix_cpu_timer_set(): Independent of the enqueued state that results in a ++ * transient failure which is user space visible (-ESRCH) for regular posix ++ * timers. But for the use case in do_cpu_nanosleep() it's the same UAF ++ * problem just that the timer is allocated on the stack. ++ * ++ * - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this ++ * silently ignores the rearm request, which is a functional problem as the ++ * timer wont expire anymore. ++ */ ++static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags) ++{ ++ enum pid_type type = clock_pid_type(timer->it_clock); ++ struct cpu_timer *ctmr = &timer->it.cpu; ++ ++ guard(rcu)(); ++ ++ for (;;) { ++ struct task_struct *t = pid_task(timer->it.cpu.pid, type); ++ ++ /* Fail if the task cannot be found. */ ++ if (!t) ++ break; ++ ++ /* Try to lock the task's sighand */ ++ if (lock_task_sighand(t, flags)) ++ return t; ++ ++ /* ++ * The next PID lookup might either fail or return the new ++ * leader. This is correct for both exit() and exec(). ++ */ ++ } ++ ++ /* ++ * If the timer is still enqueued, warn. There is nothing safe to do ++ * here as there might be two timers in there which are removed in ++ * parallel and that will cause more damage than good. This should never ++ * happen! ++ * ++ * Ensure that the stores to the timer and timerqueue are visible: ++ * ++ * __exit_signal() ++ * posix_cpu_timers*_exit() ++ * write_seqlock(seqlock) ++ * smp_wmb(); <------- ++ * __unhash_process() | !pid_task() ++ * ----> smp_rmb(); ++ * WARN_ON_ONCE(...) ++ */ ++ smp_rmb(); ++ WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node)); ++ return NULL; ++} + + /* + * Clean up a CPU-clock timer that is about to be destroyed. +@@ -470,29 +573,13 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p) + */ + static int posix_cpu_timer_del(struct k_itimer *timer) + { +- struct cpu_timer *ctmr = &timer->it.cpu; +- struct sighand_struct *sighand; + struct task_struct *p; + unsigned long flags; + int ret = 0; + +- rcu_read_lock(); +- p = cpu_timer_task_rcu(timer); +- if (!p) +- goto out; ++ p = timer_lock_sighand(timer, &flags); + +- /* +- * Protect against sighand release/switch in exit/exec and process/ +- * thread timer list entry concurrent read/writes. +- */ +- sighand = lock_task_sighand(p, &flags); +- if (unlikely(sighand == NULL)) { +- /* +- * This raced with the reaping of the task. The exit cleanup +- * should have removed this timer from the timer queue. +- */ +- WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node)); +- } else { ++ if (likely(p)) { + if (timer->it.cpu.firing) { + /* + * Prevent signal delivery. The timer cannot be dequeued +@@ -508,11 +595,8 @@ static int posix_cpu_timer_del(struct k_itimer *timer) + unlock_task_sighand(p, &flags); + } + +-out: +- rcu_read_unlock(); +- + if (!ret) { +- put_pid(ctmr->pid); ++ put_pid(timer->it.cpu.pid); + timer->it_status = POSIX_TIMER_DISARMED; + } + return ret; +@@ -626,21 +710,17 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, + clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); + struct cpu_timer *ctmr = &timer->it.cpu; + u64 old_expires, new_expires, now; +- struct sighand_struct *sighand; + struct task_struct *p; + unsigned long flags; + int ret = 0; + +- rcu_read_lock(); +- p = cpu_timer_task_rcu(timer); +- if (!p) { +- /* +- * If p has just been reaped, we can no +- * longer get any information about it at all. +- */ +- rcu_read_unlock(); ++ p = timer_lock_sighand(timer, &flags); ++ /* ++ * If p has just been reaped, we can no longer get any information about ++ * it at all. ++ */ ++ if (!p) + return -ESRCH; +- } + + /* + * Use the to_ktime conversion because that clamps the maximum +@@ -648,20 +728,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, + */ + new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value)); + +- /* +- * Protect against sighand release/switch in exit/exec and p->cpu_timers +- * and p->signal->cpu_timers read/write in arm_timer() +- */ +- sighand = lock_task_sighand(p, &flags); +- /* +- * If p has just been reaped, we can no +- * longer get any information about it at all. +- */ +- if (unlikely(sighand == NULL)) { +- rcu_read_unlock(); +- return -ESRCH; +- } +- + /* Retrieve the current expiry time before disarming the timer */ + old_expires = cpu_timer_getexpires(ctmr); + +@@ -698,7 +764,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, + /* Retry if the timer expiry is running concurrently */ + if (unlikely(ret)) { + unlock_task_sighand(p, &flags); +- goto out; ++ return ret; + } + + /* Convert relative expiry time to absolute */ +@@ -733,8 +799,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, + */ + if (!sigev_none && new_expires && now >= new_expires) + cpu_timer_fire(timer); +-out: +- rcu_read_unlock(); + return ret; + } + +@@ -1018,19 +1082,12 @@ static void check_process_timers(struct task_struct *tsk, + static bool posix_cpu_timer_rearm(struct k_itimer *timer) + { + clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); +- struct sighand_struct *sighand; + struct task_struct *p; + unsigned long flags; + u64 now; + +- guard(rcu)(); +- p = cpu_timer_task_rcu(timer); +- if (!p) +- return true; +- +- /* Protect timer list r/w in arm_timer() */ +- sighand = lock_task_sighand(p, &flags); +- if (unlikely(sighand == NULL)) ++ p = timer_lock_sighand(timer, &flags); ++ if (unlikely(!p)) + return true; + + /* +-- +2.53.0 + diff --git a/staging-6.18/posix-timers-expand-timer_-re-arm-callbacks-with-a-b.patch b/staging-6.18/posix-timers-expand-timer_-re-arm-callbacks-with-a-b.patch new file mode 100644 index 0000000000..bbd9307ed9 --- /dev/null +++ b/staging-6.18/posix-timers-expand-timer_-re-arm-callbacks-with-a-b.patch @@ -0,0 +1,183 @@ +From a28e4c85d2a7b518fc6c697fe2bd2e8caebb00e3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Apr 2026 13:53:56 +0200 +Subject: posix-timers: Expand timer_[re]arm() callbacks with a boolean return + value + +From: Thomas Gleixner + +commit 6fdb2677a594ab38eade927919bbd4d9688bfa1c upstream. + +In order to catch expiry times which are already in the past the +timer_arm() and timer_rearm() callbacks need to be able to report back to +the caller whether the timer has been queued or not. + +Change the function signature and let all implementations return true for +now. While at it simplify posix_cpu_timer_rearm(). + +No functional change intended. + +Signed-off-by: Thomas Gleixner +Reviewed-by: Frederic Weisbecker +Acked-by: Peter Zijlstra (Intel) +Acked-by: John Stultz +Link: https://patch.msgid.link/20260408114952.130222296@kernel.org +Signed-off-by: Salvatore Bonaccorso +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + kernel/time/alarmtimer.c | 6 ++++-- + kernel/time/posix-cpu-timers.c | 18 ++++++++++-------- + kernel/time/posix-timers.c | 6 ++++-- + kernel/time/posix-timers.h | 4 ++-- + 4 files changed, 20 insertions(+), 14 deletions(-) + +diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c +index 189ee8bbc3e650..525df334b15a79 100644 +--- a/kernel/time/alarmtimer.c ++++ b/kernel/time/alarmtimer.c +@@ -521,12 +521,13 @@ static void alarm_handle_timer(struct alarm *alarm, ktime_t now) + * alarm_timer_rearm - Posix timer callback for rearming timer + * @timr: Pointer to the posixtimer data struct + */ +-static void alarm_timer_rearm(struct k_itimer *timr) ++static bool alarm_timer_rearm(struct k_itimer *timr) + { + struct alarm *alarm = &timr->it.alarm.alarmtimer; + + timr->it_overrun += alarm_forward_now(alarm, timr->it_interval); + alarm_start(alarm, alarm->node.expires); ++ return true; + } + + /** +@@ -582,7 +583,7 @@ static void alarm_timer_wait_running(struct k_itimer *timr) + * @absolute: Expiry value is absolute time + * @sigev_none: Posix timer does not deliver signals + */ +-static void alarm_timer_arm(struct k_itimer *timr, ktime_t expires, ++static bool alarm_timer_arm(struct k_itimer *timr, ktime_t expires, + bool absolute, bool sigev_none) + { + struct alarm *alarm = &timr->it.alarm.alarmtimer; +@@ -594,6 +595,7 @@ static void alarm_timer_arm(struct k_itimer *timr, ktime_t expires, + alarm->node.expires = expires; + else + alarm_start(&timr->it.alarm.alarmtimer, expires); ++ return true; + } + + /** +diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c +index 99affd9e228ec5..d14523fbc146cd 100644 +--- a/kernel/time/posix-cpu-timers.c ++++ b/kernel/time/posix-cpu-timers.c +@@ -19,7 +19,7 @@ + + #include "posix-timers.h" + +-static void posix_cpu_timer_rearm(struct k_itimer *timer); ++static bool posix_cpu_timer_rearm(struct k_itimer *timer); + + void posix_cputimers_group_init(struct posix_cputimers *pct, u64 cpu_limit) + { +@@ -1011,24 +1011,27 @@ static void check_process_timers(struct task_struct *tsk, + /* + * This is called from the signal code (via posixtimer_rearm) + * when the last timer signal was delivered and we have to reload the timer. ++ * ++ * Return true unconditionally so the core code assumes the timer to be ++ * armed. Otherwise it would requeue the signal. + */ +-static void posix_cpu_timer_rearm(struct k_itimer *timer) ++static bool posix_cpu_timer_rearm(struct k_itimer *timer) + { + clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); +- struct task_struct *p; + struct sighand_struct *sighand; ++ struct task_struct *p; + unsigned long flags; + u64 now; + +- rcu_read_lock(); ++ guard(rcu)(); + p = cpu_timer_task_rcu(timer); + if (!p) +- goto out; ++ return true; + + /* Protect timer list r/w in arm_timer() */ + sighand = lock_task_sighand(p, &flags); + if (unlikely(sighand == NULL)) +- goto out; ++ return true; + + /* + * Fetch the current sample and update the timer's expiry time. +@@ -1045,8 +1048,7 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer) + */ + arm_timer(timer, p); + unlock_task_sighand(p, &flags); +-out: +- rcu_read_unlock(); ++ return true; + } + + /** +diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c +index 56e17b625c72e8..3082129d836c24 100644 +--- a/kernel/time/posix-timers.c ++++ b/kernel/time/posix-timers.c +@@ -295,12 +295,13 @@ static inline int timer_overrun_to_int(struct k_itimer *timr) + return (int)timr->it_overrun_last; + } + +-static void common_hrtimer_rearm(struct k_itimer *timr) ++static bool common_hrtimer_rearm(struct k_itimer *timr) + { + struct hrtimer *timer = &timr->it.real.timer; + + timr->it_overrun += hrtimer_forward_now(timer, timr->it_interval); + hrtimer_restart(timer); ++ return true; + } + + static bool __posixtimer_deliver_signal(struct kernel_siginfo *info, struct k_itimer *timr) +@@ -802,7 +803,7 @@ SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id) + return timer_overrun_to_int(scoped_timer); + } + +-static void common_hrtimer_arm(struct k_itimer *timr, ktime_t expires, ++static bool common_hrtimer_arm(struct k_itimer *timr, ktime_t expires, + bool absolute, bool sigev_none) + { + struct hrtimer *timer = &timr->it.real.timer; +@@ -829,6 +830,7 @@ static void common_hrtimer_arm(struct k_itimer *timr, ktime_t expires, + + if (!sigev_none) + hrtimer_start_expires(timer, HRTIMER_MODE_ABS); ++ return true; + } + + static int common_hrtimer_try_to_cancel(struct k_itimer *timr) +diff --git a/kernel/time/posix-timers.h b/kernel/time/posix-timers.h +index 7f259e845d240c..4ea9611dd7165c 100644 +--- a/kernel/time/posix-timers.h ++++ b/kernel/time/posix-timers.h +@@ -27,11 +27,11 @@ struct k_clock { + int (*timer_del)(struct k_itimer *timr); + void (*timer_get)(struct k_itimer *timr, + struct itimerspec64 *cur_setting); +- void (*timer_rearm)(struct k_itimer *timr); ++ bool (*timer_rearm)(struct k_itimer *timr); + s64 (*timer_forward)(struct k_itimer *timr, ktime_t now); + ktime_t (*timer_remaining)(struct k_itimer *timr, ktime_t now); + int (*timer_try_to_cancel)(struct k_itimer *timr); +- void (*timer_arm)(struct k_itimer *timr, ktime_t expires, ++ bool (*timer_arm)(struct k_itimer *timr, ktime_t expires, + bool absolute, bool sigev_none); + void (*timer_wait_running)(struct k_itimer *timr); + }; +-- +2.53.0 + diff --git a/staging-6.18/series b/staging-6.18/series new file mode 100644 index 0000000000..a69a51ccf5 --- /dev/null +++ b/staging-6.18/series @@ -0,0 +1,3 @@ +posix-timers-expand-timer_-re-arm-callbacks-with-a-b.patch +posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch +platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch diff --git a/staging-6.6/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch b/staging-6.6/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch new file mode 100644 index 0000000000..26b1ab2820 --- /dev/null +++ b/staging-6.6/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch @@ -0,0 +1,66 @@ +From 34104e1a1950e4b198193e544befd7f3ee530cd3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 23:07:28 +0800 +Subject: platform/x86/intel-uncore-freq: Fix current_freq_khz after CPU + hotplug +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guixiong Wei + +commit 6b63520ed14b17bbe9c2103debbd2152dde1fba3 upstream. + +When the last CPU of a legacy uncore die goes offline, +uncore_freq_remove_die_entry() clears control_cpu. During CPU hotplug +re-add, uncore_freq_add_entry() still populates sysfs attributes before +assigning the new control CPU. As a result, the current frequency read +returns -ENXIO and current_freq_khz is omitted from the recreated sysfs +group. + +Assign control_cpu before the initial read paths and before +create_attr_group() so sysfs recreation uses the new online CPU. If +sysfs creation fails, restore control_cpu to -1 to keep the error path +state consistent. + +Fixes: 4d73c6772ab7 ("platform/x86: intel-uncore-freq: Conditionally create attribute for read frequency") +Cc: stable@vger.kernel.org +Acked-by: Srinivas Pandruvada +Link: https://patch.msgid.link/20260602020752.3126-1-weiguixiong@bytedance.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +[weiguixiong: Adapt to legacy uncore_read() and instance_id naming.] +Signed-off-by: Guixiong Wei +Signed-off-by: Sasha Levin +--- + .../x86/intel/uncore-frequency/uncore-frequency-common.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +index 33bb58dc3f78c3..b362e0170a6256 100644 +--- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c ++++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +@@ -238,14 +238,19 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) + sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id); + } + ++ /* ++ * Set the control CPU before any read path so entry recreation after CPU ++ * hotplug can populate read-only attributes from the new online CPU. ++ */ ++ data->control_cpu = cpu; + uncore_read(data, &data->initial_min_freq_khz, &data->initial_max_freq_khz); + + ret = create_attr_group(data, data->name); + if (ret) { ++ data->control_cpu = -1; + if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) + ida_free(&intel_uncore_ida, data->instance_id); + } else { +- data->control_cpu = cpu; + data->valid = true; + } + +-- +2.53.0 + diff --git a/staging-6.6/series b/staging-6.6/series new file mode 100644 index 0000000000..07ddd9b506 --- /dev/null +++ b/staging-6.6/series @@ -0,0 +1 @@ +platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch diff --git a/staging-7.1/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch b/staging-7.1/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch new file mode 100644 index 0000000000..3094e3dde5 --- /dev/null +++ b/staging-7.1/platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch @@ -0,0 +1,66 @@ +From 4bf93941e8f6b5404c661c251b55429dcc11ed32 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 22:17:44 +0800 +Subject: platform/x86/intel-uncore-freq: Fix current_freq_khz after CPU + hotplug +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guixiong Wei + +commit 6b63520ed14b17bbe9c2103debbd2152dde1fba3 upstream. + +When the last CPU of a legacy uncore die goes offline, +uncore_freq_remove_die_entry() clears control_cpu. During CPU hotplug +re-add, uncore_freq_add_entry() still populates sysfs attributes before +assigning the new control CPU. As a result, the current frequency read +returns -ENXIO and current_freq_khz is omitted from the recreated sysfs +group. + +Assign control_cpu before the initial read paths and before +create_attr_group() so sysfs recreation uses the new online CPU. If +sysfs creation fails, restore control_cpu to -1 to keep the error path +state consistent. + +Fixes: 4d73c6772ab7 ("platform/x86: intel-uncore-freq: Conditionally create attribute for read frequency") +Cc: stable@vger.kernel.org +Acked-by: Srinivas Pandruvada +Link: https://patch.msgid.link/20260602020752.3126-1-weiguixiong@bytedance.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Guixiong Wei +Signed-off-by: Sasha Levin +--- + .../x86/intel/uncore-frequency/uncore-frequency-common.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +index 7070c94324e0ed..f8137ee92e4754 100644 +--- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c ++++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +@@ -275,15 +275,20 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) + data->package_id, data->die_id); + } + ++ /* ++ * Set the control CPU before any read path so entry recreation after CPU ++ * hotplug can populate read-only attributes from the new online CPU. ++ */ ++ data->control_cpu = cpu; + uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ); + uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ); + + ret = create_attr_group(data, data->name); + if (ret) { ++ data->control_cpu = -1; + if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) + ida_free(&intel_uncore_ida, data->instance_id); + } else { +- data->control_cpu = cpu; + data->valid = true; + } + +-- +2.53.0 + diff --git a/staging-7.1/sched_ext-skip-ops.set_weight-for-disabled-tasks.patch b/staging-7.1/sched_ext-skip-ops.set_weight-for-disabled-tasks.patch new file mode 100644 index 0000000000..f7e2f1ad9b --- /dev/null +++ b/staging-7.1/sched_ext-skip-ops.set_weight-for-disabled-tasks.patch @@ -0,0 +1,67 @@ +From 1ab09f47aef9a808bc1163d31f7caa9ff1076572 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 15:04:03 +0000 +Subject: sched_ext: Skip ops.set_weight() for disabled tasks + +From: Kuba Piecuch + +commit 0e2f4ab68a89fad42e0f5a9ff4b740738e7aa1d6 upstream. + +When switching a task's sched_class away from sched_ext, we get the +following sequence of events in __sched_setscheduler(): + +sched_change_begin() + switched_from_scx() + scx_disable_task(p) + ops.disable(p) +__setscheduler_params() + set_load_weight() + reweight_task_scx(p) + ops.set_weight(p) +p->sched_class = next_class; +sched_change_end() + ... + +Notably, ops.set_weight() is called _after_ ops.disable(). +This violates the expected semantics of the callbacks, the expectation +being that ops.disable() can only be followed by ops.exit_task() or +ops.enable(). + +Skipping the weight adjustment for disabled tasks should be harmless +since the weight will be recalculated in scx_enable_task() if the task +ever rejoins SCX. + +Fixes: 637b0682821b ("sched: Fold sched_class::switch{ing,ed}_{to,from}() into the change pattern") +Cc: stable@vger.kernel.org # v6.19+ +Signed-off-by: Kuba Piecuch +Signed-off-by: Tejun Heo +Signed-off-by: Sasha Levin +--- + kernel/sched/ext.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c +index 5d2d19473a82e8..8429bebc21a19a 100644 +--- a/kernel/sched/ext.c ++++ b/kernel/sched/ext.c +@@ -3904,6 +3904,17 @@ static void reweight_task_scx(struct rq *rq, struct task_struct *p, + if (task_dead_and_done(p)) + return; + ++ /* ++ * When switching sched_class away from SCX, reweight_task_scx() ++ * is called _after_ scx_disable_task(). Skip calling ops.set_weight() ++ * since the BPF scheduler may have already forgotten the task in ++ * ops.disable(). ++ * p->scx.weight will be recalculated in scx_enable_task() if the task ++ * ever returns to SCX class. ++ */ ++ if (scx_get_task_state(p) != SCX_TASK_ENABLED) ++ return; ++ + p->scx.weight = sched_weight_to_cgroup(scale_load_down(lw->weight)); + if (SCX_HAS_OP(sch, set_weight)) + SCX_CALL_OP_TASK(sch, set_weight, rq, p, p->scx.weight); +-- +2.53.0 + diff --git a/staging-7.1/series b/staging-7.1/series new file mode 100644 index 0000000000..1487d9b4e0 --- /dev/null +++ b/staging-7.1/series @@ -0,0 +1,2 @@ +platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch +sched_ext-skip-ops.set_weight-for-disabled-tasks.patch