--- /dev/null
+From 6a00ffb19f6ed49ae55a6e22d62b921f5e0e2365 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 08:19:40 -0500
+Subject: ipmi:ssif: Clean up kthread on errors
+
+From: Corey Minyard <corey@minyard.net>
+
+commit 75c486cb1bcaa1a3ec3a6438498176a3a4998ae4 upstream.
+
+If an error occurs after the ssif kthread is created, but before the
+main IPMI code starts the ssif interface, the ssif kthread will not
+be stopped.
+
+So make sure the kthread is stopped on an error condition if it is
+running.
+
+Fixes: 259307074bfc ("ipmi: Add SMBus interface driver (SSIF)")
+Reported-by: Li Xiao <<252270051@hdu.edu.cn>
+Cc: stable@vger.kernel.org
+Reviewed-by: Li Xiao <252270051@hdu.edu.cn>
+[Adjusted for stopping flag and complete operation still being present.]
+Signed-off-by: Corey Minyard <corey@minyard.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/char/ipmi/ipmi_ssif.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
+index 42cbf761fa749..55ebe1d31766b 100644
+--- a/drivers/char/ipmi/ipmi_ssif.c
++++ b/drivers/char/ipmi/ipmi_ssif.c
+@@ -1311,6 +1311,7 @@ static void shutdown_ssif(void *send_info)
+ if (ssif_info->thread) {
+ complete(&ssif_info->wake_thread);
+ kthread_stop(ssif_info->thread);
++ ssif_info->thread = NULL;
+ }
+ }
+
+@@ -1941,6 +1942,17 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
+
+ out:
+ if (rv) {
++ /*
++ * If ipmi_register_smi() starts the interface, it will
++ * call shutdown and that will free the thread and set
++ * it to NULL. Otherwise it must be freed here.
++ */
++ if (ssif_info->thread) {
++ ssif_info->stopping = true;
++ complete(&ssif_info->wake_thread);
++ kthread_stop(ssif_info->thread);
++ ssif_info->thread = NULL;
++ }
+ if (addr_info)
+ addr_info->client = NULL;
+
+--
+2.53.0
+
--- /dev/null
+From e5f141f715962e377d46eb149abf89f69dffa192 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 08:19:39 -0500
+Subject: ipmi:ssif: Fix a shutdown race
+
+From: Corey Minyard <corey@minyard.net>
+
+It was possible for the SSIF thread to stop and quit before the
+kthread_stop() call because ssif->stopping was set before the
+stop. So only exit the SSIF thread is kthread_should_stop()
+returns true.
+
+In the mainstream kernel this was fixed in 6bd0eb6d759b ("ipmi:ssif:
+Fix a shutdown race"). However, that requires a fix in kernel
+version 6.1 has a fix to kthread stop to cause interruptible waits
+to return -ERESTARTSYS on a stop. This has not been backported to
+older kernels, and that would probably be a bad idea. But it means
+that the mainstrem kernel fix for this will not work.
+
+Instead, wait for kthread_should_stop() to return true before exiting
+the thread.
+
+Signed-off-by: Corey Minyard <cminyard@mvista.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/char/ipmi/ipmi_ssif.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
+index a811b5bdba259..42cbf761fa749 100644
+--- a/drivers/char/ipmi/ipmi_ssif.c
++++ b/drivers/char/ipmi/ipmi_ssif.c
+@@ -522,6 +522,16 @@ static int ipmi_ssif_thread(void *data)
+ }
+ }
+
++ /*
++ * The thread can break out of the loop if stopping is set,
++ * and this can be before kthread_stop() gets called and thus
++ * kthread_should_stop() will not be set. This can cause
++ * spinning calling this function and other bad things. So
++ * wait for kthread_should_stop() to be set.
++ */
++ while (!kthread_should_stop())
++ msleep_interruptible(1);
++
+ return 0;
+ }
+
+--
+2.53.0
+
--- /dev/null
+From 9c957ad9390a77de785f863901a919d83409f4a1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 08:19:42 -0500
+Subject: ipmi:ssif: NULL thread on error
+
+From: Corey Minyard <corey@minyard.net>
+
+commit a8aebe93a4938c0ca1941eeaae821738f869be3d upstream.
+
+Cleanup code was checking the thread for NULL, but it was possibly
+a PTR_ERR() in one spot.
+
+Spotted with static analysis.
+
+Link: https://sourceforge.net/p/openipmi/mailman/message/59324676/
+Fixes: 75c486cb1bca ("ipmi:ssif: Clean up kthread on errors")
+Cc: <stable@vger.kernel.org> # 91eb7ec72612: ipmi:ssif: Remove unnecessary indention
+Cc: stable@vger.kernel.org
+Signed-off-by: Corey Minyard <corey@minyard.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/char/ipmi/ipmi_ssif.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
+index e93846f8f2352..4cbfe1858ab4f 100644
+--- a/drivers/char/ipmi/ipmi_ssif.c
++++ b/drivers/char/ipmi/ipmi_ssif.c
+@@ -1910,6 +1910,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
+ "kssif%4.4x", thread_num);
+ if (IS_ERR(ssif_info->thread)) {
+ rv = PTR_ERR(ssif_info->thread);
++ ssif_info->thread = NULL;
+ dev_notice(&ssif_info->client->dev,
+ "Could not start kernel thread: error %d\n",
+ rv);
+--
+2.53.0
+
--- /dev/null
+From 581fda22d681f5fa29314367c6673f5182e5853a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 08:19:41 -0500
+Subject: ipmi:ssif: Remove unnecessary indention
+
+From: Corey Minyard <corey@minyard.net>
+
+commit 91eb7ec7261254b6875909df767185838598e21e upstream.
+
+A section was in {} that didn't need to be, move the variable
+definition to the top and set th eindentino properly.
+
+Signed-off-by: Corey Minyard <corey@minyard.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/char/ipmi/ipmi_ssif.c | 28 ++++++++++++----------------
+ 1 file changed, 12 insertions(+), 16 deletions(-)
+
+diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
+index 55ebe1d31766b..e93846f8f2352 100644
+--- a/drivers/char/ipmi/ipmi_ssif.c
++++ b/drivers/char/ipmi/ipmi_ssif.c
+@@ -1694,6 +1694,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
+ int len;
+ int i;
+ u8 slave_addr = 0;
++ unsigned int thread_num;
+ struct ssif_addr_info *addr_info = NULL;
+
+ mutex_lock(&ssif_infos_mutex);
+@@ -1902,22 +1903,17 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
+ ssif_info->handlers.request_events = request_events;
+ ssif_info->handlers.set_need_watch = ssif_set_need_watch;
+
+- {
+- unsigned int thread_num;
+-
+- thread_num = ((i2c_adapter_id(ssif_info->client->adapter)
+- << 8) |
+- ssif_info->client->addr);
+- init_completion(&ssif_info->wake_thread);
+- ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info,
+- "kssif%4.4x", thread_num);
+- if (IS_ERR(ssif_info->thread)) {
+- rv = PTR_ERR(ssif_info->thread);
+- dev_notice(&ssif_info->client->dev,
+- "Could not start kernel thread: error %d\n",
+- rv);
+- goto out;
+- }
++ thread_num = ((i2c_adapter_id(ssif_info->client->adapter) << 8) |
++ ssif_info->client->addr);
++ init_completion(&ssif_info->wake_thread);
++ ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info,
++ "kssif%4.4x", thread_num);
++ if (IS_ERR(ssif_info->thread)) {
++ rv = PTR_ERR(ssif_info->thread);
++ dev_notice(&ssif_info->client->dev,
++ "Could not start kernel thread: error %d\n",
++ rv);
++ goto out;
+ }
+
+ dev_set_drvdata(&ssif_info->client->dev, ssif_info);
+--
+2.53.0
+
x86-cpu-amd-rename-init_amd_zn-to-init_amd_zen_common.patch
x86-cpu-amd-add-x86_feature_zen1.patch
net-sched-sch_red-replace-direct-dequeue-call-with-peek-and-qdisc_dequeue_peeked.patch
+ipmi-ssif-fix-a-shutdown-race.patch
+ipmi-ssif-clean-up-kthread-on-errors.patch
+ipmi-ssif-remove-unnecessary-indention.patch
+ipmi-ssif-null-thread-on-error.patch
--- /dev/null
+From 42ad916de42b4e752067c0ebcf934e1afe93b9d0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 08:09:24 -0500
+Subject: ipmi:ssif: Clean up kthread on errors
+
+From: Corey Minyard <corey@minyard.net>
+
+commit 75c486cb1bcaa1a3ec3a6438498176a3a4998ae4 upstream.
+
+If an error occurs after the ssif kthread is created, but before the
+main IPMI code starts the ssif interface, the ssif kthread will not
+be stopped.
+
+So make sure the kthread is stopped on an error condition if it is
+running.
+
+Fixes: 259307074bfc ("ipmi: Add SMBus interface driver (SSIF)")
+Reported-by: Li Xiao <<252270051@hdu.edu.cn>
+Cc: stable@vger.kernel.org
+Reviewed-by: Li Xiao <252270051@hdu.edu.cn>
+[Adjusted for stopping flag and complete operation still being present.]
+Signed-off-by: Corey Minyard <corey@minyard.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/char/ipmi/ipmi_ssif.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
+index 42cbf761fa749..55ebe1d31766b 100644
+--- a/drivers/char/ipmi/ipmi_ssif.c
++++ b/drivers/char/ipmi/ipmi_ssif.c
+@@ -1311,6 +1311,7 @@ static void shutdown_ssif(void *send_info)
+ if (ssif_info->thread) {
+ complete(&ssif_info->wake_thread);
+ kthread_stop(ssif_info->thread);
++ ssif_info->thread = NULL;
+ }
+ }
+
+@@ -1941,6 +1942,17 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
+
+ out:
+ if (rv) {
++ /*
++ * If ipmi_register_smi() starts the interface, it will
++ * call shutdown and that will free the thread and set
++ * it to NULL. Otherwise it must be freed here.
++ */
++ if (ssif_info->thread) {
++ ssif_info->stopping = true;
++ complete(&ssif_info->wake_thread);
++ kthread_stop(ssif_info->thread);
++ ssif_info->thread = NULL;
++ }
+ if (addr_info)
+ addr_info->client = NULL;
+
+--
+2.53.0
+
--- /dev/null
+From 6d9f028763bba86f44982eef81d281f7fca1cf7c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 08:09:23 -0500
+Subject: ipmi:ssif: Fix a shutdown race
+
+From: Corey Minyard <corey@minyard.net>
+
+It was possible for the SSIF thread to stop and quit before the
+kthread_stop() call because ssif->stopping was set before the
+stop. So only exit the SSIF thread is kthread_should_stop()
+returns true.
+
+In the mainstream kernel this was fixed in 6bd0eb6d759b ("ipmi:ssif:
+Fix a shutdown race"). However, that requires a fix in kernel
+version 6.1 has a fix to kthread stop to cause interruptible waits
+to return -ERESTARTSYS on a stop. This has not been backported to
+older kernels, and that would probably be a bad idea. But it means
+that the mainstrem kernel fix for this will not work.
+
+Instead, wait for kthread_should_stop() to return true before exiting
+the thread.
+
+Signed-off-by: Corey Minyard <cminyard@mvista.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/char/ipmi/ipmi_ssif.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
+index a811b5bdba259..42cbf761fa749 100644
+--- a/drivers/char/ipmi/ipmi_ssif.c
++++ b/drivers/char/ipmi/ipmi_ssif.c
+@@ -522,6 +522,16 @@ static int ipmi_ssif_thread(void *data)
+ }
+ }
+
++ /*
++ * The thread can break out of the loop if stopping is set,
++ * and this can be before kthread_stop() gets called and thus
++ * kthread_should_stop() will not be set. This can cause
++ * spinning calling this function and other bad things. So
++ * wait for kthread_should_stop() to be set.
++ */
++ while (!kthread_should_stop())
++ msleep_interruptible(1);
++
+ return 0;
+ }
+
+--
+2.53.0
+
--- /dev/null
+From 655ef16318f052bccd4c59eb61bfe309139af221 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 08:09:26 -0500
+Subject: ipmi:ssif: NULL thread on error
+
+From: Corey Minyard <corey@minyard.net>
+
+commit a8aebe93a4938c0ca1941eeaae821738f869be3d upstream.
+
+Cleanup code was checking the thread for NULL, but it was possibly
+a PTR_ERR() in one spot.
+
+Spotted with static analysis.
+
+Link: https://sourceforge.net/p/openipmi/mailman/message/59324676/
+Fixes: 75c486cb1bca ("ipmi:ssif: Clean up kthread on errors")
+Cc: <stable@vger.kernel.org> # 91eb7ec72612: ipmi:ssif: Remove unnecessary indention
+Cc: stable@vger.kernel.org
+Signed-off-by: Corey Minyard <corey@minyard.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/char/ipmi/ipmi_ssif.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
+index e93846f8f2352..4cbfe1858ab4f 100644
+--- a/drivers/char/ipmi/ipmi_ssif.c
++++ b/drivers/char/ipmi/ipmi_ssif.c
+@@ -1910,6 +1910,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
+ "kssif%4.4x", thread_num);
+ if (IS_ERR(ssif_info->thread)) {
+ rv = PTR_ERR(ssif_info->thread);
++ ssif_info->thread = NULL;
+ dev_notice(&ssif_info->client->dev,
+ "Could not start kernel thread: error %d\n",
+ rv);
+--
+2.53.0
+
--- /dev/null
+From 97c011411c8fe21b7fdd9e17bd7d5fb471fdeb3b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 08:09:25 -0500
+Subject: ipmi:ssif: Remove unnecessary indention
+
+From: Corey Minyard <corey@minyard.net>
+
+commit 91eb7ec7261254b6875909df767185838598e21e upstream.
+
+A section was in {} that didn't need to be, move the variable
+definition to the top and set th eindentino properly.
+
+Signed-off-by: Corey Minyard <corey@minyard.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/char/ipmi/ipmi_ssif.c | 28 ++++++++++++----------------
+ 1 file changed, 12 insertions(+), 16 deletions(-)
+
+diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
+index 55ebe1d31766b..e93846f8f2352 100644
+--- a/drivers/char/ipmi/ipmi_ssif.c
++++ b/drivers/char/ipmi/ipmi_ssif.c
+@@ -1694,6 +1694,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
+ int len;
+ int i;
+ u8 slave_addr = 0;
++ unsigned int thread_num;
+ struct ssif_addr_info *addr_info = NULL;
+
+ mutex_lock(&ssif_infos_mutex);
+@@ -1902,22 +1903,17 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
+ ssif_info->handlers.request_events = request_events;
+ ssif_info->handlers.set_need_watch = ssif_set_need_watch;
+
+- {
+- unsigned int thread_num;
+-
+- thread_num = ((i2c_adapter_id(ssif_info->client->adapter)
+- << 8) |
+- ssif_info->client->addr);
+- init_completion(&ssif_info->wake_thread);
+- ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info,
+- "kssif%4.4x", thread_num);
+- if (IS_ERR(ssif_info->thread)) {
+- rv = PTR_ERR(ssif_info->thread);
+- dev_notice(&ssif_info->client->dev,
+- "Could not start kernel thread: error %d\n",
+- rv);
+- goto out;
+- }
++ thread_num = ((i2c_adapter_id(ssif_info->client->adapter) << 8) |
++ ssif_info->client->addr);
++ init_completion(&ssif_info->wake_thread);
++ ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info,
++ "kssif%4.4x", thread_num);
++ if (IS_ERR(ssif_info->thread)) {
++ rv = PTR_ERR(ssif_info->thread);
++ dev_notice(&ssif_info->client->dev,
++ "Could not start kernel thread: error %d\n",
++ rv);
++ goto out;
+ }
+
+ dev_set_drvdata(&ssif_info->client->dev, ssif_info);
+--
+2.53.0
+
um-virt-pci-fix-build-failure.patch
octeontx2-pf-handle-otx2_mbox_get_rsp-errors-in-otx2.patch
net-sched-sch_red-replace-direct-dequeue-call-with-peek-and-qdisc_dequeue_peeked.patch
+ipmi-ssif-fix-a-shutdown-race.patch
+ipmi-ssif-clean-up-kthread-on-errors.patch
+ipmi-ssif-remove-unnecessary-indention.patch
+ipmi-ssif-null-thread-on-error.patch
--- /dev/null
+From 1c1508b77974cdf53f1c7a74f328c08f67af8bde Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 15:41:19 +0800
+Subject: rxrpc: Also unshare DATA/RESPONSE packets when paged frags are
+ present
+
+From: Hyunwoo Kim <imv4bel@gmail.com>
+
+commit aa54b1d27fe0c2b78e664a34fd0fdf7cd1960d71 upstream.
+
+The DATA-packet handler in rxrpc_input_call_event() and the RESPONSE
+handler in rxrpc_verify_response() copy the skb to a linear one before
+calling into the security ops only when skb_cloned() is true. An skb
+that is not cloned but still carries externally-owned paged fragments
+(e.g. SKBFL_SHARED_FRAG set by splice() into a UDP socket via
+__ip_append_data, or a chained skb_has_frag_list()) falls through to
+the in-place decryption path, which binds the frag pages directly into
+the AEAD/skcipher SGL via skb_to_sgvec().
+
+Extend the gate to also unshare when skb_has_frag_list() or
+skb_has_shared_frag() is true. This catches the splice-loopback vector
+and other externally-shared frag sources while preserving the
+zero-copy fast path for skbs whose frags are kernel-private (e.g. NIC
+page_pool RX, GRO). The OOM/trace handling already in place is reused.
+
+Fixes: d0d5c0cd1e71 ("rxrpc: Use skb_unshare() rather than skb_cow_data()")
+Cc: stable@vger.kernel.org
+Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
+Reviewed-by: Jiayuan Chen <jiayuan.chen@linux.dev>
+Acked-by: David Howells <dhowells@redhat.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/rxrpc/call_event.c | 4 +++-
+ net/rxrpc/conn_event.c | 3 ++-
+ 2 files changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
+index 62ddaa129ce5a..fda16b39e8e73 100644
+--- a/net/rxrpc/call_event.c
++++ b/net/rxrpc/call_event.c
+@@ -347,7 +347,9 @@ bool rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
+
+ if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA &&
+ sp->hdr.securityIndex != 0 &&
+- skb_cloned(skb)) {
++ (skb_cloned(skb) ||
++ skb_has_frag_list(skb) ||
++ skb_has_shared_frag(skb))) {
+ /* Unshare the packet so that it can be modified for
+ * in-place decryption.
+ */
+diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c
+index 6dcfaed1f7485..3a58fb9210383 100644
+--- a/net/rxrpc/conn_event.c
++++ b/net/rxrpc/conn_event.c
+@@ -231,7 +231,8 @@ static int rxrpc_verify_response(struct rxrpc_connection *conn,
+ {
+ int ret;
+
+- if (skb_cloned(skb)) {
++ if (skb_cloned(skb) || skb_has_frag_list(skb) ||
++ skb_has_shared_frag(skb)) {
+ /* Copy the packet if shared so that we can do in-place
+ * decryption.
+ */
+--
+2.53.0
+
--- /dev/null
+From e80c56cdc80e197f81bd94fe40a29092f28af03f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 15:41:04 +0800
+Subject: rxrpc: Fix conn-level packet handling to unshare RESPONSE packets
+
+From: David Howells <dhowells@redhat.com>
+
+commit 24481a7f573305706054c59e275371f8d0fe919f upstream.
+
+The security operations that verify the RESPONSE packets decrypt bits of it
+in place - however, the sk_buff may be shared with a packet sniffer, which
+would lead to the sniffer seeing an apparently corrupt packet (actually
+decrypted).
+
+Fix this by handing a copy of the packet off to the specific security
+handler if the packet was cloned.
+
+Fixes: 17926a79320a ("[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both")
+Closes: https://sashiko.dev/#/patchset/20260408121252.2249051-1-dhowells%40redhat.com
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: Jeffrey Altman <jaltman@auristor.com>
+cc: Simon Horman <horms@kernel.org>
+cc: linux-afs@lists.infradead.org
+cc: stable@kernel.org
+Link: https://patch.msgid.link/20260422161438.2593376-5-dhowells@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[Readd rxrpc_skb_put_response_copy which missed in bf20f46d94f1 in v6.12.86]
+Stable-dep-of: aa54b1d27fe0 ("rxrpc: Also unshare DATA/RESPONSE packets when
+paged frags are present")
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/trace/events/rxrpc.h | 1 +
+ net/rxrpc/conn_event.c | 29 ++++++++++++++++++++++++++++-
+ 2 files changed, 29 insertions(+), 1 deletion(-)
+
+diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
+index 9377acad0c5f9..63efc9e4e4102 100644
+--- a/include/trace/events/rxrpc.h
++++ b/include/trace/events/rxrpc.h
+@@ -146,6 +146,7 @@
+ EM(rxrpc_skb_put_jumbo_subpacket, "PUT jumbo-sub") \
+ EM(rxrpc_skb_put_last_nack, "PUT last-nack") \
+ EM(rxrpc_skb_put_purge, "PUT purge ") \
++ EM(rxrpc_skb_put_response_copy, "PUT resp-cpy ") \
+ EM(rxrpc_skb_put_rotate, "PUT rotate ") \
+ EM(rxrpc_skb_put_unknown, "PUT unknown ") \
+ EM(rxrpc_skb_see_conn_work, "SEE conn-work") \
+diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c
+index 82cc72123c9c9..6dcfaed1f7485 100644
+--- a/net/rxrpc/conn_event.c
++++ b/net/rxrpc/conn_event.c
+@@ -226,6 +226,33 @@ static void rxrpc_call_is_secure(struct rxrpc_call *call)
+ rxrpc_notify_socket(call);
+ }
+
++static int rxrpc_verify_response(struct rxrpc_connection *conn,
++ struct sk_buff *skb)
++{
++ int ret;
++
++ if (skb_cloned(skb)) {
++ /* Copy the packet if shared so that we can do in-place
++ * decryption.
++ */
++ struct sk_buff *nskb = skb_copy(skb, GFP_NOFS);
++
++ if (nskb) {
++ rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
++ ret = conn->security->verify_response(conn, nskb);
++ rxrpc_free_skb(nskb, rxrpc_skb_put_response_copy);
++ } else {
++ /* OOM - Drop the packet. */
++ rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
++ ret = -ENOMEM;
++ }
++ } else {
++ ret = conn->security->verify_response(conn, skb);
++ }
++
++ return ret;
++}
++
+ /*
+ * connection-level Rx packet processor
+ */
+@@ -253,7 +280,7 @@ static int rxrpc_process_event(struct rxrpc_connection *conn,
+ }
+ spin_unlock(&conn->state_lock);
+
+- ret = conn->security->verify_response(conn, skb);
++ ret = rxrpc_verify_response(conn, skb);
+ if (ret < 0)
+ return ret;
+
+--
+2.53.0
+
kvm-svm-check-validity-of-vmcb-controls-when-returning-from-smm.patch
net-sched-sch_red-replace-direct-dequeue-call-with-peek-and-qdisc_dequeue_peeked.patch
bluetooth-l2cap-fix-deadlock-in-l2cap_conn_del.patch
+rxrpc-fix-conn-level-packet-handling-to-unshare-resp.patch
+rxrpc-also-unshare-data-response-packets-when-paged-.patch
--- /dev/null
+From afa3ebbb730a90c86de45f1fda443f6198b2d873 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 3 Dec 2024 20:47:53 -0800
+Subject: bpf: Don't mark STACK_INVALID as STACK_MISC in mark_stack_slot_misc
+
+From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
+
+[ Upstream commit 69772f509e084ec6bca12dbcdeeeff41b0103774 ]
+
+Inside mark_stack_slot_misc, we should not upgrade STACK_INVALID to
+STACK_MISC when allow_ptr_leaks is false, since invalid contents
+shouldn't be read unless the program has the relevant capabilities.
+The relaxation only makes sense when env->allow_ptr_leaks is true.
+
+However, such conversion in privileged mode becomes unnecessary, as
+invalid slots can be read without being upgraded to STACK_MISC.
+
+Currently, the condition is inverted (i.e. checking for true instead of
+false), simply remove it to restore correct behavior.
+
+Fixes: eaf18febd6eb ("bpf: preserve STACK_ZERO slots on partial reg spills")
+Acked-by: Andrii Nakryiko <andrii@kernel.org>
+Reported-by: Tao Lyu <tao.lyu@epfl.ch>
+Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
+Link: https://lore.kernel.org/r/20241204044757.1483141-2-memxor@gmail.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/bpf/verifier.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
+index f6040169ef749..b7fd3995538bf 100644
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -1350,14 +1350,17 @@ static bool is_spilled_scalar_reg(const struct bpf_stack_state *stack)
+ /* Mark stack slot as STACK_MISC, unless it is already STACK_INVALID, in which
+ * case they are equivalent, or it's STACK_ZERO, in which case we preserve
+ * more precise STACK_ZERO.
+- * Note, in uprivileged mode leaving STACK_INVALID is wrong, so we take
+- * env->allow_ptr_leaks into account and force STACK_MISC, if necessary.
++ * Regardless of allow_ptr_leaks setting (i.e., privileged or unprivileged
++ * mode), we won't promote STACK_INVALID to STACK_MISC. In privileged case it is
++ * unnecessary as both are considered equivalent when loading data and pruning,
++ * in case of unprivileged mode it will be incorrect to allow reads of invalid
++ * slots.
+ */
+ static void mark_stack_slot_misc(struct bpf_verifier_env *env, u8 *stype)
+ {
+ if (*stype == STACK_ZERO)
+ return;
+- if (env->allow_ptr_leaks && *stype == STACK_INVALID)
++ if (*stype == STACK_INVALID)
+ return;
+ *stype = STACK_MISC;
+ }
+--
+2.53.0
+
--- /dev/null
+From 242c333b11a71f3577536c23d78a2a1b77d45da5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 18:25:08 +0200
+Subject: bpf: handle fake register spill to stack with BPF_ST_MEM instruction
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit 482d548d40b0af9af730e4869903d4433e44f014 ]
+
+When verifier validates BPF_ST_MEM instruction that stores known
+constant to stack (e.g., *(u64 *)(r10 - 8) = 123), it effectively spills
+a fake register with a constant (but initially imprecise) value to
+a stack slot. Because read-side logic treats it as a proper register
+fill from stack slot, we need to mark such stack slot initialization as
+INSN_F_STACK_ACCESS instruction to stop precision backtracking from
+missing it.
+
+Fixes: 41f6f64e6999 ("bpf: support non-r10 register spill/fill to/from stack in precision tracking")
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Acked-by: Eduard Zingerman <eddyz87@gmail.com>
+Link: https://lore.kernel.org/r/20231209010958.66758-1-andrii@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/bpf/verifier.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
+index 705582bdda681..f6040169ef749 100644
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -4678,7 +4678,6 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
+ __mark_reg_known(&fake_reg, insn->imm);
+ fake_reg.type = SCALAR_VALUE;
+ save_register_state(env, state, spi, &fake_reg, size);
+- insn_flags = 0; /* not a register spill */
+ } else if (reg && is_spillable_regtype(reg->type)) {
+ /* register containing pointer is being spilled into stack */
+ if (size != BPF_REG_SIZE) {
+--
+2.53.0
+
--- /dev/null
+From 93a3badc02b18ede28d034877e92c045ecb2f3db Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 18:24:13 +0200
+Subject: bpf: preserve constant zero when doing partial register restore
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit e322f0bcb8d371f4606eaf141c7f967e1a79bcb7 ]
+
+Similar to special handling of STACK_ZERO, when reading 1/2/4 bytes from
+stack from slot that has register spilled into it and that register has
+a constant value zero, preserve that zero and mark spilled register as
+precise for that. This makes spilled const zero register and STACK_ZERO
+cases equivalent in their behavior.
+
+Acked-by: Eduard Zingerman <eddyz87@gmail.com>
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/r/20231205184248.1502704-7-andrii@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/bpf/verifier.c | 25 +++++++++++++++++++++----
+ 1 file changed, 21 insertions(+), 4 deletions(-)
+
+diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
+index 8309504d1660e..eaeb996ff56a2 100644
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -4952,22 +4952,39 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
+ copy_register_state(&state->regs[dst_regno], reg);
+ state->regs[dst_regno].subreg_def = subreg_def;
+ } else {
++ int spill_cnt = 0, zero_cnt = 0;
++
+ for (i = 0; i < size; i++) {
+ type = stype[(slot - i) % BPF_REG_SIZE];
+- if (type == STACK_SPILL)
++ if (type == STACK_SPILL) {
++ spill_cnt++;
+ continue;
++ }
+ if (type == STACK_MISC)
+ continue;
+- if (type == STACK_ZERO)
++ if (type == STACK_ZERO) {
++ zero_cnt++;
+ continue;
++ }
+ if (type == STACK_INVALID && env->allow_uninit_stack)
+ continue;
+ verbose(env, "invalid read from stack off %d+%d size %d\n",
+ off, i, size);
+ return -EACCES;
+ }
+- mark_reg_unknown(env, state->regs, dst_regno);
+- insn_flags = 0; /* not restoring original register state */
++
++ if (spill_cnt == size &&
++ tnum_is_const(reg->var_off) && reg->var_off.value == 0) {
++ __mark_reg_const_zero(&state->regs[dst_regno]);
++ /* this IS register fill, so keep insn_flags */
++ } else if (zero_cnt == size) {
++ /* similarly to mark_reg_stack_read(), preserve zeroes */
++ __mark_reg_const_zero(&state->regs[dst_regno]);
++ insn_flags = 0; /* not restoring original register state */
++ } else {
++ mark_reg_unknown(env, state->regs, dst_regno);
++ insn_flags = 0; /* not restoring original register state */
++ }
+ }
+ state->regs[dst_regno].live |= REG_LIVE_WRITTEN;
+ } else if (dst_regno >= 0) {
+--
+2.53.0
+
--- /dev/null
+From 8ec4987d65565c1d66f847cf368bb96a5befbd62 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 18:23:50 +0200
+Subject: bpf: preserve STACK_ZERO slots on partial reg spills
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit eaf18febd6ebc381aeb61543705148b3e28c7c47 ]
+
+Instead of always forcing STACK_ZERO slots to STACK_MISC, preserve it in
+situations where this is possible. E.g., when spilling register as
+1/2/4-byte subslots on the stack, all the remaining bytes in the stack
+slot do not automatically become unknown. If we knew they contained
+zeroes, we can preserve those STACK_ZERO markers.
+
+Add a helper mark_stack_slot_misc(), similar to scrub_spilled_slot(),
+but that doesn't overwrite either STACK_INVALID nor STACK_ZERO. Note
+that we need to take into account possibility of being in unprivileged
+mode, in which case STACK_INVALID is forced to STACK_MISC for correctness,
+as treating STACK_INVALID as equivalent STACK_MISC is only enabled in
+privileged mode.
+
+Acked-by: Eduard Zingerman <eddyz87@gmail.com>
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/r/20231205184248.1502704-5-andrii@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/bpf/verifier.c | 28 +++++++++++++++++++++++-----
+ 1 file changed, 23 insertions(+), 5 deletions(-)
+
+diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
+index e44da369dff63..8309504d1660e 100644
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -1347,6 +1347,21 @@ static bool is_spilled_scalar_reg(const struct bpf_stack_state *stack)
+ stack->spilled_ptr.type == SCALAR_VALUE;
+ }
+
++/* Mark stack slot as STACK_MISC, unless it is already STACK_INVALID, in which
++ * case they are equivalent, or it's STACK_ZERO, in which case we preserve
++ * more precise STACK_ZERO.
++ * Note, in uprivileged mode leaving STACK_INVALID is wrong, so we take
++ * env->allow_ptr_leaks into account and force STACK_MISC, if necessary.
++ */
++static void mark_stack_slot_misc(struct bpf_verifier_env *env, u8 *stype)
++{
++ if (*stype == STACK_ZERO)
++ return;
++ if (env->allow_ptr_leaks && *stype == STACK_INVALID)
++ return;
++ *stype = STACK_MISC;
++}
++
+ static void scrub_spilled_slot(u8 *stype)
+ {
+ if (*stype != STACK_INVALID)
+@@ -4577,7 +4592,8 @@ static void copy_register_state(struct bpf_reg_state *dst, const struct bpf_reg_
+ dst->live = live;
+ }
+
+-static void save_register_state(struct bpf_func_state *state,
++static void save_register_state(struct bpf_verifier_env *env,
++ struct bpf_func_state *state,
+ int spi, struct bpf_reg_state *reg,
+ int size)
+ {
+@@ -4592,7 +4608,7 @@ static void save_register_state(struct bpf_func_state *state,
+
+ /* size < 8 bytes spill */
+ for (; i; i--)
+- scrub_spilled_slot(&state->stack[spi].slot_type[i - 1]);
++ mark_stack_slot_misc(env, &state->stack[spi].slot_type[i - 1]);
+ }
+
+ static bool is_bpf_st_mem(struct bpf_insn *insn)
+@@ -4652,7 +4668,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
+ mark_stack_slot_scratched(env, spi);
+ if (reg && !(off % BPF_REG_SIZE) && register_is_bounded(reg) &&
+ !register_is_null(reg) && env->bpf_capable) {
+- save_register_state(state, spi, reg, size);
++ save_register_state(env, state, spi, reg, size);
+ /* Break the relation on a narrowing spill. */
+ if (fls64(reg->umax_value) > BITS_PER_BYTE * size)
+ state->stack[spi].spilled_ptr.id = 0;
+@@ -4662,7 +4678,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
+
+ __mark_reg_known(&fake_reg, insn->imm);
+ fake_reg.type = SCALAR_VALUE;
+- save_register_state(state, spi, &fake_reg, size);
++ save_register_state(env, state, spi, &fake_reg, size);
+ insn_flags = 0; /* not a register spill */
+ } else if (reg && is_spillable_regtype(reg->type)) {
+ /* register containing pointer is being spilled into stack */
+@@ -4675,7 +4691,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
+ verbose(env, "cannot spill pointers to stack into stack frame of the caller\n");
+ return -EINVAL;
+ }
+- save_register_state(state, spi, reg, size);
++ save_register_state(env, state, spi, reg, size);
+ } else {
+ u8 type = STACK_MISC;
+
+@@ -4942,6 +4958,8 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
+ continue;
+ if (type == STACK_MISC)
+ continue;
++ if (type == STACK_ZERO)
++ continue;
+ if (type == STACK_INVALID && env->allow_uninit_stack)
+ continue;
+ verbose(env, "invalid read from stack off %d+%d size %d\n",
+--
+2.53.0
+
--- /dev/null
+From 76ccb0cfa693c1337a874722a89b444b870ca8a2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 18:22:35 +0200
+Subject: bpf: support non-r10 register spill/fill to/from stack in precision
+ tracking
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit 41f6f64e6999a837048b1bd13a2f8742964eca6b ]
+
+Use instruction (jump) history to record instructions that performed
+register spill/fill to/from stack, regardless if this was done through
+read-only r10 register, or any other register after copying r10 into it
+*and* potentially adjusting offset.
+
+To make this work reliably, we push extra per-instruction flags into
+instruction history, encoding stack slot index (spi) and stack frame
+number in extra 10 bit flags we take away from prev_idx in instruction
+history. We don't touch idx field for maximum performance, as it's
+checked most frequently during backtracking.
+
+This change removes basically the last remaining practical limitation of
+precision backtracking logic in BPF verifier. It fixes known
+deficiencies, but also opens up new opportunities to reduce number of
+verified states, explored in the subsequent patches.
+
+There are only three differences in selftests' BPF object files
+according to veristat, all in the positive direction (less states).
+
+File Program Insns (A) Insns (B) Insns (DIFF) States (A) States (B) States (DIFF)
+-------------------------------------- ------------- --------- --------- ------------- ---------- ---------- -------------
+test_cls_redirect_dynptr.bpf.linked3.o cls_redirect 2987 2864 -123 (-4.12%) 240 231 -9 (-3.75%)
+xdp_synproxy_kern.bpf.linked3.o syncookie_tc 82848 82661 -187 (-0.23%) 5107 5073 -34 (-0.67%)
+xdp_synproxy_kern.bpf.linked3.o syncookie_xdp 85116 84964 -152 (-0.18%) 5162 5130 -32 (-0.62%)
+
+Note, I avoided renaming jmp_history to more generic insn_hist to
+minimize number of lines changed and potential merge conflicts between
+bpf and bpf-next trees.
+
+Notice also cur_hist_entry pointer reset to NULL at the beginning of
+instruction verification loop. This pointer avoids the problem of
+relying on last jump history entry's insn_idx to determine whether we
+already have entry for current instruction or not. It can happen that we
+added jump history entry because current instruction is_jmp_point(), but
+also we need to add instruction flags for stack access. In this case, we
+don't want to entries, so we need to reuse last added entry, if it is
+present.
+
+Relying on insn_idx comparison has the same ambiguity problem as the one
+that was fixed recently in [0], so we avoid that.
+
+ [0] https://patchwork.kernel.org/project/netdevbpf/patch/20231110002638.4168352-3-andrii@kernel.org/
+
+Acked-by: Eduard Zingerman <eddyz87@gmail.com>
+Reported-by: Tao Lyu <tao.lyu@epfl.ch>
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/r/20231205184248.1502704-2-andrii@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+[ Note: Adapted the expected log format for selftests as the map format
+ in verifier logs was changed in commits 1db747d75b1d and
+ 0c95c9fdb696. ]
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/bpf_verifier.h | 31 +++-
+ kernel/bpf/verifier.c | 175 ++++++++++--------
+ .../bpf/progs/verifier_subprog_precision.c | 23 ++-
+ .../testing/selftests/bpf/verifier/precise.c | 38 ++--
+ 4 files changed, 169 insertions(+), 98 deletions(-)
+
+diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
+index 32e89758176be..dba211d3bb9a0 100644
+--- a/include/linux/bpf_verifier.h
++++ b/include/linux/bpf_verifier.h
+@@ -319,12 +319,34 @@ struct bpf_func_state {
+ struct bpf_stack_state *stack;
+ };
+
+-struct bpf_idx_pair {
+- u32 prev_idx;
++#define MAX_CALL_FRAMES 8
++
++/* instruction history flags, used in bpf_jmp_history_entry.flags field */
++enum {
++ /* instruction references stack slot through PTR_TO_STACK register;
++ * we also store stack's frame number in lower 3 bits (MAX_CALL_FRAMES is 8)
++ * and accessed stack slot's index in next 6 bits (MAX_BPF_STACK is 512,
++ * 8 bytes per slot, so slot index (spi) is [0, 63])
++ */
++ INSN_F_FRAMENO_MASK = 0x7, /* 3 bits */
++
++ INSN_F_SPI_MASK = 0x3f, /* 6 bits */
++ INSN_F_SPI_SHIFT = 3, /* shifted 3 bits to the left */
++
++ INSN_F_STACK_ACCESS = BIT(9), /* we need 10 bits total */
++};
++
++static_assert(INSN_F_FRAMENO_MASK + 1 >= MAX_CALL_FRAMES);
++static_assert(INSN_F_SPI_MASK + 1 >= MAX_BPF_STACK / 8);
++
++struct bpf_jmp_history_entry {
+ u32 idx;
++ /* insn idx can't be bigger than 1 million */
++ u32 prev_idx : 22;
++ /* special flags, e.g., whether insn is doing register stack spill/load */
++ u32 flags : 10;
+ };
+
+-#define MAX_CALL_FRAMES 8
+ /* Maximum number of register states that can exist at once */
+ #define BPF_ID_MAP_SIZE ((MAX_BPF_REG + MAX_BPF_STACK / BPF_REG_SIZE) * MAX_CALL_FRAMES)
+ struct bpf_verifier_state {
+@@ -407,7 +429,7 @@ struct bpf_verifier_state {
+ * For most states jmp_history_cnt is [0-3].
+ * For loops can go up to ~40.
+ */
+- struct bpf_idx_pair *jmp_history;
++ struct bpf_jmp_history_entry *jmp_history;
+ u32 jmp_history_cnt;
+ u32 dfs_depth;
+ u32 callback_unroll_depth;
+@@ -641,6 +663,7 @@ struct bpf_verifier_env {
+ int cur_stack;
+ } cfg;
+ struct backtrack_state bt;
++ struct bpf_jmp_history_entry *cur_hist_ent;
+ u32 pass_cnt; /* number of times do_check() was called */
+ u32 subprog_cnt;
+ /* number of instructions analyzed by the verifier */
+diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
+index 45eb795c8c045..e44da369dff63 100644
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -1763,8 +1763,8 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state,
+ int i, err;
+
+ dst_state->jmp_history = copy_array(dst_state->jmp_history, src->jmp_history,
+- src->jmp_history_cnt, sizeof(struct bpf_idx_pair),
+- GFP_USER);
++ src->jmp_history_cnt, sizeof(*dst_state->jmp_history),
++ GFP_USER);
+ if (!dst_state->jmp_history)
+ return -ENOMEM;
+ dst_state->jmp_history_cnt = src->jmp_history_cnt;
+@@ -3418,6 +3418,21 @@ static int check_reg_arg(struct bpf_verifier_env *env, u32 regno,
+ return __check_reg_arg(env, state->regs, regno, t);
+ }
+
++static int insn_stack_access_flags(int frameno, int spi)
++{
++ return INSN_F_STACK_ACCESS | (spi << INSN_F_SPI_SHIFT) | frameno;
++}
++
++static int insn_stack_access_spi(int insn_flags)
++{
++ return (insn_flags >> INSN_F_SPI_SHIFT) & INSN_F_SPI_MASK;
++}
++
++static int insn_stack_access_frameno(int insn_flags)
++{
++ return insn_flags & INSN_F_FRAMENO_MASK;
++}
++
+ static void mark_jmp_point(struct bpf_verifier_env *env, int idx)
+ {
+ env->insn_aux_data[idx].jmp_point = true;
+@@ -3429,28 +3444,51 @@ static bool is_jmp_point(struct bpf_verifier_env *env, int insn_idx)
+ }
+
+ /* for any branch, call, exit record the history of jmps in the given state */
+-static int push_jmp_history(struct bpf_verifier_env *env,
+- struct bpf_verifier_state *cur)
++static int push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_state *cur,
++ int insn_flags)
+ {
+ u32 cnt = cur->jmp_history_cnt;
+- struct bpf_idx_pair *p;
++ struct bpf_jmp_history_entry *p;
+ size_t alloc_size;
+
+- if (!is_jmp_point(env, env->insn_idx))
++ /* combine instruction flags if we already recorded this instruction */
++ if (env->cur_hist_ent) {
++ /* atomic instructions push insn_flags twice, for READ and
++ * WRITE sides, but they should agree on stack slot
++ */
++ WARN_ONCE((env->cur_hist_ent->flags & insn_flags) &&
++ (env->cur_hist_ent->flags & insn_flags) != insn_flags,
++ "verifier insn history bug: insn_idx %d cur flags %x new flags %x\n",
++ env->insn_idx, env->cur_hist_ent->flags, insn_flags);
++ env->cur_hist_ent->flags |= insn_flags;
+ return 0;
++ }
+
+ cnt++;
+ alloc_size = kmalloc_size_roundup(size_mul(cnt, sizeof(*p)));
+ p = krealloc(cur->jmp_history, alloc_size, GFP_USER);
+ if (!p)
+ return -ENOMEM;
+- p[cnt - 1].idx = env->insn_idx;
+- p[cnt - 1].prev_idx = env->prev_insn_idx;
+ cur->jmp_history = p;
++
++ p = &cur->jmp_history[cnt - 1];
++ p->idx = env->insn_idx;
++ p->prev_idx = env->prev_insn_idx;
++ p->flags = insn_flags;
+ cur->jmp_history_cnt = cnt;
++ env->cur_hist_ent = p;
++
+ return 0;
+ }
+
++static struct bpf_jmp_history_entry *get_jmp_hist_entry(struct bpf_verifier_state *st,
++ u32 hist_end, int insn_idx)
++{
++ if (hist_end > 0 && st->jmp_history[hist_end - 1].idx == insn_idx)
++ return &st->jmp_history[hist_end - 1];
++ return NULL;
++}
++
+ /* Backtrack one insn at a time. If idx is not at the top of recorded
+ * history then previous instruction came from straight line execution.
+ * Return -ENOENT if we exhausted all instructions within given state.
+@@ -3612,9 +3650,14 @@ static inline bool bt_is_reg_set(struct backtrack_state *bt, u32 reg)
+ return bt->reg_masks[bt->frame] & (1 << reg);
+ }
+
++static inline bool bt_is_frame_slot_set(struct backtrack_state *bt, u32 frame, u32 slot)
++{
++ return bt->stack_masks[frame] & (1ull << slot);
++}
++
+ static inline bool bt_is_slot_set(struct backtrack_state *bt, u32 slot)
+ {
+- return bt->stack_masks[bt->frame] & (1ull << slot);
++ return bt_is_frame_slot_set(bt, bt->frame, slot);
+ }
+
+ /* format registers bitmask, e.g., "r0,r2,r4" for 0x15 mask */
+@@ -3668,7 +3711,7 @@ static bool calls_callback(struct bpf_verifier_env *env, int insn_idx);
+ * - *was* processed previously during backtracking.
+ */
+ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
+- struct backtrack_state *bt)
++ struct bpf_jmp_history_entry *hist, struct backtrack_state *bt)
+ {
+ const struct bpf_insn_cbs cbs = {
+ .cb_call = disasm_kfunc_name,
+@@ -3681,7 +3724,7 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
+ u8 mode = BPF_MODE(insn->code);
+ u32 dreg = insn->dst_reg;
+ u32 sreg = insn->src_reg;
+- u32 spi, i;
++ u32 spi, i, fr;
+
+ if (insn->code == 0)
+ return 0;
+@@ -3744,20 +3787,15 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
+ * by 'precise' mark in corresponding register of this state.
+ * No further tracking necessary.
+ */
+- if (insn->src_reg != BPF_REG_FP)
++ if (!hist || !(hist->flags & INSN_F_STACK_ACCESS))
+ return 0;
+-
+ /* dreg = *(u64 *)[fp - off] was a fill from the stack.
+ * that [fp - off] slot contains scalar that needs to be
+ * tracked with precision
+ */
+- spi = (-insn->off - 1) / BPF_REG_SIZE;
+- if (spi >= 64) {
+- verbose(env, "BUG spi %d\n", spi);
+- WARN_ONCE(1, "verifier backtracking bug");
+- return -EFAULT;
+- }
+- bt_set_slot(bt, spi);
++ spi = insn_stack_access_spi(hist->flags);
++ fr = insn_stack_access_frameno(hist->flags);
++ bt_set_frame_slot(bt, fr, spi);
+ } else if (class == BPF_STX || class == BPF_ST) {
+ if (bt_is_reg_set(bt, dreg))
+ /* stx & st shouldn't be using _scalar_ dst_reg
+@@ -3766,17 +3804,13 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
+ */
+ return -ENOTSUPP;
+ /* scalars can only be spilled into stack */
+- if (insn->dst_reg != BPF_REG_FP)
++ if (!hist || !(hist->flags & INSN_F_STACK_ACCESS))
+ return 0;
+- spi = (-insn->off - 1) / BPF_REG_SIZE;
+- if (spi >= 64) {
+- verbose(env, "BUG spi %d\n", spi);
+- WARN_ONCE(1, "verifier backtracking bug");
+- return -EFAULT;
+- }
+- if (!bt_is_slot_set(bt, spi))
++ spi = insn_stack_access_spi(hist->flags);
++ fr = insn_stack_access_frameno(hist->flags);
++ if (!bt_is_frame_slot_set(bt, fr, spi))
+ return 0;
+- bt_clear_slot(bt, spi);
++ bt_clear_frame_slot(bt, fr, spi);
+ if (class == BPF_STX)
+ bt_set_reg(bt, sreg);
+ } else if (class == BPF_JMP || class == BPF_JMP32) {
+@@ -3820,10 +3854,14 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
+ WARN_ONCE(1, "verifier backtracking bug");
+ return -EFAULT;
+ }
+- /* we don't track register spills perfectly,
+- * so fallback to force-precise instead of failing */
+- if (bt_stack_mask(bt) != 0)
+- return -ENOTSUPP;
++ /* we are now tracking register spills correctly,
++ * so any instance of leftover slots is a bug
++ */
++ if (bt_stack_mask(bt) != 0) {
++ verbose(env, "BUG stack slots %llx\n", bt_stack_mask(bt));
++ WARN_ONCE(1, "verifier backtracking bug (subprog leftover stack slots)");
++ return -EFAULT;
++ }
+ /* propagate r1-r5 to the caller */
+ for (i = BPF_REG_1; i <= BPF_REG_5; i++) {
+ if (bt_is_reg_set(bt, i)) {
+@@ -3848,8 +3886,11 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
+ WARN_ONCE(1, "verifier backtracking bug");
+ return -EFAULT;
+ }
+- if (bt_stack_mask(bt) != 0)
+- return -ENOTSUPP;
++ if (bt_stack_mask(bt) != 0) {
++ verbose(env, "BUG stack slots %llx\n", bt_stack_mask(bt));
++ WARN_ONCE(1, "verifier backtracking bug (callback leftover stack slots)");
++ return -EFAULT;
++ }
+ /* clear r1-r5 in callback subprog's mask */
+ for (i = BPF_REG_1; i <= BPF_REG_5; i++)
+ bt_clear_reg(bt, i);
+@@ -4286,6 +4327,7 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno)
+ for (;;) {
+ DECLARE_BITMAP(mask, 64);
+ u32 history = st->jmp_history_cnt;
++ struct bpf_jmp_history_entry *hist;
+
+ if (env->log.level & BPF_LOG_LEVEL2) {
+ verbose(env, "mark_precise: frame%d: last_idx %d first_idx %d subseq_idx %d \n",
+@@ -4349,7 +4391,8 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno)
+ err = 0;
+ skip_first = false;
+ } else {
+- err = backtrack_insn(env, i, subseq_idx, bt);
++ hist = get_jmp_hist_entry(st, history, i);
++ err = backtrack_insn(env, i, subseq_idx, hist, bt);
+ }
+ if (err == -ENOTSUPP) {
+ mark_all_scalars_precise(env, env->cur_state);
+@@ -4402,22 +4445,10 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno)
+ bitmap_from_u64(mask, bt_frame_stack_mask(bt, fr));
+ for_each_set_bit(i, mask, 64) {
+ if (i >= func->allocated_stack / BPF_REG_SIZE) {
+- /* the sequence of instructions:
+- * 2: (bf) r3 = r10
+- * 3: (7b) *(u64 *)(r3 -8) = r0
+- * 4: (79) r4 = *(u64 *)(r10 -8)
+- * doesn't contain jmps. It's backtracked
+- * as a single block.
+- * During backtracking insn 3 is not recognized as
+- * stack access, so at the end of backtracking
+- * stack slot fp-8 is still marked in stack_mask.
+- * However the parent state may not have accessed
+- * fp-8 and it's "unallocated" stack space.
+- * In such case fallback to conservative.
+- */
+- mark_all_scalars_precise(env, env->cur_state);
+- bt_reset(bt);
+- return 0;
++ verbose(env, "BUG backtracking (stack slot %d, total slots %d)\n",
++ i, func->allocated_stack / BPF_REG_SIZE);
++ WARN_ONCE(1, "verifier backtracking bug (stack slot out of bounds)");
++ return -EFAULT;
+ }
+
+ if (!is_spilled_scalar_reg(&func->stack[i])) {
+@@ -4582,7 +4613,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
+ int i, slot = -off - 1, spi = slot / BPF_REG_SIZE, err;
+ struct bpf_insn *insn = &env->prog->insnsi[insn_idx];
+ struct bpf_reg_state *reg = NULL;
+- u32 dst_reg = insn->dst_reg;
++ int insn_flags = insn_stack_access_flags(state->frameno, spi);
+
+ /* caller checked that off % size == 0 and -MAX_BPF_STACK <= off < 0,
+ * so it's aligned access and [off, off + size) are within stack limits
+@@ -4621,17 +4652,6 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
+ mark_stack_slot_scratched(env, spi);
+ if (reg && !(off % BPF_REG_SIZE) && register_is_bounded(reg) &&
+ !register_is_null(reg) && env->bpf_capable) {
+- if (dst_reg != BPF_REG_FP) {
+- /* The backtracking logic can only recognize explicit
+- * stack slot address like [fp - 8]. Other spill of
+- * scalar via different register has to be conservative.
+- * Backtrack from here and mark all registers as precise
+- * that contributed into 'reg' being a constant.
+- */
+- err = mark_chain_precision(env, value_regno);
+- if (err)
+- return err;
+- }
+ save_register_state(state, spi, reg, size);
+ /* Break the relation on a narrowing spill. */
+ if (fls64(reg->umax_value) > BITS_PER_BYTE * size)
+@@ -4643,6 +4663,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
+ __mark_reg_known(&fake_reg, insn->imm);
+ fake_reg.type = SCALAR_VALUE;
+ save_register_state(state, spi, &fake_reg, size);
++ insn_flags = 0; /* not a register spill */
+ } else if (reg && is_spillable_regtype(reg->type)) {
+ /* register containing pointer is being spilled into stack */
+ if (size != BPF_REG_SIZE) {
+@@ -4688,9 +4709,12 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
+
+ /* Mark slots affected by this stack write. */
+ for (i = 0; i < size; i++)
+- state->stack[spi].slot_type[(slot - i) % BPF_REG_SIZE] =
+- type;
++ state->stack[spi].slot_type[(slot - i) % BPF_REG_SIZE] = type;
++ insn_flags = 0; /* not a register spill */
+ }
++
++ if (insn_flags)
++ return push_jmp_history(env, env->cur_state, insn_flags);
+ return 0;
+ }
+
+@@ -4879,6 +4903,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
+ int i, slot = -off - 1, spi = slot / BPF_REG_SIZE;
+ struct bpf_reg_state *reg;
+ u8 *stype, type;
++ int insn_flags = insn_stack_access_flags(reg_state->frameno, spi);
+
+ stype = reg_state->stack[spi].slot_type;
+ reg = ®_state->stack[spi].spilled_ptr;
+@@ -4924,12 +4949,10 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
+ return -EACCES;
+ }
+ mark_reg_unknown(env, state->regs, dst_regno);
++ insn_flags = 0; /* not restoring original register state */
+ }
+ state->regs[dst_regno].live |= REG_LIVE_WRITTEN;
+- return 0;
+- }
+-
+- if (dst_regno >= 0) {
++ } else if (dst_regno >= 0) {
+ /* restore register state from stack */
+ copy_register_state(&state->regs[dst_regno], reg);
+ /* mark reg as written since spilled pointer state likely
+@@ -4965,7 +4988,10 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
+ mark_reg_read(env, reg, reg->parent, REG_LIVE_READ64);
+ if (dst_regno >= 0)
+ mark_reg_stack_read(env, reg_state, off, off + size, dst_regno);
++ insn_flags = 0; /* we are not restoring spilled register */
+ }
++ if (insn_flags)
++ return push_jmp_history(env, env->cur_state, insn_flags);
+ return 0;
+ }
+
+@@ -7050,7 +7076,6 @@ static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_i
+ BPF_SIZE(insn->code), BPF_WRITE, -1, true, false);
+ if (err)
+ return err;
+-
+ return 0;
+ }
+
+@@ -16845,7 +16870,8 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
+ * the precision needs to be propagated back in
+ * the current state.
+ */
+- err = err ? : push_jmp_history(env, cur);
++ if (is_jmp_point(env, env->insn_idx))
++ err = err ? : push_jmp_history(env, cur, 0);
+ err = err ? : propagate_precision(env, &sl->state);
+ if (err)
+ return err;
+@@ -17069,6 +17095,9 @@ static int do_check(struct bpf_verifier_env *env)
+ u8 class;
+ int err;
+
++ /* reset current history entry on each new instruction */
++ env->cur_hist_ent = NULL;
++
+ env->prev_insn_idx = prev_insn_idx;
+ if (env->insn_idx >= insn_cnt) {
+ verbose(env, "invalid insn idx %d insn_cnt %d\n",
+@@ -17108,7 +17137,7 @@ static int do_check(struct bpf_verifier_env *env)
+ }
+
+ if (is_jmp_point(env, env->insn_idx)) {
+- err = push_jmp_history(env, state);
++ err = push_jmp_history(env, state, 0);
+ if (err)
+ return err;
+ }
+diff --git a/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c b/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c
+index f61d623b1ce8d..7c159b5618624 100644
+--- a/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c
++++ b/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c
+@@ -541,11 +541,24 @@ static __u64 subprog_spill_reg_precise(void)
+
+ SEC("?raw_tp")
+ __success __log_level(2)
+-/* precision backtracking can't currently handle stack access not through r10,
+- * so we won't be able to mark stack slot fp-8 as precise, and so will
+- * fallback to forcing all as precise
+- */
+-__msg("mark_precise: frame0: falling back to forcing all scalars precise")
++__msg("10: (0f) r1 += r7")
++__msg("mark_precise: frame0: last_idx 10 first_idx 7 subseq_idx -1")
++__msg("mark_precise: frame0: regs=r7 stack= before 9: (bf) r1 = r8")
++__msg("mark_precise: frame0: regs=r7 stack= before 8: (27) r7 *= 4")
++__msg("mark_precise: frame0: regs=r7 stack= before 7: (79) r7 = *(u64 *)(r10 -8)")
++__msg("mark_precise: frame0: parent state regs= stack=-8: R0_w=2 R6_w=1 R8_rw=map_value(off=0,ks=4,vs=16,imm=0) R10=fp0 fp-8_rw=P1")
++__msg("mark_precise: frame0: last_idx 18 first_idx 0 subseq_idx 7")
++__msg("mark_precise: frame0: regs= stack=-8 before 18: (95) exit")
++__msg("mark_precise: frame1: regs= stack= before 17: (0f) r0 += r2")
++__msg("mark_precise: frame1: regs= stack= before 16: (79) r2 = *(u64 *)(r1 +0)")
++__msg("mark_precise: frame1: regs= stack= before 15: (79) r0 = *(u64 *)(r10 -16)")
++__msg("mark_precise: frame1: regs= stack= before 14: (7b) *(u64 *)(r10 -16) = r2")
++__msg("mark_precise: frame1: regs= stack= before 13: (7b) *(u64 *)(r1 +0) = r2")
++__msg("mark_precise: frame1: regs=r2 stack= before 6: (85) call pc+6")
++__msg("mark_precise: frame0: regs=r2 stack= before 5: (bf) r2 = r6")
++__msg("mark_precise: frame0: regs=r6 stack= before 4: (07) r1 += -8")
++__msg("mark_precise: frame0: regs=r6 stack= before 3: (bf) r1 = r10")
++__msg("mark_precise: frame0: regs=r6 stack= before 2: (b7) r6 = 1")
+ __naked int subprog_spill_into_parent_stack_slot_precise(void)
+ {
+ asm volatile (
+diff --git a/tools/testing/selftests/bpf/verifier/precise.c b/tools/testing/selftests/bpf/verifier/precise.c
+index 0d84dd1f38b6b..8a2ff81d83508 100644
+--- a/tools/testing/selftests/bpf/verifier/precise.c
++++ b/tools/testing/selftests/bpf/verifier/precise.c
+@@ -140,10 +140,11 @@
+ .result = REJECT,
+ },
+ {
+- "precise: ST insn causing spi > allocated_stack",
++ "precise: ST zero to stack insn is supported",
+ .insns = {
+ BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_3, 123, 0),
++ /* not a register spill, so we stop precision propagation for R4 here */
+ BPF_ST_MEM(BPF_DW, BPF_REG_3, -8, 0),
+ BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8),
+ BPF_MOV64_IMM(BPF_REG_0, -1),
+@@ -157,11 +158,11 @@
+ mark_precise: frame0: last_idx 4 first_idx 2\
+ mark_precise: frame0: regs=r4 stack= before 4\
+ mark_precise: frame0: regs=r4 stack= before 3\
+- mark_precise: frame0: regs= stack=-8 before 2\
+- mark_precise: frame0: falling back to forcing all scalars precise\
+- force_precise: frame0: forcing r0 to be precise\
+ mark_precise: frame0: last_idx 5 first_idx 5\
+- mark_precise: frame0: parent state regs= stack=:",
++ mark_precise: frame0: parent state regs=r0 stack=:\
++ mark_precise: frame0: last_idx 4 first_idx 2\
++ mark_precise: frame0: regs=r0 stack= before 4\
++ 5: R0=-1 R4=0",
+ .result = VERBOSE_ACCEPT,
+ .retval = -1,
+ },
+@@ -169,6 +170,8 @@
+ "precise: STX insn causing spi > allocated_stack",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
++ /* make later reg spill more interesting by having somewhat known scalar */
++ BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xff),
+ BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_3, 123, 0),
+ BPF_STX_MEM(BPF_DW, BPF_REG_3, BPF_REG_0, -8),
+@@ -179,18 +182,21 @@
+ },
+ .prog_type = BPF_PROG_TYPE_XDP,
+ .flags = BPF_F_TEST_STATE_FREQ,
+- .errstr = "mark_precise: frame0: last_idx 6 first_idx 6\
++ .errstr = "mark_precise: frame0: last_idx 7 first_idx 7\
+ mark_precise: frame0: parent state regs=r4 stack=:\
+- mark_precise: frame0: last_idx 5 first_idx 3\
+- mark_precise: frame0: regs=r4 stack= before 5\
+- mark_precise: frame0: regs=r4 stack= before 4\
+- mark_precise: frame0: regs= stack=-8 before 3\
+- mark_precise: frame0: falling back to forcing all scalars precise\
+- force_precise: frame0: forcing r0 to be precise\
+- force_precise: frame0: forcing r0 to be precise\
+- force_precise: frame0: forcing r0 to be precise\
+- force_precise: frame0: forcing r0 to be precise\
+- mark_precise: frame0: last_idx 6 first_idx 6\
++ mark_precise: frame0: last_idx 6 first_idx 4\
++ mark_precise: frame0: regs=r4 stack= before 6: (b7) r0 = -1\
++ mark_precise: frame0: regs=r4 stack= before 5: (79) r4 = *(u64 *)(r10 -8)\
++ mark_precise: frame0: regs= stack=-8 before 4: (7b) *(u64 *)(r3 -8) = r0\
++ mark_precise: frame0: parent state regs=r0 stack=:\
++ mark_precise: frame0: last_idx 3 first_idx 3\
++ mark_precise: frame0: regs=r0 stack= before 3: (55) if r3 != 0x7b goto pc+0\
++ mark_precise: frame0: regs=r0 stack= before 2: (bf) r3 = r10\
++ mark_precise: frame0: regs=r0 stack= before 1: (57) r0 &= 255\
++ mark_precise: frame0: parent state regs=r0 stack=:\
++ mark_precise: frame0: last_idx 0 first_idx 0\
++ mark_precise: frame0: regs=r0 stack= before 0: (85) call bpf_get_prandom_u32#7\
++ mark_precise: frame0: last_idx 7 first_idx 7\
+ mark_precise: frame0: parent state regs= stack=:",
+ .result = VERBOSE_ACCEPT,
+ .retval = -1,
+--
+2.53.0
+
--- /dev/null
+From cc8ef559d656769966b3fb1fcac7dbfc375a8c44 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 18:24:37 +0200
+Subject: bpf: track aligned STACK_ZERO cases as imprecise spilled registers
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit 18a433b62061e3d787bfc3e670fa711fecbd7cb4 ]
+
+Now that precision backtracing is supporting register spill/fill to/from
+stack, there is another oportunity to be exploited here: minimizing
+precise STACK_ZERO cases. With a simple code change we can rely on
+initially imprecise register spill tracking for cases when register
+spilled to stack was a known zero.
+
+This is a very common case for initializing on the stack variables,
+including rather large structures. Often times zero has no special
+meaning for the subsequent BPF program logic and is often overwritten
+with non-zero values soon afterwards. But due to STACK_ZERO vs
+STACK_MISC tracking, such initial zero initialization actually causes
+duplication of verifier states as STACK_ZERO is clearly different than
+STACK_MISC or spilled SCALAR_VALUE register.
+
+The effect of this (now) trivial change is huge, as can be seen below.
+These are differences between BPF selftests, Cilium, and Meta-internal
+BPF object files relative to previous patch in this series. You can see
+improvements ranging from single-digit percentage improvement for
+instructions and states, all the way to 50-60% reduction for some of
+Meta-internal host agent programs, and even some Cilium programs.
+
+For Meta-internal ones I left only the differences for largest BPF
+object files by states/instructions, as there were too many differences
+in the overall output. All the differences were improvements, reducting
+number of states and thus instructions validated.
+
+Note, Meta-internal BPF object file names are not printed below.
+Many copies of balancer_ingress are actually many different
+configurations of Katran, so they are different BPF programs, which
+explains state reduction going from -16% all the way to 31%, depending
+on BPF program logic complexity.
+
+I also tooked a closer look at a few small-ish BPF programs to validate
+the behavior. Let's take bpf_iter_netrlink.bpf.o (first row below).
+While it's just 8 vs 5 states, verifier log is still pretty long to
+include it here. But the reduction in states is due to the following
+piece of C code:
+
+ unsigned long ino;
+
+ ...
+
+ sk = s->sk_socket;
+ if (!sk) {
+ ino = 0;
+ } else {
+ inode = SOCK_INODE(sk);
+ bpf_probe_read_kernel(&ino, sizeof(ino), &inode->i_ino);
+ }
+ BPF_SEQ_PRINTF(seq, "%-8u %-8lu\n", s->sk_drops.counter, ino);
+ return 0;
+
+You can see that in some situations `ino` is zero-initialized, while in
+others it's unknown value filled out by bpf_probe_read_kernel(). Before
+this change code after if/else branches have to be validated twice. Once
+with (precise) ino == 0, due to eager STACK_ZERO logic, and then again
+for when ino is just STACK_MISC. But BPF_SEQ_PRINTF() doesn't care about
+precise value of ino, so with the change in this patch verifier is able
+to prune states from after one of the branches, reducing number of total
+states (and instructions) required for successful validation.
+
+Similar principle applies to bigger real-world applications, just at
+a much larger scale.
+
+SELFTESTS
+=========
+File Program Insns (A) Insns (B) Insns (DIFF) States (A) States (B) States (DIFF)
+--------------------------------------- ----------------------- --------- --------- --------------- ---------- ---------- -------------
+bpf_iter_netlink.bpf.linked3.o dump_netlink 148 104 -44 (-29.73%) 8 5 -3 (-37.50%)
+bpf_iter_unix.bpf.linked3.o dump_unix 8474 8404 -70 (-0.83%) 151 147 -4 (-2.65%)
+bpf_loop.bpf.linked3.o stack_check 560 324 -236 (-42.14%) 42 24 -18 (-42.86%)
+local_storage_bench.bpf.linked3.o get_local 120 77 -43 (-35.83%) 9 6 -3 (-33.33%)
+loop6.bpf.linked3.o trace_virtqueue_add_sgs 10167 9868 -299 (-2.94%) 226 206 -20 (-8.85%)
+pyperf600_bpf_loop.bpf.linked3.o on_event 4872 3423 -1449 (-29.74%) 322 229 -93 (-28.88%)
+strobemeta.bpf.linked3.o on_event 180697 176036 -4661 (-2.58%) 4780 4734 -46 (-0.96%)
+test_cls_redirect.bpf.linked3.o cls_redirect 65594 65401 -193 (-0.29%) 4230 4212 -18 (-0.43%)
+test_global_func_args.bpf.linked3.o test_cls 145 136 -9 (-6.21%) 10 9 -1 (-10.00%)
+test_l4lb.bpf.linked3.o balancer_ingress 4760 2612 -2148 (-45.13%) 113 102 -11 (-9.73%)
+test_l4lb_noinline.bpf.linked3.o balancer_ingress 4845 4877 +32 (+0.66%) 219 221 +2 (+0.91%)
+test_l4lb_noinline_dynptr.bpf.linked3.o balancer_ingress 2072 2087 +15 (+0.72%) 97 98 +1 (+1.03%)
+test_seg6_loop.bpf.linked3.o __add_egr_x 12440 9975 -2465 (-19.82%) 364 353 -11 (-3.02%)
+test_tcp_hdr_options.bpf.linked3.o estab 2558 2572 +14 (+0.55%) 179 180 +1 (+0.56%)
+test_xdp_dynptr.bpf.linked3.o _xdp_tx_iptunnel 645 596 -49 (-7.60%) 26 24 -2 (-7.69%)
+test_xdp_noinline.bpf.linked3.o balancer_ingress_v6 3520 3516 -4 (-0.11%) 216 216 +0 (+0.00%)
+xdp_synproxy_kern.bpf.linked3.o syncookie_tc 82661 81241 -1420 (-1.72%) 5073 5155 +82 (+1.62%)
+xdp_synproxy_kern.bpf.linked3.o syncookie_xdp 84964 82297 -2667 (-3.14%) 5130 5157 +27 (+0.53%)
+
+META-INTERNAL
+=============
+Program Insns (A) Insns (B) Insns (DIFF) States (A) States (B) States (DIFF)
+-------------------------------------- --------- --------- ----------------- ---------- ---------- ---------------
+balancer_ingress 27925 23608 -4317 (-15.46%) 1488 1482 -6 (-0.40%)
+balancer_ingress 31824 27546 -4278 (-13.44%) 1658 1652 -6 (-0.36%)
+balancer_ingress 32213 27935 -4278 (-13.28%) 1689 1683 -6 (-0.36%)
+balancer_ingress 32213 27935 -4278 (-13.28%) 1689 1683 -6 (-0.36%)
+balancer_ingress 31824 27546 -4278 (-13.44%) 1658 1652 -6 (-0.36%)
+balancer_ingress 38647 29562 -9085 (-23.51%) 2069 1835 -234 (-11.31%)
+balancer_ingress 38647 29562 -9085 (-23.51%) 2069 1835 -234 (-11.31%)
+balancer_ingress 40339 30792 -9547 (-23.67%) 2193 1934 -259 (-11.81%)
+balancer_ingress 37321 29055 -8266 (-22.15%) 1972 1795 -177 (-8.98%)
+balancer_ingress 38176 29753 -8423 (-22.06%) 2008 1831 -177 (-8.81%)
+balancer_ingress 29193 20910 -8283 (-28.37%) 1599 1422 -177 (-11.07%)
+balancer_ingress 30013 21452 -8561 (-28.52%) 1645 1447 -198 (-12.04%)
+balancer_ingress 28691 24290 -4401 (-15.34%) 1545 1531 -14 (-0.91%)
+balancer_ingress 34223 28965 -5258 (-15.36%) 1984 1875 -109 (-5.49%)
+balancer_ingress 35481 26158 -9323 (-26.28%) 2095 1806 -289 (-13.79%)
+balancer_ingress 35481 26158 -9323 (-26.28%) 2095 1806 -289 (-13.79%)
+balancer_ingress 35868 26455 -9413 (-26.24%) 2140 1827 -313 (-14.63%)
+balancer_ingress 35868 26455 -9413 (-26.24%) 2140 1827 -313 (-14.63%)
+balancer_ingress 35481 26158 -9323 (-26.28%) 2095 1806 -289 (-13.79%)
+balancer_ingress 35481 26158 -9323 (-26.28%) 2095 1806 -289 (-13.79%)
+balancer_ingress 34844 29485 -5359 (-15.38%) 2036 1918 -118 (-5.80%)
+fbflow_egress 3256 2652 -604 (-18.55%) 218 192 -26 (-11.93%)
+fbflow_ingress 1026 944 -82 (-7.99%) 70 63 -7 (-10.00%)
+sslwall_tc_egress 8424 7360 -1064 (-12.63%) 498 458 -40 (-8.03%)
+syar_accept_protect 15040 9539 -5501 (-36.58%) 364 220 -144 (-39.56%)
+syar_connect_tcp_v6 15036 9535 -5501 (-36.59%) 360 216 -144 (-40.00%)
+syar_connect_udp_v4 15039 9538 -5501 (-36.58%) 361 217 -144 (-39.89%)
+syar_connect_connect4_protect4 24805 15833 -8972 (-36.17%) 756 480 -276 (-36.51%)
+syar_lsm_file_open 167772 151813 -15959 (-9.51%) 1836 1667 -169 (-9.20%)
+syar_namespace_create_new 14805 9304 -5501 (-37.16%) 353 209 -144 (-40.79%)
+syar_python3_detect 17531 12030 -5501 (-31.38%) 391 247 -144 (-36.83%)
+syar_ssh_post_fork 16412 10911 -5501 (-33.52%) 405 261 -144 (-35.56%)
+syar_enter_execve 14728 9227 -5501 (-37.35%) 345 201 -144 (-41.74%)
+syar_enter_execveat 14728 9227 -5501 (-37.35%) 345 201 -144 (-41.74%)
+syar_exit_execve 16622 11121 -5501 (-33.09%) 376 232 -144 (-38.30%)
+syar_exit_execveat 16622 11121 -5501 (-33.09%) 376 232 -144 (-38.30%)
+syar_syscalls_kill 15288 9787 -5501 (-35.98%) 398 254 -144 (-36.18%)
+syar_task_enter_pivot_root 14898 9397 -5501 (-36.92%) 357 213 -144 (-40.34%)
+syar_syscalls_setreuid 16678 11177 -5501 (-32.98%) 429 285 -144 (-33.57%)
+syar_syscalls_setuid 16678 11177 -5501 (-32.98%) 429 285 -144 (-33.57%)
+syar_syscalls_process_vm_readv 14959 9458 -5501 (-36.77%) 364 220 -144 (-39.56%)
+syar_syscalls_process_vm_writev 15757 10256 -5501 (-34.91%) 390 246 -144 (-36.92%)
+do_uprobe 15519 10018 -5501 (-35.45%) 373 229 -144 (-38.61%)
+edgewall 179715 55783 -123932 (-68.96%) 12607 3999 -8608 (-68.28%)
+bictcp_state 7570 4131 -3439 (-45.43%) 496 269 -227 (-45.77%)
+cubictcp_state 7570 4131 -3439 (-45.43%) 496 269 -227 (-45.77%)
+tcp_rate_skb_delivered 447 272 -175 (-39.15%) 29 18 -11 (-37.93%)
+kprobe__bbr_set_state 4566 2615 -1951 (-42.73%) 209 124 -85 (-40.67%)
+kprobe__bictcp_state 4566 2615 -1951 (-42.73%) 209 124 -85 (-40.67%)
+inet_sock_set_state 1501 1337 -164 (-10.93%) 93 85 -8 (-8.60%)
+tcp_retransmit_skb 1145 981 -164 (-14.32%) 67 59 -8 (-11.94%)
+tcp_retransmit_synack 1183 951 -232 (-19.61%) 67 55 -12 (-17.91%)
+bpf_tcptuner 1459 1187 -272 (-18.64%) 99 80 -19 (-19.19%)
+tw_egress 801 776 -25 (-3.12%) 69 66 -3 (-4.35%)
+tw_ingress 795 770 -25 (-3.14%) 69 66 -3 (-4.35%)
+ttls_tc_ingress 19025 19383 +358 (+1.88%) 470 465 -5 (-1.06%)
+ttls_nat_egress 490 299 -191 (-38.98%) 33 20 -13 (-39.39%)
+ttls_nat_ingress 448 285 -163 (-36.38%) 32 21 -11 (-34.38%)
+tw_twfw_egress 511127 212071 -299056 (-58.51%) 16733 8504 -8229 (-49.18%)
+tw_twfw_ingress 500095 212069 -288026 (-57.59%) 16223 8504 -7719 (-47.58%)
+tw_twfw_tc_eg 511113 212064 -299049 (-58.51%) 16732 8504 -8228 (-49.18%)
+tw_twfw_tc_in 500095 212069 -288026 (-57.59%) 16223 8504 -7719 (-47.58%)
+tw_twfw_egress 12632 12435 -197 (-1.56%) 276 260 -16 (-5.80%)
+tw_twfw_ingress 12631 12454 -177 (-1.40%) 278 261 -17 (-6.12%)
+tw_twfw_tc_eg 12595 12435 -160 (-1.27%) 274 259 -15 (-5.47%)
+tw_twfw_tc_in 12631 12454 -177 (-1.40%) 278 261 -17 (-6.12%)
+tw_xdp_dump 266 209 -57 (-21.43%) 9 8 -1 (-11.11%)
+
+CILIUM
+=========
+File Program Insns (A) Insns (B) Insns (DIFF) States (A) States (B) States (DIFF)
+------------- -------------------------------- --------- --------- ---------------- ---------- ---------- --------------
+bpf_host.o cil_to_netdev 6047 4578 -1469 (-24.29%) 362 249 -113 (-31.22%)
+bpf_host.o handle_lxc_traffic 2227 1585 -642 (-28.83%) 156 103 -53 (-33.97%)
+bpf_host.o tail_handle_ipv4_from_netdev 2244 1458 -786 (-35.03%) 163 106 -57 (-34.97%)
+bpf_host.o tail_handle_nat_fwd_ipv4 21022 10479 -10543 (-50.15%) 1289 670 -619 (-48.02%)
+bpf_host.o tail_handle_nat_fwd_ipv6 15433 11375 -4058 (-26.29%) 905 643 -262 (-28.95%)
+bpf_host.o tail_ipv4_host_policy_ingress 2219 1367 -852 (-38.40%) 161 96 -65 (-40.37%)
+bpf_host.o tail_nodeport_nat_egress_ipv4 22460 19862 -2598 (-11.57%) 1469 1293 -176 (-11.98%)
+bpf_host.o tail_nodeport_nat_ingress_ipv4 5526 3534 -1992 (-36.05%) 366 243 -123 (-33.61%)
+bpf_host.o tail_nodeport_nat_ingress_ipv6 5132 4256 -876 (-17.07%) 241 219 -22 (-9.13%)
+bpf_host.o tail_nodeport_nat_ipv6_egress 3702 3542 -160 (-4.32%) 215 205 -10 (-4.65%)
+bpf_lxc.o tail_handle_nat_fwd_ipv4 21022 10479 -10543 (-50.15%) 1289 670 -619 (-48.02%)
+bpf_lxc.o tail_handle_nat_fwd_ipv6 15433 11375 -4058 (-26.29%) 905 643 -262 (-28.95%)
+bpf_lxc.o tail_ipv4_ct_egress 5073 3374 -1699 (-33.49%) 262 172 -90 (-34.35%)
+bpf_lxc.o tail_ipv4_ct_ingress 5093 3385 -1708 (-33.54%) 262 172 -90 (-34.35%)
+bpf_lxc.o tail_ipv4_ct_ingress_policy_only 5093 3385 -1708 (-33.54%) 262 172 -90 (-34.35%)
+bpf_lxc.o tail_ipv6_ct_egress 4593 3878 -715 (-15.57%) 194 151 -43 (-22.16%)
+bpf_lxc.o tail_ipv6_ct_ingress 4606 3891 -715 (-15.52%) 194 151 -43 (-22.16%)
+bpf_lxc.o tail_ipv6_ct_ingress_policy_only 4606 3891 -715 (-15.52%) 194 151 -43 (-22.16%)
+bpf_lxc.o tail_nodeport_nat_ingress_ipv4 5526 3534 -1992 (-36.05%) 366 243 -123 (-33.61%)
+bpf_lxc.o tail_nodeport_nat_ingress_ipv6 5132 4256 -876 (-17.07%) 241 219 -22 (-9.13%)
+bpf_overlay.o tail_handle_nat_fwd_ipv4 20524 10114 -10410 (-50.72%) 1271 638 -633 (-49.80%)
+bpf_overlay.o tail_nodeport_nat_egress_ipv4 22718 19490 -3228 (-14.21%) 1475 1275 -200 (-13.56%)
+bpf_overlay.o tail_nodeport_nat_ingress_ipv4 5526 3534 -1992 (-36.05%) 366 243 -123 (-33.61%)
+bpf_overlay.o tail_nodeport_nat_ingress_ipv6 5132 4256 -876 (-17.07%) 241 219 -22 (-9.13%)
+bpf_overlay.o tail_nodeport_nat_ipv6_egress 3638 3548 -90 (-2.47%) 209 203 -6 (-2.87%)
+bpf_overlay.o tail_rev_nodeport_lb4 4368 3820 -548 (-12.55%) 248 215 -33 (-13.31%)
+bpf_overlay.o tail_rev_nodeport_lb6 2867 2428 -439 (-15.31%) 167 140 -27 (-16.17%)
+bpf_sock.o cil_sock6_connect 1718 1703 -15 (-0.87%) 100 99 -1 (-1.00%)
+bpf_xdp.o tail_handle_nat_fwd_ipv4 12917 12443 -474 (-3.67%) 875 849 -26 (-2.97%)
+bpf_xdp.o tail_handle_nat_fwd_ipv6 13515 13264 -251 (-1.86%) 715 702 -13 (-1.82%)
+bpf_xdp.o tail_lb_ipv4 39492 36367 -3125 (-7.91%) 2430 2251 -179 (-7.37%)
+bpf_xdp.o tail_lb_ipv6 80441 78058 -2383 (-2.96%) 3647 3523 -124 (-3.40%)
+bpf_xdp.o tail_nodeport_ipv6_dsr 1038 901 -137 (-13.20%) 61 55 -6 (-9.84%)
+bpf_xdp.o tail_nodeport_nat_egress_ipv4 13027 12096 -931 (-7.15%) 868 809 -59 (-6.80%)
+bpf_xdp.o tail_nodeport_nat_ingress_ipv4 7617 5900 -1717 (-22.54%) 522 413 -109 (-20.88%)
+bpf_xdp.o tail_nodeport_nat_ingress_ipv6 7575 7395 -180 (-2.38%) 383 374 -9 (-2.35%)
+bpf_xdp.o tail_rev_nodeport_lb4 6808 6739 -69 (-1.01%) 403 396 -7 (-1.74%)
+bpf_xdp.o tail_rev_nodeport_lb6 16173 15847 -326 (-2.02%) 1010 990 -20 (-1.98%)
+
+Acked-by: Eduard Zingerman <eddyz87@gmail.com>
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/r/20231205184248.1502704-9-andrii@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/bpf/verifier.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
+index eaeb996ff56a2..705582bdda681 100644
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -4666,8 +4666,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
+ return err;
+
+ mark_stack_slot_scratched(env, spi);
+- if (reg && !(off % BPF_REG_SIZE) && register_is_bounded(reg) &&
+- !register_is_null(reg) && env->bpf_capable) {
++ if (reg && !(off % BPF_REG_SIZE) && register_is_bounded(reg) && env->bpf_capable) {
+ save_register_state(env, state, spi, reg, size);
+ /* Break the relation on a narrowing spill. */
+ if (fls64(reg->umax_value) > BITS_PER_BYTE * size)
+@@ -4716,7 +4715,12 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
+ /* when we zero initialize stack slots mark them as such */
+ if ((reg && register_is_null(reg)) ||
+ (!reg && is_bpf_st_mem(insn) && insn->imm == 0)) {
+- /* backtracking doesn't work for STACK_ZERO yet. */
++ /* STACK_ZERO case happened because register spill
++ * wasn't properly aligned at the stack slot boundary,
++ * so it's not a register spill anymore; force
++ * originating register to be precise to make
++ * STACK_ZERO correct for subsequent states
++ */
+ err = mark_chain_precision(env, value_regno);
+ if (err)
+ return err;
+--
+2.53.0
+
--- /dev/null
+From f8ef084bd4921c4549c3779c1f2a48996b81b561 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 18:23:40 +0200
+Subject: selftests/bpf: add stack access precision test
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit 876301881c436bf38e83a2c0d276a24b642e4aab ]
+
+Add a new selftests that validates precision tracking for stack access
+instruction, using both r10-based and non-r10-based accesses. For
+non-r10 ones we also make sure to have non-zero var_off to validate that
+final stack offset is tracked properly in instruction history
+information inside verifier.
+
+Acked-by: Eduard Zingerman <eddyz87@gmail.com>
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/r/20231205184248.1502704-3-andrii@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../bpf/progs/verifier_subprog_precision.c | 64 +++++++++++++++++--
+ 1 file changed, 59 insertions(+), 5 deletions(-)
+
+diff --git a/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c b/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c
+index 7c159b5618624..4b8b0f45d17d7 100644
+--- a/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c
++++ b/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c
+@@ -593,14 +593,68 @@ __naked int subprog_spill_into_parent_stack_slot_precise(void)
+ );
+ }
+
+-__naked __noinline __used
+-static __u64 subprog_with_checkpoint(void)
++SEC("?raw_tp")
++__success __log_level(2)
++__msg("17: (0f) r1 += r0")
++__msg("mark_precise: frame0: last_idx 17 first_idx 0 subseq_idx -1")
++__msg("mark_precise: frame0: regs=r0 stack= before 16: (bf) r1 = r7")
++__msg("mark_precise: frame0: regs=r0 stack= before 15: (27) r0 *= 4")
++__msg("mark_precise: frame0: regs=r0 stack= before 14: (79) r0 = *(u64 *)(r10 -16)")
++__msg("mark_precise: frame0: regs= stack=-16 before 13: (7b) *(u64 *)(r7 -8) = r0")
++__msg("mark_precise: frame0: regs=r0 stack= before 12: (79) r0 = *(u64 *)(r8 +16)")
++__msg("mark_precise: frame0: regs= stack=-16 before 11: (7b) *(u64 *)(r8 +16) = r0")
++__msg("mark_precise: frame0: regs=r0 stack= before 10: (79) r0 = *(u64 *)(r7 -8)")
++__msg("mark_precise: frame0: regs= stack=-16 before 9: (7b) *(u64 *)(r10 -16) = r0")
++__msg("mark_precise: frame0: regs=r0 stack= before 8: (07) r8 += -32")
++__msg("mark_precise: frame0: regs=r0 stack= before 7: (bf) r8 = r10")
++__msg("mark_precise: frame0: regs=r0 stack= before 6: (07) r7 += -8")
++__msg("mark_precise: frame0: regs=r0 stack= before 5: (bf) r7 = r10")
++__msg("mark_precise: frame0: regs=r0 stack= before 21: (95) exit")
++__msg("mark_precise: frame1: regs=r0 stack= before 20: (bf) r0 = r1")
++__msg("mark_precise: frame1: regs=r1 stack= before 4: (85) call pc+15")
++__msg("mark_precise: frame0: regs=r1 stack= before 3: (bf) r1 = r6")
++__msg("mark_precise: frame0: regs=r6 stack= before 2: (b7) r6 = 1")
++__naked int stack_slot_aliases_precision(void)
+ {
+ asm volatile (
+- "r0 = 0;"
+- /* guaranteed checkpoint if BPF_F_TEST_STATE_FREQ is used */
+- "goto +0;"
++ "r6 = 1;"
++ /* pass r6 through r1 into subprog to get it back as r0;
++ * this whole chain will have to be marked as precise later
++ */
++ "r1 = r6;"
++ "call identity_subprog;"
++ /* let's setup two registers that are aliased to r10 */
++ "r7 = r10;"
++ "r7 += -8;" /* r7 = r10 - 8 */
++ "r8 = r10;"
++ "r8 += -32;" /* r8 = r10 - 32 */
++ /* now spill subprog's return value (a r6 -> r1 -> r0 chain)
++ * a few times through different stack pointer regs, making
++ * sure to use r10, r7, and r8 both in LDX and STX insns, and
++ * *importantly* also using a combination of const var_off and
++ * insn->off to validate that we record final stack slot
++ * correctly, instead of relying on just insn->off derivation,
++ * which is only valid for r10-based stack offset
++ */
++ "*(u64 *)(r10 - 16) = r0;"
++ "r0 = *(u64 *)(r7 - 8);" /* r7 - 8 == r10 - 16 */
++ "*(u64 *)(r8 + 16) = r0;" /* r8 + 16 = r10 - 16 */
++ "r0 = *(u64 *)(r8 + 16);"
++ "*(u64 *)(r7 - 8) = r0;"
++ "r0 = *(u64 *)(r10 - 16);"
++ /* get ready to use r0 as an index into array to force precision */
++ "r0 *= 4;"
++ "r1 = %[vals];"
++ /* here r0->r1->r6 chain is forced to be precise and has to be
++ * propagated back to the beginning, including through the
++ * subprog call and all the stack spills and loads
++ */
++ "r1 += r0;"
++ "r0 = *(u32 *)(r1 + 0);"
+ "exit;"
++ :
++ : __imm_ptr(vals)
++ : __clobber_common, "r6"
+ );
+ }
+
+--
+2.53.0
+
--- /dev/null
+From bc3fd3bed9e246e5c430ac4fe08ac8ddfb4513bb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 18:25:19 +0200
+Subject: selftests/bpf: validate fake register spill/fill precision
+ backtracking logic
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit 7d8ed51bcb32716a40d71043fcd01c4118858c51 ]
+
+Add two tests validating that verifier's precision backtracking logic
+handles BPF_ST_MEM instructions that produce fake register spill into
+register slot. This is happening when non-zero constant is written
+directly to a slot, e.g., *(u64 *)(r10 -8) = 123.
+
+Add both full 64-bit register spill, as well as 32-bit "sub-spill".
+
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Acked-by: Eduard Zingerman <eddyz87@gmail.com>
+Link: https://lore.kernel.org/r/20231209010958.66758-2-andrii@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+[ Note: Adapted the expected log format for the selftests because it
+ changed later on in commits 67d43dfbb42d, 0c95c9fdb696, and
+ 1db747d75b1d. ]
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../selftests/bpf/progs/verifier_spill_fill.c | 154 ++++++++++++++++++
+ 1 file changed, 154 insertions(+)
+
+diff --git a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c
+index df4920da34728..1f71f596d33f8 100644
+--- a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c
++++ b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c
+@@ -577,4 +577,158 @@ __naked void partial_stack_load_preserves_zeros(void)
+ : __clobber_common);
+ }
+
++char two_byte_buf[2] SEC(".data.two_byte_buf");
++
++SEC("raw_tp")
++__log_level(2) __flag(BPF_F_TEST_STATE_FREQ)
++__success
++/* make sure fp-8 is IMPRECISE fake register spill */
++__msg("3: (7a) *(u64 *)(r10 -8) = 1 ; R10=fp0 fp-8_w=1")
++/* and fp-16 is spilled IMPRECISE const reg */
++__msg("5: (7b) *(u64 *)(r10 -16) = r0 ; R0_w=1 R10=fp0 fp-16_w=1")
++/* validate load from fp-8, which was initialized using BPF_ST_MEM */
++__msg("8: (79) r2 = *(u64 *)(r10 -8) ; R2_w=1 R10=fp0 fp-8=1")
++__msg("9: (0f) r1 += r2")
++__msg("mark_precise: frame0: last_idx 9 first_idx 7 subseq_idx -1")
++__msg("mark_precise: frame0: regs=r2 stack= before 8: (79) r2 = *(u64 *)(r10 -8)")
++__msg("mark_precise: frame0: regs= stack=-8 before 7: (bf) r1 = r6")
++/* note, fp-8 is precise, fp-16 is not yet precise, we'll get there */
++__msg("mark_precise: frame0: parent state regs= stack=-8: R0_w=1 R1=ctx(off=0,imm=0) R6_r=map_value(off=0,ks=4,vs=2,imm=0) R10=fp0 fp-8_rw=P1 fp-16_w=1")
++__msg("mark_precise: frame0: last_idx 6 first_idx 3 subseq_idx 7")
++__msg("mark_precise: frame0: regs= stack=-8 before 6: (05) goto pc+0")
++__msg("mark_precise: frame0: regs= stack=-8 before 5: (7b) *(u64 *)(r10 -16) = r0")
++__msg("mark_precise: frame0: regs= stack=-8 before 4: (b7) r0 = 1")
++__msg("mark_precise: frame0: regs= stack=-8 before 3: (7a) *(u64 *)(r10 -8) = 1")
++__msg("10: R1_w=map_value(off=1,ks=4,vs=2,imm=0) R2_w=1")
++/* validate load from fp-16, which was initialized using BPF_STX_MEM */
++__msg("12: (79) r2 = *(u64 *)(r10 -16) ; R2_w=1 R10=fp0 fp-16=1")
++__msg("13: (0f) r1 += r2")
++__msg("mark_precise: frame0: last_idx 13 first_idx 7 subseq_idx -1")
++__msg("mark_precise: frame0: regs=r2 stack= before 12: (79) r2 = *(u64 *)(r10 -16)")
++__msg("mark_precise: frame0: regs= stack=-16 before 11: (bf) r1 = r6")
++__msg("mark_precise: frame0: regs= stack=-16 before 10: (73) *(u8 *)(r1 +0) = r2")
++__msg("mark_precise: frame0: regs= stack=-16 before 9: (0f) r1 += r2")
++__msg("mark_precise: frame0: regs= stack=-16 before 8: (79) r2 = *(u64 *)(r10 -8)")
++__msg("mark_precise: frame0: regs= stack=-16 before 7: (bf) r1 = r6")
++/* now both fp-8 and fp-16 are precise, very good */
++__msg("mark_precise: frame0: parent state regs= stack=-16: R0_w=1 R1=ctx(off=0,imm=0) R6_r=map_value(off=0,ks=4,vs=2,imm=0) R10=fp0 fp-8_rw=P1 fp-16_rw=P1")
++__msg("mark_precise: frame0: last_idx 6 first_idx 3 subseq_idx 7")
++__msg("mark_precise: frame0: regs= stack=-16 before 6: (05) goto pc+0")
++__msg("mark_precise: frame0: regs= stack=-16 before 5: (7b) *(u64 *)(r10 -16) = r0")
++__msg("mark_precise: frame0: regs=r0 stack= before 4: (b7) r0 = 1")
++__msg("14: R1_w=map_value(off=1,ks=4,vs=2,imm=0) R2_w=1")
++__naked void stack_load_preserves_const_precision(void)
++{
++ asm volatile (
++ /* establish checkpoint with state that has no stack slots;
++ * if we bubble up to this state without finding desired stack
++ * slot, then it's a bug and should be caught
++ */
++ "goto +0;"
++
++ /* fp-8 is const 1 *fake* register */
++ ".8byte %[fp8_st_one];" /* LLVM-18+: *(u64 *)(r10 -8) = 1; */
++
++ /* fp-16 is const 1 register */
++ "r0 = 1;"
++ "*(u64 *)(r10 -16) = r0;"
++
++ /* force checkpoint to check precision marks preserved in parent states */
++ "goto +0;"
++
++ /* load single U64 from aligned FAKE_REG=1 slot */
++ "r1 = %[two_byte_buf];"
++ "r2 = *(u64 *)(r10 -8);"
++ "r1 += r2;"
++ "*(u8 *)(r1 + 0) = r2;" /* this should be fine */
++
++ /* load single U64 from aligned REG=1 slot */
++ "r1 = %[two_byte_buf];"
++ "r2 = *(u64 *)(r10 -16);"
++ "r1 += r2;"
++ "*(u8 *)(r1 + 0) = r2;" /* this should be fine */
++
++ "r0 = 0;"
++ "exit;"
++ :
++ : __imm_ptr(two_byte_buf),
++ __imm_insn(fp8_st_one, BPF_ST_MEM(BPF_DW, BPF_REG_FP, -8, 1))
++ : __clobber_common);
++}
++
++SEC("raw_tp")
++__log_level(2) __flag(BPF_F_TEST_STATE_FREQ)
++__success
++/* make sure fp-8 is 32-bit FAKE subregister spill */
++__msg("3: (62) *(u32 *)(r10 -8) = 1 ; R10=fp0 fp-8=1")
++/* but fp-16 is spilled IMPRECISE zero const reg */
++__msg("5: (63) *(u32 *)(r10 -16) = r0 ; R0_w=1 R10=fp0 fp-16=1")
++/* validate load from fp-8, which was initialized using BPF_ST_MEM */
++__msg("8: (61) r2 = *(u32 *)(r10 -8) ; R2_w=1 R10=fp0 fp-8=1")
++__msg("9: (0f) r1 += r2")
++__msg("mark_precise: frame0: last_idx 9 first_idx 7 subseq_idx -1")
++__msg("mark_precise: frame0: regs=r2 stack= before 8: (61) r2 = *(u32 *)(r10 -8)")
++__msg("mark_precise: frame0: regs= stack=-8 before 7: (bf) r1 = r6")
++__msg("mark_precise: frame0: parent state regs= stack=-8: R0_w=1 R1=ctx(off=0,imm=0) R6_r=map_value(off=0,ks=4,vs=2,imm=0) R10=fp0 fp-8_r=P1 fp-16=1")
++__msg("mark_precise: frame0: last_idx 6 first_idx 3 subseq_idx 7")
++__msg("mark_precise: frame0: regs= stack=-8 before 6: (05) goto pc+0")
++__msg("mark_precise: frame0: regs= stack=-8 before 5: (63) *(u32 *)(r10 -16) = r0")
++__msg("mark_precise: frame0: regs= stack=-8 before 4: (b7) r0 = 1")
++__msg("mark_precise: frame0: regs= stack=-8 before 3: (62) *(u32 *)(r10 -8) = 1")
++__msg("10: R1_w=map_value(off=1,ks=4,vs=2,imm=0) R2_w=1")
++/* validate load from fp-16, which was initialized using BPF_STX_MEM */
++__msg("12: (61) r2 = *(u32 *)(r10 -16) ; R2_w=1 R10=fp0 fp-16=1")
++__msg("13: (0f) r1 += r2")
++__msg("mark_precise: frame0: last_idx 13 first_idx 7 subseq_idx -1")
++__msg("mark_precise: frame0: regs=r2 stack= before 12: (61) r2 = *(u32 *)(r10 -16)")
++__msg("mark_precise: frame0: regs= stack=-16 before 11: (bf) r1 = r6")
++__msg("mark_precise: frame0: regs= stack=-16 before 10: (73) *(u8 *)(r1 +0) = r2")
++__msg("mark_precise: frame0: regs= stack=-16 before 9: (0f) r1 += r2")
++__msg("mark_precise: frame0: regs= stack=-16 before 8: (61) r2 = *(u32 *)(r10 -8)")
++__msg("mark_precise: frame0: regs= stack=-16 before 7: (bf) r1 = r6")
++__msg("mark_precise: frame0: parent state regs= stack=-16: R0_w=1 R1=ctx(off=0,imm=0) R6_r=map_value(off=0,ks=4,vs=2,imm=0) R10=fp0 fp-8_r=P1 fp-16_r=P1")
++__msg("mark_precise: frame0: last_idx 6 first_idx 3 subseq_idx 7")
++__msg("mark_precise: frame0: regs= stack=-16 before 6: (05) goto pc+0")
++__msg("mark_precise: frame0: regs= stack=-16 before 5: (63) *(u32 *)(r10 -16) = r0")
++__msg("mark_precise: frame0: regs=r0 stack= before 4: (b7) r0 = 1")
++__msg("14: R1_w=map_value(off=1,ks=4,vs=2,imm=0) R2_w=1")
++__naked void stack_load_preserves_const_precision_subreg(void)
++{
++ asm volatile (
++ /* establish checkpoint with state that has no stack slots;
++ * if we bubble up to this state without finding desired stack
++ * slot, then it's a bug and should be caught
++ */
++ "goto +0;"
++
++ /* fp-8 is const 1 *fake* SUB-register */
++ ".8byte %[fp8_st_one];" /* LLVM-18+: *(u32 *)(r10 -8) = 1; */
++
++ /* fp-16 is const 1 SUB-register */
++ "r0 = 1;"
++ "*(u32 *)(r10 -16) = r0;"
++
++ /* force checkpoint to check precision marks preserved in parent states */
++ "goto +0;"
++
++ /* load single U32 from aligned FAKE_REG=1 slot */
++ "r1 = %[two_byte_buf];"
++ "r2 = *(u32 *)(r10 -8);"
++ "r1 += r2;"
++ "*(u8 *)(r1 + 0) = r2;" /* this should be fine */
++
++ /* load single U32 from aligned REG=1 slot */
++ "r1 = %[two_byte_buf];"
++ "r2 = *(u32 *)(r10 -16);"
++ "r1 += r2;"
++ "*(u8 *)(r1 + 0) = r2;" /* this should be fine */
++
++ "r0 = 0;"
++ "exit;"
++ :
++ : __imm_ptr(two_byte_buf),
++ __imm_insn(fp8_st_one, BPF_ST_MEM(BPF_W, BPF_REG_FP, -8, 1)) /* 32-bit spill */
++ : __clobber_common);
++}
++
+ char _license[] SEC("license") = "GPL";
+--
+2.53.0
+
--- /dev/null
+From a07d00a3e82e2be5e8b77eab636e12841807c8a1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 18:24:52 +0200
+Subject: selftests/bpf: validate precision logic in
+ partial_stack_load_preserves_zeros
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit 064e0bea19b356c5d5f48a4549d80a3c03ce898b ]
+
+Enhance partial_stack_load_preserves_zeros subtest with detailed
+precision propagation log checks. We know expect fp-16 to be spilled,
+initially imprecise, zero const register, which is later marked as
+precise even when partial stack slot load is performed, even if it's not
+a register fill (!).
+
+Acked-by: Eduard Zingerman <eddyz87@gmail.com>
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/r/20231205184248.1502704-10-andrii@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../selftests/bpf/progs/verifier_spill_fill.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+diff --git a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c
+index 41fd61299eab0..df4920da34728 100644
+--- a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c
++++ b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c
+@@ -495,6 +495,22 @@ char single_byte_buf[1] SEC(".data.single_byte_buf");
+ SEC("raw_tp")
+ __log_level(2)
+ __success
++/* make sure fp-8 is all STACK_ZERO */
++__msg("2: (7a) *(u64 *)(r10 -8) = 0 ; R10=fp0 fp-8_w=00000000")
++/* but fp-16 is spilled IMPRECISE zero const reg */
++__msg("4: (7b) *(u64 *)(r10 -16) = r0 ; R0_w=0 R10=fp0 fp-16_w=0")
++/* and now check that precision propagation works even for such tricky case */
++__msg("10: (71) r2 = *(u8 *)(r10 -9) ; R2_w=P0 R10=fp0 fp-16_w=0")
++__msg("11: (0f) r1 += r2")
++__msg("mark_precise: frame0: last_idx 11 first_idx 0 subseq_idx -1")
++__msg("mark_precise: frame0: regs=r2 stack= before 10: (71) r2 = *(u8 *)(r10 -9)")
++__msg("mark_precise: frame0: regs= stack=-16 before 9: (bf) r1 = r6")
++__msg("mark_precise: frame0: regs= stack=-16 before 8: (73) *(u8 *)(r1 +0) = r2")
++__msg("mark_precise: frame0: regs= stack=-16 before 7: (0f) r1 += r2")
++__msg("mark_precise: frame0: regs= stack=-16 before 6: (71) r2 = *(u8 *)(r10 -1)")
++__msg("mark_precise: frame0: regs= stack=-16 before 5: (bf) r1 = r6")
++__msg("mark_precise: frame0: regs= stack=-16 before 4: (7b) *(u64 *)(r10 -16) = r0")
++__msg("mark_precise: frame0: regs=r0 stack= before 3: (b7) r0 = 0")
+ __naked void partial_stack_load_preserves_zeros(void)
+ {
+ asm volatile (
+--
+2.53.0
+
--- /dev/null
+From 1ab77c490ea0f174976630723c5a92e7e66dd4e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 18:24:01 +0200
+Subject: selftests/bpf: validate STACK_ZERO is preserved on subreg spill
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit b33ceb6a3d2ee07fdd836373383a6d4783581324 ]
+
+Add tests validating that STACK_ZERO slots are preserved when slot is
+partially overwritten with subregister spill.
+
+Acked-by: Eduard Zingerman <eddyz87@gmail.com>
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/r/20231205184248.1502704-6-andrii@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../selftests/bpf/progs/verifier_spill_fill.c | 40 +++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+
+diff --git a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c
+index 6115520154e33..d9dabae811767 100644
+--- a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c
++++ b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c
+@@ -4,6 +4,7 @@
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
+ #include "bpf_misc.h"
++#include <../../../tools/include/linux/filter.h>
+
+ struct {
+ __uint(type, BPF_MAP_TYPE_RINGBUF);
+@@ -450,4 +451,43 @@ l0_%=: r1 >>= 16; \
+ : __clobber_all);
+ }
+
++SEC("raw_tp")
++__log_level(2)
++__success
++__msg("fp-8=0m??mmmm")
++__msg("fp-16=00mm??mm")
++__msg("fp-24=00mm???m")
++__naked void spill_subregs_preserve_stack_zero(void)
++{
++ asm volatile (
++ "call %[bpf_get_prandom_u32];"
++
++ /* 32-bit subreg spill with ZERO, MISC, and INVALID */
++ ".8byte %[fp1_u8_st_zero];" /* ZERO, LLVM-18+: *(u8 *)(r10 -1) = 0; */
++ "*(u8 *)(r10 -2) = r0;" /* MISC */
++ /* fp-3 and fp-4 stay INVALID */
++ "*(u32 *)(r10 -8) = r0;"
++
++ /* 16-bit subreg spill with ZERO, MISC, and INVALID */
++ ".8byte %[fp10_u16_st_zero];" /* ZERO, LLVM-18+: *(u16 *)(r10 -10) = 0; */
++ "*(u16 *)(r10 -12) = r0;" /* MISC */
++ /* fp-13 and fp-14 stay INVALID */
++ "*(u16 *)(r10 -16) = r0;"
++
++ /* 8-bit subreg spill with ZERO, MISC, and INVALID */
++ ".8byte %[fp18_u16_st_zero];" /* ZERO, LLVM-18+: *(u16 *)(r18 -10) = 0; */
++ "*(u16 *)(r10 -20) = r0;" /* MISC */
++ /* fp-21, fp-22, and fp-23 stay INVALID */
++ "*(u8 *)(r10 -24) = r0;"
++
++ "r0 = 0;"
++ "exit;"
++ :
++ : __imm(bpf_get_prandom_u32),
++ __imm_insn(fp1_u8_st_zero, BPF_ST_MEM(BPF_B, BPF_REG_FP, -1, 0)),
++ __imm_insn(fp10_u16_st_zero, BPF_ST_MEM(BPF_H, BPF_REG_FP, -10, 0)),
++ __imm_insn(fp18_u16_st_zero, BPF_ST_MEM(BPF_H, BPF_REG_FP, -18, 0))
++ : __clobber_all);
++}
++
+ char _license[] SEC("license") = "GPL";
+--
+2.53.0
+
--- /dev/null
+From 5c47b7e91955a7e3120c702e447c54d19242e684 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 18:24:27 +0200
+Subject: selftests/bpf: validate zero preservation for sub-slot loads
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit add1cd7f22e61756987865ada9fe95cd86569025 ]
+
+Validate that 1-, 2-, and 4-byte loads from stack slots not aligned on
+8-byte boundary still preserve zero, when loading from all-STACK_ZERO
+sub-slots, or when stack sub-slots are covered by spilled register with
+known constant zero value.
+
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/r/20231205184248.1502704-8-andrii@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../selftests/bpf/progs/verifier_spill_fill.c | 71 +++++++++++++++++++
+ 1 file changed, 71 insertions(+)
+
+diff --git a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c
+index d9dabae811767..41fd61299eab0 100644
+--- a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c
++++ b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c
+@@ -490,4 +490,75 @@ __naked void spill_subregs_preserve_stack_zero(void)
+ : __clobber_all);
+ }
+
++char single_byte_buf[1] SEC(".data.single_byte_buf");
++
++SEC("raw_tp")
++__log_level(2)
++__success
++__naked void partial_stack_load_preserves_zeros(void)
++{
++ asm volatile (
++ /* fp-8 is all STACK_ZERO */
++ ".8byte %[fp8_st_zero];" /* LLVM-18+: *(u64 *)(r10 -8) = 0; */
++
++ /* fp-16 is const zero register */
++ "r0 = 0;"
++ "*(u64 *)(r10 -16) = r0;"
++
++ /* load single U8 from non-aligned STACK_ZERO slot */
++ "r1 = %[single_byte_buf];"
++ "r2 = *(u8 *)(r10 -1);"
++ "r1 += r2;"
++ "*(u8 *)(r1 + 0) = r2;" /* this should be fine */
++
++ /* load single U8 from non-aligned ZERO REG slot */
++ "r1 = %[single_byte_buf];"
++ "r2 = *(u8 *)(r10 -9);"
++ "r1 += r2;"
++ "*(u8 *)(r1 + 0) = r2;" /* this should be fine */
++
++ /* load single U16 from non-aligned STACK_ZERO slot */
++ "r1 = %[single_byte_buf];"
++ "r2 = *(u16 *)(r10 -2);"
++ "r1 += r2;"
++ "*(u8 *)(r1 + 0) = r2;" /* this should be fine */
++
++ /* load single U16 from non-aligned ZERO REG slot */
++ "r1 = %[single_byte_buf];"
++ "r2 = *(u16 *)(r10 -10);"
++ "r1 += r2;"
++ "*(u8 *)(r1 + 0) = r2;" /* this should be fine */
++
++ /* load single U32 from non-aligned STACK_ZERO slot */
++ "r1 = %[single_byte_buf];"
++ "r2 = *(u32 *)(r10 -4);"
++ "r1 += r2;"
++ "*(u8 *)(r1 + 0) = r2;" /* this should be fine */
++
++ /* load single U32 from non-aligned ZERO REG slot */
++ "r1 = %[single_byte_buf];"
++ "r2 = *(u32 *)(r10 -12);"
++ "r1 += r2;"
++ "*(u8 *)(r1 + 0) = r2;" /* this should be fine */
++
++ /* for completeness, load U64 from STACK_ZERO slot */
++ "r1 = %[single_byte_buf];"
++ "r2 = *(u64 *)(r10 -8);"
++ "r1 += r2;"
++ "*(u8 *)(r1 + 0) = r2;" /* this should be fine */
++
++ /* for completeness, load U64 from ZERO REG slot */
++ "r1 = %[single_byte_buf];"
++ "r2 = *(u64 *)(r10 -16);"
++ "r1 += r2;"
++ "*(u8 *)(r1 + 0) = r2;" /* this should be fine */
++
++ "r0 = 0;"
++ "exit;"
++ :
++ : __imm_ptr(single_byte_buf),
++ __imm_insn(fp8_st_zero, BPF_ST_MEM(BPF_DW, BPF_REG_FP, -8, 0))
++ : __clobber_common);
++}
++
+ char _license[] SEC("license") = "GPL";
+--
+2.53.0
+
dmaengine-idxd-fix-leaking-event-log-memory.patch
kvm-svm-check-validity-of-vmcb-controls-when-returning-from-smm.patch
net-sched-sch_red-replace-direct-dequeue-call-with-peek-and-qdisc_dequeue_peeked.patch
+bpf-support-non-r10-register-spill-fill-to-from-stac.patch
+selftests-bpf-add-stack-access-precision-test.patch
+bpf-preserve-stack_zero-slots-on-partial-reg-spills.patch
+selftests-bpf-validate-stack_zero-is-preserved-on-su.patch
+bpf-preserve-constant-zero-when-doing-partial-regist.patch
+selftests-bpf-validate-zero-preservation-for-sub-slo.patch
+bpf-track-aligned-stack_zero-cases-as-imprecise-spil.patch
+selftests-bpf-validate-precision-logic-in-partial_st.patch
+bpf-handle-fake-register-spill-to-stack-with-bpf_st_.patch
+selftests-bpf-validate-fake-register-spill-fill-prec.patch
+bpf-don-t-mark-stack_invalid-as-stack_misc-in-mark_s.patch