]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees
authorSasha Levin <sashal@kernel.org>
Tue, 12 May 2026 00:17:17 +0000 (20:17 -0400)
committerSasha Levin <sashal@kernel.org>
Tue, 12 May 2026 00:17:17 +0000 (20:17 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
25 files changed:
queue-5.10/ipmi-ssif-clean-up-kthread-on-errors.patch [new file with mode: 0644]
queue-5.10/ipmi-ssif-fix-a-shutdown-race.patch [new file with mode: 0644]
queue-5.10/ipmi-ssif-null-thread-on-error.patch [new file with mode: 0644]
queue-5.10/ipmi-ssif-remove-unnecessary-indention.patch [new file with mode: 0644]
queue-5.10/series
queue-5.15/ipmi-ssif-clean-up-kthread-on-errors.patch [new file with mode: 0644]
queue-5.15/ipmi-ssif-fix-a-shutdown-race.patch [new file with mode: 0644]
queue-5.15/ipmi-ssif-null-thread-on-error.patch [new file with mode: 0644]
queue-5.15/ipmi-ssif-remove-unnecessary-indention.patch [new file with mode: 0644]
queue-5.15/series
queue-6.12/rxrpc-also-unshare-data-response-packets-when-paged-.patch [new file with mode: 0644]
queue-6.12/rxrpc-fix-conn-level-packet-handling-to-unshare-resp.patch [new file with mode: 0644]
queue-6.12/series
queue-6.6/bpf-don-t-mark-stack_invalid-as-stack_misc-in-mark_s.patch [new file with mode: 0644]
queue-6.6/bpf-handle-fake-register-spill-to-stack-with-bpf_st_.patch [new file with mode: 0644]
queue-6.6/bpf-preserve-constant-zero-when-doing-partial-regist.patch [new file with mode: 0644]
queue-6.6/bpf-preserve-stack_zero-slots-on-partial-reg-spills.patch [new file with mode: 0644]
queue-6.6/bpf-support-non-r10-register-spill-fill-to-from-stac.patch [new file with mode: 0644]
queue-6.6/bpf-track-aligned-stack_zero-cases-as-imprecise-spil.patch [new file with mode: 0644]
queue-6.6/selftests-bpf-add-stack-access-precision-test.patch [new file with mode: 0644]
queue-6.6/selftests-bpf-validate-fake-register-spill-fill-prec.patch [new file with mode: 0644]
queue-6.6/selftests-bpf-validate-precision-logic-in-partial_st.patch [new file with mode: 0644]
queue-6.6/selftests-bpf-validate-stack_zero-is-preserved-on-su.patch [new file with mode: 0644]
queue-6.6/selftests-bpf-validate-zero-preservation-for-sub-slo.patch [new file with mode: 0644]
queue-6.6/series

diff --git a/queue-5.10/ipmi-ssif-clean-up-kthread-on-errors.patch b/queue-5.10/ipmi-ssif-clean-up-kthread-on-errors.patch
new file mode 100644 (file)
index 0000000..c3291d3
--- /dev/null
@@ -0,0 +1,60 @@
+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
+
diff --git a/queue-5.10/ipmi-ssif-fix-a-shutdown-race.patch b/queue-5.10/ipmi-ssif-fix-a-shutdown-race.patch
new file mode 100644 (file)
index 0000000..f418bfa
--- /dev/null
@@ -0,0 +1,52 @@
+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
+
diff --git a/queue-5.10/ipmi-ssif-null-thread-on-error.patch b/queue-5.10/ipmi-ssif-null-thread-on-error.patch
new file mode 100644 (file)
index 0000000..d249e17
--- /dev/null
@@ -0,0 +1,39 @@
+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
+
diff --git a/queue-5.10/ipmi-ssif-remove-unnecessary-indention.patch b/queue-5.10/ipmi-ssif-remove-unnecessary-indention.patch
new file mode 100644 (file)
index 0000000..b4240cc
--- /dev/null
@@ -0,0 +1,67 @@
+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
+
index d9dd79a1748a83bb6159ba788feffe7c29fa5242..228435a6c845cf73925b3b115f728c43d541eeaf 100644 (file)
@@ -217,3 +217,7 @@ x86-cpu-amd-call-the-spectral-chicken-in-the-zen2-init-function.patch
 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
diff --git a/queue-5.15/ipmi-ssif-clean-up-kthread-on-errors.patch b/queue-5.15/ipmi-ssif-clean-up-kthread-on-errors.patch
new file mode 100644 (file)
index 0000000..000a997
--- /dev/null
@@ -0,0 +1,60 @@
+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
+
diff --git a/queue-5.15/ipmi-ssif-fix-a-shutdown-race.patch b/queue-5.15/ipmi-ssif-fix-a-shutdown-race.patch
new file mode 100644 (file)
index 0000000..bb91f17
--- /dev/null
@@ -0,0 +1,52 @@
+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
+
diff --git a/queue-5.15/ipmi-ssif-null-thread-on-error.patch b/queue-5.15/ipmi-ssif-null-thread-on-error.patch
new file mode 100644 (file)
index 0000000..25db8f4
--- /dev/null
@@ -0,0 +1,39 @@
+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
+
diff --git a/queue-5.15/ipmi-ssif-remove-unnecessary-indention.patch b/queue-5.15/ipmi-ssif-remove-unnecessary-indention.patch
new file mode 100644 (file)
index 0000000..600a902
--- /dev/null
@@ -0,0 +1,67 @@
+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
+
index cb5e1d54d52191061723214e1a4c3aa5d35db7f3..cd633700826214a1436fb330977b1f1057d1b314 100644 (file)
@@ -290,3 +290,7 @@ spi-meson-spicc-fix-double-put-in-remove-path.patch
 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
diff --git a/queue-6.12/rxrpc-also-unshare-data-response-packets-when-paged-.patch b/queue-6.12/rxrpc-also-unshare-data-response-packets-when-paged-.patch
new file mode 100644 (file)
index 0000000..8a72910
--- /dev/null
@@ -0,0 +1,70 @@
+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
+
diff --git a/queue-6.12/rxrpc-fix-conn-level-packet-handling-to-unshare-resp.patch b/queue-6.12/rxrpc-fix-conn-level-packet-handling-to-unshare-resp.patch
new file mode 100644 (file)
index 0000000..695027e
--- /dev/null
@@ -0,0 +1,99 @@
+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
+
index 030ee03b9e23aa34a6be5ab7ef421a336cdf6897..d5a34ebe31d5292547c4c702d2f745c84fd19556 100644 (file)
@@ -23,3 +23,5 @@ net-af_key-zero-aligned-sockaddr-tail-in-pf_key-expo.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
 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
diff --git a/queue-6.6/bpf-don-t-mark-stack_invalid-as-stack_misc-in-mark_s.patch b/queue-6.6/bpf-don-t-mark-stack_invalid-as-stack_misc-in-mark_s.patch
new file mode 100644 (file)
index 0000000..d38edec
--- /dev/null
@@ -0,0 +1,59 @@
+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
+
diff --git a/queue-6.6/bpf-handle-fake-register-spill-to-stack-with-bpf_st_.patch b/queue-6.6/bpf-handle-fake-register-spill-to-stack-with-bpf_st_.patch
new file mode 100644 (file)
index 0000000..c2d7a8c
--- /dev/null
@@ -0,0 +1,45 @@
+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
+
diff --git a/queue-6.6/bpf-preserve-constant-zero-when-doing-partial-regist.patch b/queue-6.6/bpf-preserve-constant-zero-when-doing-partial-regist.patch
new file mode 100644 (file)
index 0000000..1103c65
--- /dev/null
@@ -0,0 +1,78 @@
+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
+
diff --git a/queue-6.6/bpf-preserve-stack_zero-slots-on-partial-reg-spills.patch b/queue-6.6/bpf-preserve-stack_zero-slots-on-partial-reg-spills.patch
new file mode 100644 (file)
index 0000000..163bb84
--- /dev/null
@@ -0,0 +1,118 @@
+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
+
diff --git a/queue-6.6/bpf-support-non-r10-register-spill-fill-to-from-stac.patch b/queue-6.6/bpf-support-non-r10-register-spill-fill-to-from-stac.patch
new file mode 100644 (file)
index 0000000..74a50a0
--- /dev/null
@@ -0,0 +1,619 @@
+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 = &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
+
diff --git a/queue-6.6/bpf-track-aligned-stack_zero-cases-as-imprecise-spil.patch b/queue-6.6/bpf-track-aligned-stack_zero-cases-as-imprecise-spil.patch
new file mode 100644 (file)
index 0000000..cdf191d
--- /dev/null
@@ -0,0 +1,254 @@
+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
+
diff --git a/queue-6.6/selftests-bpf-add-stack-access-precision-test.patch b/queue-6.6/selftests-bpf-add-stack-access-precision-test.patch
new file mode 100644 (file)
index 0000000..1dbc825
--- /dev/null
@@ -0,0 +1,108 @@
+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
+
diff --git a/queue-6.6/selftests-bpf-validate-fake-register-spill-fill-prec.patch b/queue-6.6/selftests-bpf-validate-fake-register-spill-fill-prec.patch
new file mode 100644 (file)
index 0000000..9e15e69
--- /dev/null
@@ -0,0 +1,198 @@
+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
+
diff --git a/queue-6.6/selftests-bpf-validate-precision-logic-in-partial_st.patch b/queue-6.6/selftests-bpf-validate-precision-logic-in-partial_st.patch
new file mode 100644 (file)
index 0000000..6fa62a7
--- /dev/null
@@ -0,0 +1,58 @@
+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
+
diff --git a/queue-6.6/selftests-bpf-validate-stack_zero-is-preserved-on-su.patch b/queue-6.6/selftests-bpf-validate-stack_zero-is-preserved-on-su.patch
new file mode 100644 (file)
index 0000000..6f49ed1
--- /dev/null
@@ -0,0 +1,83 @@
+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
+
diff --git a/queue-6.6/selftests-bpf-validate-zero-preservation-for-sub-slo.patch b/queue-6.6/selftests-bpf-validate-zero-preservation-for-sub-slo.patch
new file mode 100644 (file)
index 0000000..948679b
--- /dev/null
@@ -0,0 +1,108 @@
+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
+
index 5cdd8f92e773c9eac695b2a8b23744162e7227fb..15327029978bcf8ad0f434318d4c37cf12888320 100644 (file)
@@ -169,3 +169,14 @@ dmaengine-idxd-fix-crash-when-the-event-log-is-disab.patch
 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