]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.12-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 17 Oct 2025 07:18:31 +0000 (09:18 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 17 Oct 2025 07:18:31 +0000 (09:18 +0200)
added patches:
acpi-battery-add-synchronization-between-interface-updates.patch
acpi-battery-allocate-driver-data-through-devm_-apis.patch
acpi-battery-check-for-error-code-from-devm_mutex_init-call.patch
acpi-battery-initialize-mutexes-through-devm_-apis.patch
acpi-property-add-code-comments-explaining-what-is-going-on.patch
acpi-property-disregard-references-in-data-only-subnode-lists.patch
acpi-property-do-not-pass-null-handles-to-acpi_attach_data.patch
ipmi-fix-handling-of-messages-with-provided-receive-message-pointer.patch
ipmi-rework-user-message-limit-handling.patch
mptcp-pm-in-kernel-usable-client-side-with-c-flag.patch
nfsd-don-t-use-sv_nrthreads-in-connection-limiting-calculations.patch
nfsd-fix-nfsd_may_bypass_gss-and-nfsd_may_bypass_gss_on_root.patch
nfsd-refine-and-rename-nfsd_may_lock.patch
nfsd-replace-use-of-nfsd_may_lock-in-nfsd4_lock.patch
nfsd-unregister-with-rpcbind-when-deleting-a-transport.patch

16 files changed:
queue-6.12/acpi-battery-add-synchronization-between-interface-updates.patch [new file with mode: 0644]
queue-6.12/acpi-battery-allocate-driver-data-through-devm_-apis.patch [new file with mode: 0644]
queue-6.12/acpi-battery-check-for-error-code-from-devm_mutex_init-call.patch [new file with mode: 0644]
queue-6.12/acpi-battery-initialize-mutexes-through-devm_-apis.patch [new file with mode: 0644]
queue-6.12/acpi-property-add-code-comments-explaining-what-is-going-on.patch [new file with mode: 0644]
queue-6.12/acpi-property-disregard-references-in-data-only-subnode-lists.patch [new file with mode: 0644]
queue-6.12/acpi-property-do-not-pass-null-handles-to-acpi_attach_data.patch [new file with mode: 0644]
queue-6.12/ipmi-fix-handling-of-messages-with-provided-receive-message-pointer.patch [new file with mode: 0644]
queue-6.12/ipmi-rework-user-message-limit-handling.patch [new file with mode: 0644]
queue-6.12/mptcp-pm-in-kernel-usable-client-side-with-c-flag.patch [new file with mode: 0644]
queue-6.12/nfsd-don-t-use-sv_nrthreads-in-connection-limiting-calculations.patch [new file with mode: 0644]
queue-6.12/nfsd-fix-nfsd_may_bypass_gss-and-nfsd_may_bypass_gss_on_root.patch [new file with mode: 0644]
queue-6.12/nfsd-refine-and-rename-nfsd_may_lock.patch [new file with mode: 0644]
queue-6.12/nfsd-replace-use-of-nfsd_may_lock-in-nfsd4_lock.patch [new file with mode: 0644]
queue-6.12/nfsd-unregister-with-rpcbind-when-deleting-a-transport.patch [new file with mode: 0644]
queue-6.12/series

diff --git a/queue-6.12/acpi-battery-add-synchronization-between-interface-updates.patch b/queue-6.12/acpi-battery-add-synchronization-between-interface-updates.patch
new file mode 100644 (file)
index 0000000..24b3405
--- /dev/null
@@ -0,0 +1,217 @@
+From stable+bounces-186154-greg=kroah.com@vger.kernel.org Thu Oct 16 15:34:54 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Oct 2025 09:34:38 -0400
+Subject: ACPI: battery: Add synchronization between interface updates
+To: stable@vger.kernel.org
+Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, GuangFei Luo <luogf2025@163.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251016133438.3296275-4-sashal@kernel.org>
+
+From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
+
+[ Upstream commit 399dbcadc01ebf0035f325eaa8c264f8b5cd0a14 ]
+
+There is no synchronization between different code paths in the ACPI
+battery driver that update its sysfs interface or its power supply
+class device interface.  In some cases this results to functional
+failures due to race conditions.
+
+One example of this is when two ACPI notifications:
+
+  - ACPI_BATTERY_NOTIFY_STATUS (0x80)
+  - ACPI_BATTERY_NOTIFY_INFO   (0x81)
+
+are triggered (by the platform firmware) in a row with a little delay
+in between after removing and reinserting a laptop battery.  Both
+notifications cause acpi_battery_update() to be called and if the delay
+between them is sufficiently small, sysfs_add_battery() can be re-entered
+before battery->bat is set which leads to a duplicate sysfs entry error:
+
+ sysfs: cannot create duplicate filename '/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0A:00/power_supply/BAT1'
+ CPU: 1 UID: 0 PID: 185 Comm: kworker/1:4 Kdump: loaded Not tainted 6.12.38+deb13-amd64 #1  Debian 6.12.38-1
+ Hardware name: Gateway          NV44             /SJV40-MV        , BIOS V1.3121 04/08/2009
+ Workqueue: kacpi_notify acpi_os_execute_deferred
+ Call Trace:
+  <TASK>
+  dump_stack_lvl+0x5d/0x80
+  sysfs_warn_dup.cold+0x17/0x23
+  sysfs_create_dir_ns+0xce/0xe0
+  kobject_add_internal+0xba/0x250
+  kobject_add+0x96/0xc0
+  ? get_device_parent+0xde/0x1e0
+  device_add+0xe2/0x870
+  __power_supply_register.part.0+0x20f/0x3f0
+  ? wake_up_q+0x4e/0x90
+  sysfs_add_battery+0xa4/0x1d0 [battery]
+  acpi_battery_update+0x19e/0x290 [battery]
+  acpi_battery_notify+0x50/0x120 [battery]
+  acpi_ev_notify_dispatch+0x49/0x70
+  acpi_os_execute_deferred+0x1a/0x30
+  process_one_work+0x177/0x330
+  worker_thread+0x251/0x390
+  ? __pfx_worker_thread+0x10/0x10
+  kthread+0xd2/0x100
+  ? __pfx_kthread+0x10/0x10
+  ret_from_fork+0x34/0x50
+  ? __pfx_kthread+0x10/0x10
+  ret_from_fork_asm+0x1a/0x30
+  </TASK>
+ kobject: kobject_add_internal failed for BAT1 with -EEXIST, don't try to register things with the same name in the same directory.
+
+There are also other scenarios in which analogous issues may occur.
+
+Address this by using a common lock in all of the code paths leading
+to updates of driver interfaces: ACPI Notify () handler, system resume
+callback and post-resume notification, device addition and removal.
+
+This new lock replaces sysfs_lock that has been used only in
+sysfs_remove_battery() which now is going to be always called under
+the new lock, so it doesn't need any internal locking any more.
+
+Fixes: 10666251554c ("ACPI: battery: Install Notify() handler directly")
+Closes: https://lore.kernel.org/linux-acpi/20250910142653.313360-1-luogf2025@163.com/
+Reported-by: GuangFei Luo <luogf2025@163.com>
+Tested-by: GuangFei Luo <luogf2025@163.com>
+Cc: 6.6+ <stable@vger.kernel.org> # 6.6+
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/battery.c |   43 +++++++++++++++++++++++++++++--------------
+ 1 file changed, 29 insertions(+), 14 deletions(-)
+
+--- a/drivers/acpi/battery.c
++++ b/drivers/acpi/battery.c
+@@ -92,7 +92,7 @@ enum {
+ struct acpi_battery {
+       struct mutex lock;
+-      struct mutex sysfs_lock;
++      struct mutex update_lock;
+       struct power_supply *bat;
+       struct power_supply_desc bat_desc;
+       struct acpi_device *device;
+@@ -903,15 +903,12 @@ static int sysfs_add_battery(struct acpi
+ static void sysfs_remove_battery(struct acpi_battery *battery)
+ {
+-      mutex_lock(&battery->sysfs_lock);
+-      if (!battery->bat) {
+-              mutex_unlock(&battery->sysfs_lock);
++      if (!battery->bat)
+               return;
+-      }
++
+       battery_hook_remove_battery(battery);
+       power_supply_unregister(battery->bat);
+       battery->bat = NULL;
+-      mutex_unlock(&battery->sysfs_lock);
+ }
+ static void find_battery(const struct dmi_header *dm, void *private)
+@@ -1071,6 +1068,9 @@ static void acpi_battery_notify(acpi_han
+       if (!battery)
+               return;
++
++      guard(mutex)(&battery->update_lock);
++
+       old = battery->bat;
+       /*
+        * On Acer Aspire V5-573G notifications are sometimes triggered too
+@@ -1093,21 +1093,22 @@ static void acpi_battery_notify(acpi_han
+ }
+ static int battery_notify(struct notifier_block *nb,
+-                             unsigned long mode, void *_unused)
++                        unsigned long mode, void *_unused)
+ {
+       struct acpi_battery *battery = container_of(nb, struct acpi_battery,
+                                                   pm_nb);
+-      int result;
+-      switch (mode) {
+-      case PM_POST_HIBERNATION:
+-      case PM_POST_SUSPEND:
++      if (mode == PM_POST_SUSPEND || mode == PM_POST_HIBERNATION) {
++              guard(mutex)(&battery->update_lock);
++
+               if (!acpi_battery_present(battery))
+                       return 0;
+               if (battery->bat) {
+                       acpi_battery_refresh(battery);
+               } else {
++                      int result;
++
+                       result = acpi_battery_get_info(battery);
+                       if (result)
+                               return result;
+@@ -1119,7 +1120,6 @@ static int battery_notify(struct notifie
+               acpi_battery_init_alarm(battery);
+               acpi_battery_get_state(battery);
+-              break;
+       }
+       return 0;
+@@ -1197,6 +1197,8 @@ static int acpi_battery_update_retry(str
+ {
+       int retry, ret;
++      guard(mutex)(&battery->update_lock);
++
+       for (retry = 5; retry; retry--) {
+               ret = acpi_battery_update(battery, false);
+               if (!ret)
+@@ -1207,6 +1209,13 @@ static int acpi_battery_update_retry(str
+       return ret;
+ }
++static void sysfs_battery_cleanup(struct acpi_battery *battery)
++{
++      guard(mutex)(&battery->update_lock);
++
++      sysfs_remove_battery(battery);
++}
++
+ static int acpi_battery_add(struct acpi_device *device)
+ {
+       int result = 0;
+@@ -1229,7 +1238,7 @@ static int acpi_battery_add(struct acpi_
+       if (result)
+               return result;
+-      result = devm_mutex_init(&device->dev, &battery->sysfs_lock);
++      result = devm_mutex_init(&device->dev, &battery->update_lock);
+       if (result)
+               return result;
+@@ -1259,7 +1268,7 @@ fail_pm:
+       device_init_wakeup(&device->dev, 0);
+       unregister_pm_notifier(&battery->pm_nb);
+ fail:
+-      sysfs_remove_battery(battery);
++      sysfs_battery_cleanup(battery);
+       return result;
+ }
+@@ -1278,6 +1287,9 @@ static void acpi_battery_remove(struct a
+       device_init_wakeup(&device->dev, 0);
+       unregister_pm_notifier(&battery->pm_nb);
++
++      guard(mutex)(&battery->update_lock);
++
+       sysfs_remove_battery(battery);
+ }
+@@ -1295,6 +1307,9 @@ static int acpi_battery_resume(struct de
+               return -EINVAL;
+       battery->update_time = 0;
++
++      guard(mutex)(&battery->update_lock);
++
+       acpi_battery_update(battery, true);
+       return 0;
+ }
diff --git a/queue-6.12/acpi-battery-allocate-driver-data-through-devm_-apis.patch b/queue-6.12/acpi-battery-allocate-driver-data-through-devm_-apis.patch
new file mode 100644 (file)
index 0000000..bb11ae2
--- /dev/null
@@ -0,0 +1,51 @@
+From stable+bounces-186151-greg=kroah.com@vger.kernel.org Thu Oct 16 15:34:49 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Oct 2025 09:34:35 -0400
+Subject: ACPI: battery: allocate driver data through devm_ APIs
+To: stable@vger.kernel.org
+Cc: "Thomas Weißschuh" <linux@weissschuh.net>, "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20251016133438.3296275-1-sashal@kernel.org>
+
+From: Thomas Weißschuh <linux@weissschuh.net>
+
+[ Upstream commit 909dfc60692331e1599d5e28a8f08a611f353aef ]
+
+Simplify the cleanup logic a bit.
+
+Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
+Link: https://patch.msgid.link/20240904-acpi-battery-cleanups-v1-2-a3bf74f22d40@weissschuh.net
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Stable-dep-of: 399dbcadc01e ("ACPI: battery: Add synchronization between interface updates")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/battery.c |    4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/drivers/acpi/battery.c
++++ b/drivers/acpi/battery.c
+@@ -1218,7 +1218,7 @@ static int acpi_battery_add(struct acpi_
+       if (device->dep_unmet)
+               return -EPROBE_DEFER;
+-      battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
++      battery = devm_kzalloc(&device->dev, sizeof(*battery), GFP_KERNEL);
+       if (!battery)
+               return -ENOMEM;
+       battery->device = device;
+@@ -1256,7 +1256,6 @@ fail:
+       sysfs_remove_battery(battery);
+       mutex_destroy(&battery->lock);
+       mutex_destroy(&battery->sysfs_lock);
+-      kfree(battery);
+       return result;
+ }
+@@ -1279,7 +1278,6 @@ static void acpi_battery_remove(struct a
+       mutex_destroy(&battery->lock);
+       mutex_destroy(&battery->sysfs_lock);
+-      kfree(battery);
+ }
+ #ifdef CONFIG_PM_SLEEP
diff --git a/queue-6.12/acpi-battery-check-for-error-code-from-devm_mutex_init-call.patch b/queue-6.12/acpi-battery-check-for-error-code-from-devm_mutex_init-call.patch
new file mode 100644 (file)
index 0000000..ae5d923
--- /dev/null
@@ -0,0 +1,49 @@
+From stable+bounces-186153-greg=kroah.com@vger.kernel.org Thu Oct 16 15:35:02 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Oct 2025 09:34:37 -0400
+Subject: ACPI: battery: Check for error code from devm_mutex_init() call
+To: stable@vger.kernel.org
+Cc: "Andy Shevchenko" <andriy.shevchenko@linux.intel.com>, "Thomas Weißschuh" <linux@weissschuh.net>, "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20251016133438.3296275-3-sashal@kernel.org>
+
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+
+[ Upstream commit 815daedc318b2f9f1b956d0631377619a0d69d96 ]
+
+Even if it's not critical, the avoidance of checking the error code
+from devm_mutex_init() call today diminishes the point of using devm
+variant of it. Tomorrow it may even leak something. Add the missed
+check.
+
+Fixes: 0710c1ce5045 ("ACPI: battery: initialize mutexes through devm_ APIs")
+Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Reviewed-by: Thomas Weißschuh <linux@weissschuh.net>
+Link: https://patch.msgid.link/20241030162754.2110946-1-andriy.shevchenko@linux.intel.com
+[ rjw: Added 2 empty code lines ]
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Stable-dep-of: 399dbcadc01e ("ACPI: battery: Add synchronization between interface updates")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/battery.c |   10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+--- a/drivers/acpi/battery.c
++++ b/drivers/acpi/battery.c
+@@ -1225,8 +1225,14 @@ static int acpi_battery_add(struct acpi_
+       strscpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
+       strscpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
+       device->driver_data = battery;
+-      devm_mutex_init(&device->dev, &battery->lock);
+-      devm_mutex_init(&device->dev, &battery->sysfs_lock);
++      result = devm_mutex_init(&device->dev, &battery->lock);
++      if (result)
++              return result;
++
++      result = devm_mutex_init(&device->dev, &battery->sysfs_lock);
++      if (result)
++              return result;
++
+       if (acpi_has_method(battery->device->handle, "_BIX"))
+               set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
diff --git a/queue-6.12/acpi-battery-initialize-mutexes-through-devm_-apis.patch b/queue-6.12/acpi-battery-initialize-mutexes-through-devm_-apis.patch
new file mode 100644 (file)
index 0000000..e18ba97
--- /dev/null
@@ -0,0 +1,56 @@
+From stable+bounces-186152-greg=kroah.com@vger.kernel.org Thu Oct 16 15:34:55 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Oct 2025 09:34:36 -0400
+Subject: ACPI: battery: initialize mutexes through devm_ APIs
+To: stable@vger.kernel.org
+Cc: "Thomas Weißschuh" <linux@weissschuh.net>, "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20251016133438.3296275-2-sashal@kernel.org>
+
+From: Thomas Weißschuh <linux@weissschuh.net>
+
+[ Upstream commit 0710c1ce50455ed0db91bffa0eebbaa4f69b1773 ]
+
+Simplify the cleanup logic a bit.
+
+Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
+Link: https://patch.msgid.link/20240904-acpi-battery-cleanups-v1-3-a3bf74f22d40@weissschuh.net
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Stable-dep-of: 399dbcadc01e ("ACPI: battery: Add synchronization between interface updates")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/battery.c |    9 ++-------
+ 1 file changed, 2 insertions(+), 7 deletions(-)
+
+--- a/drivers/acpi/battery.c
++++ b/drivers/acpi/battery.c
+@@ -1225,8 +1225,8 @@ static int acpi_battery_add(struct acpi_
+       strscpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
+       strscpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
+       device->driver_data = battery;
+-      mutex_init(&battery->lock);
+-      mutex_init(&battery->sysfs_lock);
++      devm_mutex_init(&device->dev, &battery->lock);
++      devm_mutex_init(&device->dev, &battery->sysfs_lock);
+       if (acpi_has_method(battery->device->handle, "_BIX"))
+               set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
+@@ -1254,8 +1254,6 @@ fail_pm:
+       unregister_pm_notifier(&battery->pm_nb);
+ fail:
+       sysfs_remove_battery(battery);
+-      mutex_destroy(&battery->lock);
+-      mutex_destroy(&battery->sysfs_lock);
+       return result;
+ }
+@@ -1275,9 +1273,6 @@ static void acpi_battery_remove(struct a
+       device_init_wakeup(&device->dev, 0);
+       unregister_pm_notifier(&battery->pm_nb);
+       sysfs_remove_battery(battery);
+-
+-      mutex_destroy(&battery->lock);
+-      mutex_destroy(&battery->sysfs_lock);
+ }
+ #ifdef CONFIG_PM_SLEEP
diff --git a/queue-6.12/acpi-property-add-code-comments-explaining-what-is-going-on.patch b/queue-6.12/acpi-property-add-code-comments-explaining-what-is-going-on.patch
new file mode 100644 (file)
index 0000000..04ad366
--- /dev/null
@@ -0,0 +1,115 @@
+From stable+bounces-186193-greg=kroah.com@vger.kernel.org Thu Oct 16 21:12:38 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Oct 2025 15:12:26 -0400
+Subject: ACPI: property: Add code comments explaining what is going on
+To: stable@vger.kernel.org
+Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, Sakari Ailus <sakari.ailus@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251016191227.3377985-3-sashal@kernel.org>
+
+From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
+
+[ Upstream commit 737c3a09dcf69ba2814f3674947ccaec1861c985 ]
+
+In some places in the ACPI device properties handling code, it is
+unclear why the code is what it is.  Some assumptions are not documented
+and some pieces of code are based on knowledge that is not mentioned
+anywhere.
+
+Add code comments explaining these things.
+
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Reviewed-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Tested-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Stable-dep-of: baf60d5cb8bc ("ACPI: property: Do not pass NULL handles to acpi_attach_data()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/property.c |   46 ++++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 44 insertions(+), 2 deletions(-)
+
+--- a/drivers/acpi/property.c
++++ b/drivers/acpi/property.c
+@@ -108,7 +108,18 @@ static bool acpi_nondev_subnode_extract(
+       if (handle)
+               acpi_get_parent(handle, &scope);
++      /*
++       * Extract properties from the _DSD-equivalent package pointed to by
++       * desc and use scope (if not NULL) for the completion of relative
++       * pathname segments.
++       *
++       * The extracted properties will be held in the new data node dn.
++       */
+       result = acpi_extract_properties(scope, desc, &dn->data);
++      /*
++       * Look for subnodes in the _DSD-equivalent package pointed to by desc
++       * and create child nodes of dn if there are any.
++       */
+       if (acpi_enumerate_nondev_subnodes(scope, desc, &dn->data, &dn->fwnode))
+               result = true;
+@@ -133,6 +144,12 @@ static bool acpi_nondev_subnode_ok(acpi_
+       acpi_handle handle;
+       acpi_status status;
++      /*
++       * If the scope is unknown, the _DSD-equivalent package being parsed
++       * was embedded in an outer _DSD-equivalent package as a result of
++       * direct evaluation of an object pointed to by a reference.  In that
++       * case, using a pathname as the target object pointer is invalid.
++       */
+       if (!scope)
+               return false;
+@@ -162,6 +179,10 @@ static bool acpi_add_nondev_subnodes(acp
+       bool ret = false;
+       int i;
++      /*
++       * Every element in the links package is expected to represent a link
++       * to a non-device node in a tree containing device-specific data.
++       */
+       for (i = 0; i < links->package.count; i++) {
+               union acpi_object *link, *desc;
+               bool result;
+@@ -171,17 +192,38 @@ static bool acpi_add_nondev_subnodes(acp
+               if (link->package.count != 2)
+                       continue;
+-              /* The first one must be a string. */
++              /* The first one (the key) must be a string. */
+               if (link->package.elements[0].type != ACPI_TYPE_STRING)
+                       continue;
+-              /* The second one may be a string or a package. */
++              /* The second one (the target) may be a string or a package. */
+               switch (link->package.elements[1].type) {
+               case ACPI_TYPE_STRING:
++                      /*
++                       * The string is expected to be a full pathname or a
++                       * pathname segment relative to the given scope.  That
++                       * pathname is expected to point to an object returning
++                       * a package that contains _DSD-equivalent information.
++                       */
+                       result = acpi_nondev_subnode_ok(scope, link, list,
+                                                        parent);
+                       break;
+               case ACPI_TYPE_PACKAGE:
++                      /*
++                       * This happens when a reference is used in AML to
++                       * point to the target.  Since the target is expected
++                       * to be a named object, a reference to it will cause it
++                       * to be avaluated in place and its return package will
++                       * be embedded in the links package at the location of
++                       * the reference.
++                       *
++                       * The target package is expected to contain _DSD-
++                       * equivalent information, but the scope in which it
++                       * is located in the original AML is unknown.  Thus
++                       * it cannot contain pathname segments represented as
++                       * strings because there is no way to build full
++                       * pathnames out of them.
++                       */
+                       desc = &link->package.elements[1];
+                       result = acpi_nondev_subnode_extract(desc, NULL, link,
+                                                            list, parent);
diff --git a/queue-6.12/acpi-property-disregard-references-in-data-only-subnode-lists.patch b/queue-6.12/acpi-property-disregard-references-in-data-only-subnode-lists.patch
new file mode 100644 (file)
index 0000000..a907f39
--- /dev/null
@@ -0,0 +1,138 @@
+From stable+bounces-186192-greg=kroah.com@vger.kernel.org Thu Oct 16 21:12:37 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Oct 2025 15:12:25 -0400
+Subject: ACPI: property: Disregard references in data-only subnode lists
+To: stable@vger.kernel.org
+Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, Sakari Ailus <sakari.ailus@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251016191227.3377985-2-sashal@kernel.org>
+
+From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
+
+[ Upstream commit d06118fe9b03426484980ed4c189a8c7b99fa631 ]
+
+Data-only subnode links following the ACPI data subnode GUID in a _DSD
+package are expected to point to named objects returning _DSD-equivalent
+packages.  If a reference to such an object is used in the target field
+of any of those links, that object will be evaluated in place (as a
+named object) and its return data will be embedded in the outer _DSD
+package.
+
+For this reason, it is not expected to see a subnode link with the
+target field containing a local reference (that would mean pointing
+to a device or another object that cannot be evaluated in place and
+therefore cannot return a _DSD-equivalent package).
+
+Accordingly, simplify the code parsing data-only subnode links to
+simply print a message when it encounters a local reference in the
+target field of one of those links.
+
+Moreover, since acpi_nondev_subnode_data_ok() would only have one
+caller after the change above, fold it into that caller.
+
+Link: https://lore.kernel.org/linux-acpi/CAJZ5v0jVeSrDO6hrZhKgRZrH=FpGD4vNUjFD8hV9WwN9TLHjzQ@mail.gmail.com/
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Reviewed-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Tested-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Stable-dep-of: baf60d5cb8bc ("ACPI: property: Do not pass NULL handles to acpi_attach_data()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/property.c |   51 ++++++++++++++++++++----------------------------
+ 1 file changed, 22 insertions(+), 29 deletions(-)
+
+--- a/drivers/acpi/property.c
++++ b/drivers/acpi/property.c
+@@ -124,32 +124,12 @@ static bool acpi_nondev_subnode_extract(
+       return false;
+ }
+-static bool acpi_nondev_subnode_data_ok(acpi_handle handle,
+-                                      const union acpi_object *link,
+-                                      struct list_head *list,
+-                                      struct fwnode_handle *parent)
+-{
+-      struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+-      acpi_status status;
+-
+-      status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
+-                                          ACPI_TYPE_PACKAGE);
+-      if (ACPI_FAILURE(status))
+-              return false;
+-
+-      if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list,
+-                                      parent))
+-              return true;
+-
+-      ACPI_FREE(buf.pointer);
+-      return false;
+-}
+-
+ static bool acpi_nondev_subnode_ok(acpi_handle scope,
+                                  const union acpi_object *link,
+                                  struct list_head *list,
+                                  struct fwnode_handle *parent)
+ {
++      struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+       acpi_handle handle;
+       acpi_status status;
+@@ -161,7 +141,17 @@ static bool acpi_nondev_subnode_ok(acpi_
+       if (ACPI_FAILURE(status))
+               return false;
+-      return acpi_nondev_subnode_data_ok(handle, link, list, parent);
++      status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
++                                          ACPI_TYPE_PACKAGE);
++      if (ACPI_FAILURE(status))
++              return false;
++
++      if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list,
++                                      parent))
++              return true;
++
++      ACPI_FREE(buf.pointer);
++      return false;
+ }
+ static bool acpi_add_nondev_subnodes(acpi_handle scope,
+@@ -174,7 +164,6 @@ static bool acpi_add_nondev_subnodes(acp
+       for (i = 0; i < links->package.count; i++) {
+               union acpi_object *link, *desc;
+-              acpi_handle handle;
+               bool result;
+               link = &links->package.elements[i];
+@@ -186,22 +175,26 @@ static bool acpi_add_nondev_subnodes(acp
+               if (link->package.elements[0].type != ACPI_TYPE_STRING)
+                       continue;
+-              /* The second one may be a string, a reference or a package. */
++              /* The second one may be a string or a package. */
+               switch (link->package.elements[1].type) {
+               case ACPI_TYPE_STRING:
+                       result = acpi_nondev_subnode_ok(scope, link, list,
+                                                        parent);
+                       break;
+-              case ACPI_TYPE_LOCAL_REFERENCE:
+-                      handle = link->package.elements[1].reference.handle;
+-                      result = acpi_nondev_subnode_data_ok(handle, link, list,
+-                                                           parent);
+-                      break;
+               case ACPI_TYPE_PACKAGE:
+                       desc = &link->package.elements[1];
+                       result = acpi_nondev_subnode_extract(desc, NULL, link,
+                                                            list, parent);
+                       break;
++              case ACPI_TYPE_LOCAL_REFERENCE:
++                      /*
++                       * It is not expected to see any local references in
++                       * the links package because referencing a named object
++                       * should cause it to be evaluated in place.
++                       */
++                      acpi_handle_info(scope, "subnode %s: Unexpected reference\n",
++                                       link->package.elements[0].string.pointer);
++                      fallthrough;
+               default:
+                       result = false;
+                       break;
diff --git a/queue-6.12/acpi-property-do-not-pass-null-handles-to-acpi_attach_data.patch b/queue-6.12/acpi-property-do-not-pass-null-handles-to-acpi_attach_data.patch
new file mode 100644 (file)
index 0000000..f0a2f0c
--- /dev/null
@@ -0,0 +1,77 @@
+From stable+bounces-186194-greg=kroah.com@vger.kernel.org Thu Oct 16 21:12:39 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Oct 2025 15:12:27 -0400
+Subject: ACPI: property: Do not pass NULL handles to acpi_attach_data()
+To: stable@vger.kernel.org
+Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, Sakari Ailus <sakari.ailus@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251016191227.3377985-4-sashal@kernel.org>
+
+From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
+
+[ Upstream commit baf60d5cb8bc6b85511c5df5f0ad7620bb66d23c ]
+
+In certain circumstances, the ACPI handle of a data-only node may be
+NULL, in which case it does not make sense to attempt to attach that
+node to an ACPI namespace object, so update the code to avoid attempts
+to do so.
+
+This prevents confusing and unuseful error messages from being printed.
+
+Also document the fact that the ACPI handle of a data-only node may be
+NULL and when that happens in a code comment.  In addition, make
+acpi_add_nondev_subnodes() print a diagnostic message for each data-only
+node with an unknown ACPI namespace scope.
+
+Fixes: 1d52f10917a7 ("ACPI: property: Tie data nodes to acpi handles")
+Cc: 6.0+ <stable@vger.kernel.org> # 6.0+
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Reviewed-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Tested-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/property.c |   12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/acpi/property.c
++++ b/drivers/acpi/property.c
+@@ -124,6 +124,10 @@ static bool acpi_nondev_subnode_extract(
+               result = true;
+       if (result) {
++              /*
++               * This will be NULL if the desc package is embedded in an outer
++               * _DSD-equivalent package and its scope cannot be determined.
++               */
+               dn->handle = handle;
+               dn->data.pointer = desc;
+               list_add_tail(&dn->sibling, list);
+@@ -224,6 +228,8 @@ static bool acpi_add_nondev_subnodes(acp
+                        * strings because there is no way to build full
+                        * pathnames out of them.
+                        */
++                      acpi_handle_debug(scope, "subnode %s: Unknown scope\n",
++                                        link->package.elements[0].string.pointer);
+                       desc = &link->package.elements[1];
+                       result = acpi_nondev_subnode_extract(desc, NULL, link,
+                                                            list, parent);
+@@ -396,6 +402,9 @@ static void acpi_untie_nondev_subnodes(s
+       struct acpi_data_node *dn;
+       list_for_each_entry(dn, &data->subnodes, sibling) {
++              if (!dn->handle)
++                      continue;
++
+               acpi_detach_data(dn->handle, acpi_nondev_subnode_tag);
+               acpi_untie_nondev_subnodes(&dn->data);
+@@ -410,6 +419,9 @@ static bool acpi_tie_nondev_subnodes(str
+               acpi_status status;
+               bool ret;
++              if (!dn->handle)
++                      continue;
++
+               status = acpi_attach_data(dn->handle, acpi_nondev_subnode_tag, dn);
+               if (ACPI_FAILURE(status) && status != AE_ALREADY_EXISTS) {
+                       acpi_handle_err(dn->handle, "Can't tag data node\n");
diff --git a/queue-6.12/ipmi-fix-handling-of-messages-with-provided-receive-message-pointer.patch b/queue-6.12/ipmi-fix-handling-of-messages-with-provided-receive-message-pointer.patch
new file mode 100644 (file)
index 0000000..bf4dc61
--- /dev/null
@@ -0,0 +1,50 @@
+From stable+bounces-186184-greg=kroah.com@vger.kernel.org Thu Oct 16 20:49:33 2025
+From: Corey Minyard <corey@minyard.net>
+Date: Thu, 16 Oct 2025 13:49:17 -0500
+Subject: ipmi: Fix handling of messages with provided receive message pointer
+To: stable@vger.kernel.org
+Cc: Guenter Roeck <linux@roeck-us.net>, Eric Dumazet <edumazet@google.com>, Greg Thelen <gthelen@google.com>, Corey Minyard <corey@minyard.net>
+Message-ID: <20251016184917.1875857-2-corey@minyard.net>
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+commit e2c69490dda5d4c9f1bfbb2898989c8f3530e354 upstream
+
+Prior to commit b52da4054ee0 ("ipmi: Rework user message limit handling"),
+i_ipmi_request() used to increase the user reference counter if the receive
+message is provided by the caller of IPMI API functions. This is no longer
+the case. However, ipmi_free_recv_msg() is still called and decreases the
+reference counter. This results in the reference counter reaching zero,
+the user data pointer is released, and all kinds of interesting crashes are
+seen.
+
+Fix the problem by increasing user reference counter if the receive message
+has been provided by the caller.
+
+Fixes: b52da4054ee0 ("ipmi: Rework user message limit handling")
+Reported-by: Eric Dumazet <edumazet@google.com>
+Cc: Eric Dumazet <edumazet@google.com>
+Cc: Greg Thelen <gthelen@google.com>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Message-ID: <20251006201857.3433837-1-linux@roeck-us.net>
+Signed-off-by: Corey Minyard <corey@minyard.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/char/ipmi/ipmi_msghandler.c |    5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/char/ipmi/ipmi_msghandler.c
++++ b/drivers/char/ipmi/ipmi_msghandler.c
+@@ -2311,8 +2311,11 @@ static int i_ipmi_request(struct ipmi_us
+       if (supplied_recv) {
+               recv_msg = supplied_recv;
+               recv_msg->user = user;
+-              if (user)
++              if (user) {
+                       atomic_inc(&user->nr_msgs);
++                      /* The put happens when the message is freed. */
++                      kref_get(&user->refcount);
++              }
+       } else {
+               recv_msg = ipmi_alloc_recv_msg(user);
+               if (IS_ERR(recv_msg))
diff --git a/queue-6.12/ipmi-rework-user-message-limit-handling.patch b/queue-6.12/ipmi-rework-user-message-limit-handling.patch
new file mode 100644 (file)
index 0000000..8110856
--- /dev/null
@@ -0,0 +1,646 @@
+From stable+bounces-186185-greg=kroah.com@vger.kernel.org Thu Oct 16 20:49:37 2025
+From: Corey Minyard <corey@minyard.net>
+Date: Thu, 16 Oct 2025 13:49:16 -0500
+Subject: ipmi: Rework user message limit handling
+To: stable@vger.kernel.org
+Cc: Corey Minyard <corey@minyard.net>, Gilles BULOZ <gilles.buloz@kontron.com>
+Message-ID: <20251016184917.1875857-1-corey@minyard.net>
+
+From: Corey Minyard <corey@minyard.net>
+
+commit b52da4054ee0bf9ecb44996f2c83236ff50b3812 upstream
+
+This patch required quite a bit of work to backport due to a number
+of unrelated changes that do not make sense to backport.  This has
+been run against my test suite and passes all tests.
+
+The limit on the number of user messages had a number of issues,
+improper counting in some cases and a use after free.
+
+Restructure how this is all done to handle more in the receive message
+allocation routine, so all refcouting and user message limit counts
+are done in that routine.  It's a lot cleaner and safer.
+
+Reported-by: Gilles BULOZ <gilles.buloz@kontron.com>
+Closes: https://lore.kernel.org/lkml/aLsw6G0GyqfpKs2S@mail.minyard.net/
+Fixes: 8e76741c3d8b ("ipmi: Add a limit on the number of users that may use IPMI")
+Cc: <stable@vger.kernel.org> # 4.19
+Signed-off-by: Corey Minyard <corey@minyard.net>
+Tested-by: Gilles BULOZ <gilles.buloz@kontron.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/char/ipmi/ipmi_msghandler.c |  415 +++++++++++++++++-------------------
+ 1 file changed, 198 insertions(+), 217 deletions(-)
+
+--- a/drivers/char/ipmi/ipmi_msghandler.c
++++ b/drivers/char/ipmi/ipmi_msghandler.c
+@@ -39,7 +39,9 @@
+ #define IPMI_DRIVER_VERSION "39.2"
+-static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
++static struct ipmi_recv_msg *ipmi_alloc_recv_msg(struct ipmi_user *user);
++static void ipmi_set_recv_msg_user(struct ipmi_recv_msg *msg,
++                                 struct ipmi_user *user);
+ static int ipmi_init_msghandler(void);
+ static void smi_recv_work(struct work_struct *t);
+ static void handle_new_recv_msgs(struct ipmi_smi *intf);
+@@ -939,13 +941,11 @@ static int deliver_response(struct ipmi_
+                * risk.  At this moment, simply skip it in that case.
+                */
+               ipmi_free_recv_msg(msg);
+-              atomic_dec(&msg->user->nr_msgs);
+       } else {
+               int index;
+               struct ipmi_user *user = acquire_ipmi_user(msg->user, &index);
+               if (user) {
+-                      atomic_dec(&user->nr_msgs);
+                       user->handler->ipmi_recv_hndl(msg, user->handler_data);
+                       release_ipmi_user(user, index);
+               } else {
+@@ -1634,8 +1634,7 @@ int ipmi_set_gets_events(struct ipmi_use
+               spin_unlock_irqrestore(&intf->events_lock, flags);
+               list_for_each_entry_safe(msg, msg2, &msgs, link) {
+-                      msg->user = user;
+-                      kref_get(&user->refcount);
++                      ipmi_set_recv_msg_user(msg, user);
+                       deliver_local_response(intf, msg);
+               }
+@@ -2309,22 +2308,15 @@ static int i_ipmi_request(struct ipmi_us
+       struct ipmi_recv_msg *recv_msg;
+       int rv = 0;
+-      if (user) {
+-              if (atomic_add_return(1, &user->nr_msgs) > max_msgs_per_user) {
+-                      /* Decrement will happen at the end of the routine. */
+-                      rv = -EBUSY;
+-                      goto out;
+-              }
+-      }
+-
+-      if (supplied_recv)
++      if (supplied_recv) {
+               recv_msg = supplied_recv;
+-      else {
+-              recv_msg = ipmi_alloc_recv_msg();
+-              if (recv_msg == NULL) {
+-                      rv = -ENOMEM;
+-                      goto out;
+-              }
++              recv_msg->user = user;
++              if (user)
++                      atomic_inc(&user->nr_msgs);
++      } else {
++              recv_msg = ipmi_alloc_recv_msg(user);
++              if (IS_ERR(recv_msg))
++                      return PTR_ERR(recv_msg);
+       }
+       recv_msg->user_msg_data = user_msg_data;
+@@ -2335,8 +2327,7 @@ static int i_ipmi_request(struct ipmi_us
+               if (smi_msg == NULL) {
+                       if (!supplied_recv)
+                               ipmi_free_recv_msg(recv_msg);
+-                      rv = -ENOMEM;
+-                      goto out;
++                      return -ENOMEM;
+               }
+       }
+@@ -2346,10 +2337,6 @@ static int i_ipmi_request(struct ipmi_us
+               goto out_err;
+       }
+-      recv_msg->user = user;
+-      if (user)
+-              /* The put happens when the message is freed. */
+-              kref_get(&user->refcount);
+       recv_msg->msgid = msgid;
+       /*
+        * Store the message to send in the receive message so timeout
+@@ -2378,8 +2365,10 @@ static int i_ipmi_request(struct ipmi_us
+       if (rv) {
+ out_err:
+-              ipmi_free_smi_msg(smi_msg);
+-              ipmi_free_recv_msg(recv_msg);
++              if (!supplied_smi)
++                      ipmi_free_smi_msg(smi_msg);
++              if (!supplied_recv)
++                      ipmi_free_recv_msg(recv_msg);
+       } else {
+               dev_dbg(intf->si_dev, "Send: %*ph\n",
+                       smi_msg->data_size, smi_msg->data);
+@@ -2388,9 +2377,6 @@ out_err:
+       }
+       rcu_read_unlock();
+-out:
+-      if (rv && user)
+-              atomic_dec(&user->nr_msgs);
+       return rv;
+ }
+@@ -3882,7 +3868,7 @@ static int handle_ipmb_get_msg_cmd(struc
+       unsigned char            chan;
+       struct ipmi_user         *user = NULL;
+       struct ipmi_ipmb_addr    *ipmb_addr;
+-      struct ipmi_recv_msg     *recv_msg;
++      struct ipmi_recv_msg     *recv_msg = NULL;
+       if (msg->rsp_size < 10) {
+               /* Message not big enough, just ignore it. */
+@@ -3903,9 +3889,8 @@ static int handle_ipmb_get_msg_cmd(struc
+       rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
+       if (rcvr) {
+               user = rcvr->user;
+-              kref_get(&user->refcount);
+-      } else
+-              user = NULL;
++              recv_msg = ipmi_alloc_recv_msg(user);
++      }
+       rcu_read_unlock();
+       if (user == NULL) {
+@@ -3940,47 +3925,41 @@ static int handle_ipmb_get_msg_cmd(struc
+                       rv = -1;
+               }
+               rcu_read_unlock();
+-      } else {
+-              recv_msg = ipmi_alloc_recv_msg();
+-              if (!recv_msg) {
+-                      /*
+-                       * We couldn't allocate memory for the
+-                       * message, so requeue it for handling
+-                       * later.
+-                       */
+-                      rv = 1;
+-                      kref_put(&user->refcount, free_user);
+-              } else {
+-                      /* Extract the source address from the data. */
+-                      ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr;
+-                      ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
+-                      ipmb_addr->slave_addr = msg->rsp[6];
+-                      ipmb_addr->lun = msg->rsp[7] & 3;
+-                      ipmb_addr->channel = msg->rsp[3] & 0xf;
++      } else if (!IS_ERR(recv_msg)) {
++              /* Extract the source address from the data. */
++              ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr;
++              ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
++              ipmb_addr->slave_addr = msg->rsp[6];
++              ipmb_addr->lun = msg->rsp[7] & 3;
++              ipmb_addr->channel = msg->rsp[3] & 0xf;
+-                      /*
+-                       * Extract the rest of the message information
+-                       * from the IPMB header.
+-                       */
+-                      recv_msg->user = user;
+-                      recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
+-                      recv_msg->msgid = msg->rsp[7] >> 2;
+-                      recv_msg->msg.netfn = msg->rsp[4] >> 2;
+-                      recv_msg->msg.cmd = msg->rsp[8];
+-                      recv_msg->msg.data = recv_msg->msg_data;
++              /*
++               * Extract the rest of the message information
++               * from the IPMB header.
++               */
++              recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
++              recv_msg->msgid = msg->rsp[7] >> 2;
++              recv_msg->msg.netfn = msg->rsp[4] >> 2;
++              recv_msg->msg.cmd = msg->rsp[8];
++              recv_msg->msg.data = recv_msg->msg_data;
+-                      /*
+-                       * We chop off 10, not 9 bytes because the checksum
+-                       * at the end also needs to be removed.
+-                       */
+-                      recv_msg->msg.data_len = msg->rsp_size - 10;
+-                      memcpy(recv_msg->msg_data, &msg->rsp[9],
+-                             msg->rsp_size - 10);
+-                      if (deliver_response(intf, recv_msg))
+-                              ipmi_inc_stat(intf, unhandled_commands);
+-                      else
+-                              ipmi_inc_stat(intf, handled_commands);
+-              }
++              /*
++               * We chop off 10, not 9 bytes because the checksum
++               * at the end also needs to be removed.
++               */
++              recv_msg->msg.data_len = msg->rsp_size - 10;
++              memcpy(recv_msg->msg_data, &msg->rsp[9],
++                     msg->rsp_size - 10);
++              if (deliver_response(intf, recv_msg))
++                      ipmi_inc_stat(intf, unhandled_commands);
++              else
++                      ipmi_inc_stat(intf, handled_commands);
++      } else {
++              /*
++               * We couldn't allocate memory for the message, so
++               * requeue it for handling later.
++               */
++              rv = 1;
+       }
+       return rv;
+@@ -3993,7 +3972,7 @@ static int handle_ipmb_direct_rcv_cmd(st
+       int                      rv = 0;
+       struct ipmi_user         *user = NULL;
+       struct ipmi_ipmb_direct_addr *daddr;
+-      struct ipmi_recv_msg     *recv_msg;
++      struct ipmi_recv_msg     *recv_msg = NULL;
+       unsigned char netfn = msg->rsp[0] >> 2;
+       unsigned char cmd = msg->rsp[3];
+@@ -4002,9 +3981,8 @@ static int handle_ipmb_direct_rcv_cmd(st
+       rcvr = find_cmd_rcvr(intf, netfn, cmd, 0);
+       if (rcvr) {
+               user = rcvr->user;
+-              kref_get(&user->refcount);
+-      } else
+-              user = NULL;
++              recv_msg = ipmi_alloc_recv_msg(user);
++      }
+       rcu_read_unlock();
+       if (user == NULL) {
+@@ -4031,44 +4009,38 @@ static int handle_ipmb_direct_rcv_cmd(st
+                       rv = -1;
+               }
+               rcu_read_unlock();
+-      } else {
+-              recv_msg = ipmi_alloc_recv_msg();
+-              if (!recv_msg) {
+-                      /*
+-                       * We couldn't allocate memory for the
+-                       * message, so requeue it for handling
+-                       * later.
+-                       */
+-                      rv = 1;
+-                      kref_put(&user->refcount, free_user);
+-              } else {
+-                      /* Extract the source address from the data. */
+-                      daddr = (struct ipmi_ipmb_direct_addr *)&recv_msg->addr;
+-                      daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE;
+-                      daddr->channel = 0;
+-                      daddr->slave_addr = msg->rsp[1];
+-                      daddr->rs_lun = msg->rsp[0] & 3;
+-                      daddr->rq_lun = msg->rsp[2] & 3;
++      } else if (!IS_ERR(recv_msg)) {
++              /* Extract the source address from the data. */
++              daddr = (struct ipmi_ipmb_direct_addr *)&recv_msg->addr;
++              daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE;
++              daddr->channel = 0;
++              daddr->slave_addr = msg->rsp[1];
++              daddr->rs_lun = msg->rsp[0] & 3;
++              daddr->rq_lun = msg->rsp[2] & 3;
+-                      /*
+-                       * Extract the rest of the message information
+-                       * from the IPMB header.
+-                       */
+-                      recv_msg->user = user;
+-                      recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
+-                      recv_msg->msgid = (msg->rsp[2] >> 2);
+-                      recv_msg->msg.netfn = msg->rsp[0] >> 2;
+-                      recv_msg->msg.cmd = msg->rsp[3];
+-                      recv_msg->msg.data = recv_msg->msg_data;
+-
+-                      recv_msg->msg.data_len = msg->rsp_size - 4;
+-                      memcpy(recv_msg->msg_data, msg->rsp + 4,
+-                             msg->rsp_size - 4);
+-                      if (deliver_response(intf, recv_msg))
+-                              ipmi_inc_stat(intf, unhandled_commands);
+-                      else
+-                              ipmi_inc_stat(intf, handled_commands);
+-              }
++              /*
++               * Extract the rest of the message information
++               * from the IPMB header.
++               */
++              recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
++              recv_msg->msgid = (msg->rsp[2] >> 2);
++              recv_msg->msg.netfn = msg->rsp[0] >> 2;
++              recv_msg->msg.cmd = msg->rsp[3];
++              recv_msg->msg.data = recv_msg->msg_data;
++
++              recv_msg->msg.data_len = msg->rsp_size - 4;
++              memcpy(recv_msg->msg_data, msg->rsp + 4,
++                     msg->rsp_size - 4);
++              if (deliver_response(intf, recv_msg))
++                      ipmi_inc_stat(intf, unhandled_commands);
++              else
++                      ipmi_inc_stat(intf, handled_commands);
++      } else {
++              /*
++               * We couldn't allocate memory for the message, so
++               * requeue it for handling later.
++               */
++              rv = 1;
+       }
+       return rv;
+@@ -4182,7 +4154,7 @@ static int handle_lan_get_msg_cmd(struct
+       unsigned char            chan;
+       struct ipmi_user         *user = NULL;
+       struct ipmi_lan_addr     *lan_addr;
+-      struct ipmi_recv_msg     *recv_msg;
++      struct ipmi_recv_msg     *recv_msg = NULL;
+       if (msg->rsp_size < 12) {
+               /* Message not big enough, just ignore it. */
+@@ -4203,9 +4175,8 @@ static int handle_lan_get_msg_cmd(struct
+       rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
+       if (rcvr) {
+               user = rcvr->user;
+-              kref_get(&user->refcount);
+-      } else
+-              user = NULL;
++              recv_msg = ipmi_alloc_recv_msg(user);
++      }
+       rcu_read_unlock();
+       if (user == NULL) {
+@@ -4217,49 +4188,44 @@ static int handle_lan_get_msg_cmd(struct
+                * them to be freed.
+                */
+               rv = 0;
+-      } else {
+-              recv_msg = ipmi_alloc_recv_msg();
+-              if (!recv_msg) {
+-                      /*
+-                       * We couldn't allocate memory for the
+-                       * message, so requeue it for handling later.
+-                       */
+-                      rv = 1;
+-                      kref_put(&user->refcount, free_user);
+-              } else {
+-                      /* Extract the source address from the data. */
+-                      lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr;
+-                      lan_addr->addr_type = IPMI_LAN_ADDR_TYPE;
+-                      lan_addr->session_handle = msg->rsp[4];
+-                      lan_addr->remote_SWID = msg->rsp[8];
+-                      lan_addr->local_SWID = msg->rsp[5];
+-                      lan_addr->lun = msg->rsp[9] & 3;
+-                      lan_addr->channel = msg->rsp[3] & 0xf;
+-                      lan_addr->privilege = msg->rsp[3] >> 4;
++      } else if (!IS_ERR(recv_msg)) {
++              /* Extract the source address from the data. */
++              lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr;
++              lan_addr->addr_type = IPMI_LAN_ADDR_TYPE;
++              lan_addr->session_handle = msg->rsp[4];
++              lan_addr->remote_SWID = msg->rsp[8];
++              lan_addr->local_SWID = msg->rsp[5];
++              lan_addr->lun = msg->rsp[9] & 3;
++              lan_addr->channel = msg->rsp[3] & 0xf;
++              lan_addr->privilege = msg->rsp[3] >> 4;
+-                      /*
+-                       * Extract the rest of the message information
+-                       * from the IPMB header.
+-                       */
+-                      recv_msg->user = user;
+-                      recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
+-                      recv_msg->msgid = msg->rsp[9] >> 2;
+-                      recv_msg->msg.netfn = msg->rsp[6] >> 2;
+-                      recv_msg->msg.cmd = msg->rsp[10];
+-                      recv_msg->msg.data = recv_msg->msg_data;
++              /*
++               * Extract the rest of the message information
++               * from the IPMB header.
++               */
++              recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
++              recv_msg->msgid = msg->rsp[9] >> 2;
++              recv_msg->msg.netfn = msg->rsp[6] >> 2;
++              recv_msg->msg.cmd = msg->rsp[10];
++              recv_msg->msg.data = recv_msg->msg_data;
+-                      /*
+-                       * We chop off 12, not 11 bytes because the checksum
+-                       * at the end also needs to be removed.
+-                       */
+-                      recv_msg->msg.data_len = msg->rsp_size - 12;
+-                      memcpy(recv_msg->msg_data, &msg->rsp[11],
+-                             msg->rsp_size - 12);
+-                      if (deliver_response(intf, recv_msg))
+-                              ipmi_inc_stat(intf, unhandled_commands);
+-                      else
+-                              ipmi_inc_stat(intf, handled_commands);
+-              }
++              /*
++               * We chop off 12, not 11 bytes because the checksum
++               * at the end also needs to be removed.
++               */
++              recv_msg->msg.data_len = msg->rsp_size - 12;
++              memcpy(recv_msg->msg_data, &msg->rsp[11],
++                     msg->rsp_size - 12);
++              if (deliver_response(intf, recv_msg))
++                      ipmi_inc_stat(intf, unhandled_commands);
++              else
++                      ipmi_inc_stat(intf, handled_commands);
++      } else {
++              /*
++               * We couldn't allocate memory for the message, so
++               * requeue it for handling later.
++               */
++              rv = 1;
+       }
+       return rv;
+@@ -4281,7 +4247,7 @@ static int handle_oem_get_msg_cmd(struct
+       unsigned char         chan;
+       struct ipmi_user *user = NULL;
+       struct ipmi_system_interface_addr *smi_addr;
+-      struct ipmi_recv_msg  *recv_msg;
++      struct ipmi_recv_msg  *recv_msg = NULL;
+       /*
+        * We expect the OEM SW to perform error checking
+@@ -4310,9 +4276,8 @@ static int handle_oem_get_msg_cmd(struct
+       rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
+       if (rcvr) {
+               user = rcvr->user;
+-              kref_get(&user->refcount);
+-      } else
+-              user = NULL;
++              recv_msg = ipmi_alloc_recv_msg(user);
++      }
+       rcu_read_unlock();
+       if (user == NULL) {
+@@ -4325,48 +4290,42 @@ static int handle_oem_get_msg_cmd(struct
+                */
+               rv = 0;
+-      } else {
+-              recv_msg = ipmi_alloc_recv_msg();
+-              if (!recv_msg) {
+-                      /*
+-                       * We couldn't allocate memory for the
+-                       * message, so requeue it for handling
+-                       * later.
+-                       */
+-                      rv = 1;
+-                      kref_put(&user->refcount, free_user);
+-              } else {
+-                      /*
+-                       * OEM Messages are expected to be delivered via
+-                       * the system interface to SMS software.  We might
+-                       * need to visit this again depending on OEM
+-                       * requirements
+-                       */
+-                      smi_addr = ((struct ipmi_system_interface_addr *)
+-                                  &recv_msg->addr);
+-                      smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+-                      smi_addr->channel = IPMI_BMC_CHANNEL;
+-                      smi_addr->lun = msg->rsp[0] & 3;
+-
+-                      recv_msg->user = user;
+-                      recv_msg->user_msg_data = NULL;
+-                      recv_msg->recv_type = IPMI_OEM_RECV_TYPE;
+-                      recv_msg->msg.netfn = msg->rsp[0] >> 2;
+-                      recv_msg->msg.cmd = msg->rsp[1];
+-                      recv_msg->msg.data = recv_msg->msg_data;
++      } else if (!IS_ERR(recv_msg)) {
++              /*
++               * OEM Messages are expected to be delivered via
++               * the system interface to SMS software.  We might
++               * need to visit this again depending on OEM
++               * requirements
++               */
++              smi_addr = ((struct ipmi_system_interface_addr *)
++                          &recv_msg->addr);
++              smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
++              smi_addr->channel = IPMI_BMC_CHANNEL;
++              smi_addr->lun = msg->rsp[0] & 3;
++
++              recv_msg->user_msg_data = NULL;
++              recv_msg->recv_type = IPMI_OEM_RECV_TYPE;
++              recv_msg->msg.netfn = msg->rsp[0] >> 2;
++              recv_msg->msg.cmd = msg->rsp[1];
++              recv_msg->msg.data = recv_msg->msg_data;
+-                      /*
+-                       * The message starts at byte 4 which follows the
+-                       * Channel Byte in the "GET MESSAGE" command
+-                       */
+-                      recv_msg->msg.data_len = msg->rsp_size - 4;
+-                      memcpy(recv_msg->msg_data, &msg->rsp[4],
+-                             msg->rsp_size - 4);
+-                      if (deliver_response(intf, recv_msg))
+-                              ipmi_inc_stat(intf, unhandled_commands);
+-                      else
+-                              ipmi_inc_stat(intf, handled_commands);
+-              }
++              /*
++               * The message starts at byte 4 which follows the
++               * Channel Byte in the "GET MESSAGE" command
++               */
++              recv_msg->msg.data_len = msg->rsp_size - 4;
++              memcpy(recv_msg->msg_data, &msg->rsp[4],
++                     msg->rsp_size - 4);
++              if (deliver_response(intf, recv_msg))
++                      ipmi_inc_stat(intf, unhandled_commands);
++              else
++                      ipmi_inc_stat(intf, handled_commands);
++      } else {
++              /*
++               * We couldn't allocate memory for the message, so
++               * requeue it for handling later.
++               */
++              rv = 1;
+       }
+       return rv;
+@@ -4425,8 +4384,8 @@ static int handle_read_event_rsp(struct
+               if (!user->gets_events)
+                       continue;
+-              recv_msg = ipmi_alloc_recv_msg();
+-              if (!recv_msg) {
++              recv_msg = ipmi_alloc_recv_msg(user);
++              if (IS_ERR(recv_msg)) {
+                       rcu_read_unlock();
+                       list_for_each_entry_safe(recv_msg, recv_msg2, &msgs,
+                                                link) {
+@@ -4445,8 +4404,6 @@ static int handle_read_event_rsp(struct
+               deliver_count++;
+               copy_event_into_recv_msg(recv_msg, msg);
+-              recv_msg->user = user;
+-              kref_get(&user->refcount);
+               list_add_tail(&recv_msg->link, &msgs);
+       }
+       srcu_read_unlock(&intf->users_srcu, index);
+@@ -4462,8 +4419,8 @@ static int handle_read_event_rsp(struct
+                * No one to receive the message, put it in queue if there's
+                * not already too many things in the queue.
+                */
+-              recv_msg = ipmi_alloc_recv_msg();
+-              if (!recv_msg) {
++              recv_msg = ipmi_alloc_recv_msg(NULL);
++              if (IS_ERR(recv_msg)) {
+                       /*
+                        * We couldn't allocate memory for the
+                        * message, so requeue it for handling
+@@ -5155,27 +5112,51 @@ static void free_recv_msg(struct ipmi_re
+               kfree(msg);
+ }
+-static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void)
++static struct ipmi_recv_msg *ipmi_alloc_recv_msg(struct ipmi_user *user)
+ {
+       struct ipmi_recv_msg *rv;
++      if (user) {
++              if (atomic_add_return(1, &user->nr_msgs) > max_msgs_per_user) {
++                      atomic_dec(&user->nr_msgs);
++                      return ERR_PTR(-EBUSY);
++              }
++      }
++
+       rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC);
+-      if (rv) {
+-              rv->user = NULL;
+-              rv->done = free_recv_msg;
+-              atomic_inc(&recv_msg_inuse_count);
++      if (!rv) {
++              if (user)
++                      atomic_dec(&user->nr_msgs);
++              return ERR_PTR(-ENOMEM);
+       }
++
++      rv->user = user;
++      rv->done = free_recv_msg;
++      if (user)
++              kref_get(&user->refcount);
++      atomic_inc(&recv_msg_inuse_count);
+       return rv;
+ }
+ void ipmi_free_recv_msg(struct ipmi_recv_msg *msg)
+ {
+-      if (msg->user && !oops_in_progress)
++      if (msg->user && !oops_in_progress) {
++              atomic_dec(&msg->user->nr_msgs);
+               kref_put(&msg->user->refcount, free_user);
++      }
+       msg->done(msg);
+ }
+ EXPORT_SYMBOL(ipmi_free_recv_msg);
++static void ipmi_set_recv_msg_user(struct ipmi_recv_msg *msg,
++                                 struct ipmi_user *user)
++{
++      WARN_ON_ONCE(msg->user); /* User should not be set. */
++      msg->user = user;
++      atomic_inc(&user->nr_msgs);
++      kref_get(&user->refcount);
++}
++
+ static atomic_t panic_done_count = ATOMIC_INIT(0);
+ static void dummy_smi_done_handler(struct ipmi_smi_msg *msg)
diff --git a/queue-6.12/mptcp-pm-in-kernel-usable-client-side-with-c-flag.patch b/queue-6.12/mptcp-pm-in-kernel-usable-client-side-with-c-flag.patch
new file mode 100644 (file)
index 0000000..0eb594a
--- /dev/null
@@ -0,0 +1,178 @@
+From stable+bounces-186178-greg=kroah.com@vger.kernel.org Thu Oct 16 18:57:55 2025
+From: "Matthieu Baerts (NGI0)" <matttbe@kernel.org>
+Date: Thu, 16 Oct 2025 18:56:57 +0200
+Subject: mptcp: pm: in-kernel: usable client side with C-flag
+To: stable@vger.kernel.org, gregkh@linuxfoundation.org
+Cc: MPTCP Upstream <mptcp@lists.linux.dev>, "Matthieu Baerts (NGI0)" <matttbe@kernel.org>, Geliang Tang <geliang@kernel.org>, Jakub Kicinski <kuba@kernel.org>
+Message-ID: <20251016165656.940021-2-matttbe@kernel.org>
+
+From: "Matthieu Baerts (NGI0)" <matttbe@kernel.org>
+
+commit 4b1ff850e0c1aacc23e923ed22989b827b9808f9 upstream.
+
+When servers set the C-flag in their MP_CAPABLE to tell clients not to
+create subflows to the initial address and port, clients will likely not
+use their other endpoints. That's because the in-kernel path-manager
+uses the 'subflow' endpoints to create subflows only to the initial
+address and port.
+
+If the limits have not been modified to accept ADD_ADDR, the client
+doesn't try to establish new subflows. If the limits accept ADD_ADDR,
+the routing routes will be used to select the source IP.
+
+The C-flag is typically set when the server is operating behind a legacy
+Layer 4 load balancer, or using anycast IP address. Clients having their
+different 'subflow' endpoints setup, don't end up creating multiple
+subflows as expected, and causing some deployment issues.
+
+A special case is then added here: when servers set the C-flag in the
+MPC and directly sends an ADD_ADDR, this single ADD_ADDR is accepted.
+The 'subflows' endpoints will then be used with this new remote IP and
+port. This exception is only allowed when the ADD_ADDR is sent
+immediately after the 3WHS, and makes the client switching to the 'fully
+established' mode. After that, 'select_local_address()' will not be able
+to find any subflows, because 'id_avail_bitmap' will be filled in
+mptcp_pm_create_subflow_or_signal_addr(), when switching to 'fully
+established' mode.
+
+Fixes: df377be38725 ("mptcp: add deny_join_id0 in mptcp_options_received")
+Cc: stable@vger.kernel.org
+Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/536
+Reviewed-by: Geliang Tang <geliang@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20250925-net-next-mptcp-c-flag-laminar-v1-1-ad126cc47c6b@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[ Conflict in pm.c, because commit 498d7d8b75f1 ("mptcp: pm: remove
+  '_nl' from mptcp_pm_nl_is_init_remote_addr") renamed an helper in the
+  context, and it is not in this version. The same new code can be
+  applied at the same place.
+  Conflict in pm_kernel.c, because the modified code has been moved from
+  pm_netlink.c to pm_kernel.c in commit 8617e85e04bd ("mptcp: pm: split
+  in-kernel PM specific code"), which is not in this version. The
+  resolution is easy: simply by applying the patch where 'pm_kernel.c'
+  has been replaced 'pm_netlink.c'. 'patch --merge' managed to apply
+  this modified patch without creating any conflicts. ]
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/mptcp/pm.c         |    7 ++++--
+ net/mptcp/pm_netlink.c |   50 ++++++++++++++++++++++++++++++++++++++++++++++++-
+ net/mptcp/protocol.h   |    8 +++++++
+ 3 files changed, 62 insertions(+), 3 deletions(-)
+
+--- a/net/mptcp/pm.c
++++ b/net/mptcp/pm.c
+@@ -226,9 +226,12 @@ void mptcp_pm_add_addr_received(const st
+               } else {
+                       __MPTCP_INC_STATS(sock_net((struct sock *)msk), MPTCP_MIB_ADDADDRDROP);
+               }
+-      /* id0 should not have a different address */
++      /* - id0 should not have a different address
++       * - special case for C-flag: linked to fill_local_addresses_vec()
++       */
+       } else if ((addr->id == 0 && !mptcp_pm_nl_is_init_remote_addr(msk, addr)) ||
+-                 (addr->id > 0 && !READ_ONCE(pm->accept_addr))) {
++                 (addr->id > 0 && !READ_ONCE(pm->accept_addr) &&
++                  !mptcp_pm_add_addr_c_flag_case(msk))) {
+               mptcp_pm_announce_addr(msk, addr, true);
+               mptcp_pm_add_addr_send_ack(msk);
+       } else if (mptcp_pm_schedule_work(msk, MPTCP_PM_ADD_ADDR_RECEIVED)) {
+--- a/net/mptcp/pm_netlink.c
++++ b/net/mptcp/pm_netlink.c
+@@ -674,10 +674,12 @@ static unsigned int fill_local_addresses
+       struct mptcp_addr_info mpc_addr;
+       struct pm_nl_pernet *pernet;
+       unsigned int subflows_max;
++      bool c_flag_case;
+       int i = 0;
+       pernet = pm_nl_get_pernet_from_msk(msk);
+       subflows_max = mptcp_pm_get_subflows_max(msk);
++      c_flag_case = remote->id && mptcp_pm_add_addr_c_flag_case(msk);
+       mptcp_local_address((struct sock_common *)msk, &mpc_addr);
+@@ -690,12 +692,27 @@ static unsigned int fill_local_addresses
+                       continue;
+               if (msk->pm.subflows < subflows_max) {
++                      bool is_id0;
++
+                       locals[i].addr = entry->addr;
+                       locals[i].flags = entry->flags;
+                       locals[i].ifindex = entry->ifindex;
++                      is_id0 = mptcp_addresses_equal(&locals[i].addr,
++                                                     &mpc_addr,
++                                                     locals[i].addr.port);
++
++                      if (c_flag_case &&
++                          (entry->flags & MPTCP_PM_ADDR_FLAG_SUBFLOW)) {
++                              __clear_bit(locals[i].addr.id,
++                                          msk->pm.id_avail_bitmap);
++
++                              if (!is_id0)
++                                      msk->pm.local_addr_used++;
++                      }
++
+                       /* Special case for ID0: set the correct ID */
+-                      if (mptcp_addresses_equal(&locals[i].addr, &mpc_addr, locals[i].addr.port))
++                      if (is_id0)
+                               locals[i].addr.id = 0;
+                       msk->pm.subflows++;
+@@ -704,6 +721,37 @@ static unsigned int fill_local_addresses
+       }
+       rcu_read_unlock();
++      /* Special case: peer sets the C flag, accept one ADD_ADDR if default
++       * limits are used -- accepting no ADD_ADDR -- and use subflow endpoints
++       */
++      if (!i && c_flag_case) {
++              unsigned int local_addr_max = mptcp_pm_get_local_addr_max(msk);
++
++              while (msk->pm.local_addr_used < local_addr_max &&
++                     msk->pm.subflows < subflows_max) {
++                      struct mptcp_pm_local *local = &locals[i];
++
++                      if (!select_local_address(pernet, msk, local))
++                              break;
++
++                      __clear_bit(local->addr.id, msk->pm.id_avail_bitmap);
++
++                      if (!mptcp_pm_addr_families_match(sk, &local->addr,
++                                                        remote))
++                              continue;
++
++                      if (mptcp_addresses_equal(&local->addr, &mpc_addr,
++                                                local->addr.port))
++                              continue;
++
++                      msk->pm.local_addr_used++;
++                      msk->pm.subflows++;
++                      i++;
++              }
++
++              return i;
++      }
++
+       /* If the array is empty, fill in the single
+        * 'IPADDRANY' local address
+        */
+--- a/net/mptcp/protocol.h
++++ b/net/mptcp/protocol.h
+@@ -1172,6 +1172,14 @@ static inline void mptcp_pm_close_subflo
+       spin_unlock_bh(&msk->pm.lock);
+ }
++static inline bool mptcp_pm_add_addr_c_flag_case(struct mptcp_sock *msk)
++{
++      return READ_ONCE(msk->pm.remote_deny_join_id0) &&
++             msk->pm.local_addr_used == 0 &&
++             mptcp_pm_get_add_addr_accept_max(msk) == 0 &&
++             msk->pm.subflows < mptcp_pm_get_subflows_max(msk);
++}
++
+ void mptcp_sockopt_sync_locked(struct mptcp_sock *msk, struct sock *ssk);
+ static inline struct mptcp_ext *mptcp_get_ext(const struct sk_buff *skb)
diff --git a/queue-6.12/nfsd-don-t-use-sv_nrthreads-in-connection-limiting-calculations.patch b/queue-6.12/nfsd-don-t-use-sv_nrthreads-in-connection-limiting-calculations.patch
new file mode 100644 (file)
index 0000000..4b0b415
--- /dev/null
@@ -0,0 +1,203 @@
+From stable+bounces-185865-greg=kroah.com@vger.kernel.org Thu Oct 16 00:09:00 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Oct 2025 18:08:45 -0400
+Subject: nfsd: don't use sv_nrthreads in connection limiting calculations.
+To: stable@vger.kernel.org
+Cc: NeilBrown <neilb@suse.de>, Jeff Layton <jlayton@kernel.org>, Chuck Lever <chuck.lever@oracle.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251015220846.1531878-4-sashal@kernel.org>
+
+From: NeilBrown <neilb@suse.de>
+
+[ Upstream commit eccbbc7c00a5aae5e704d4002adfaf4c3fa4b30d ]
+
+The heuristic for limiting the number of incoming connections to nfsd
+currently uses sv_nrthreads - allowing more connections if more threads
+were configured.
+
+A future patch will allow number of threads to grow dynamically so that
+there will be no need to configure sv_nrthreads.  So we need a different
+solution for limiting connections.
+
+It isn't clear what problem is solved by limiting connections (as
+mentioned in a code comment) but the most likely problem is a connection
+storm - many connections that are not doing productive work.  These will
+be closed after about 6 minutes already but it might help to slow down a
+storm.
+
+This patch adds a per-connection flag XPT_PEER_VALID which indicates
+that the peer has presented a filehandle for which it has some sort of
+access.  i.e the peer is known to be trusted in some way.  We now only
+count connections which have NOT been determined to be valid.  There
+should be relative few of these at any given time.
+
+If the number of non-validated peer exceed a limit - currently 64 - we
+close the oldest non-validated peer to avoid having too many of these
+useless connections.
+
+Note that this patch significantly changes the meaning of the various
+configuration parameters for "max connections".  The next patch will
+remove all of these.
+
+Signed-off-by: NeilBrown <neilb@suse.de>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 898374fdd7f0 ("nfsd: unregister with rpcbind when deleting a transport")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/callback.c               |    4 ----
+ fs/nfs/callback_xdr.c           |    1 +
+ fs/nfsd/netns.h                 |    4 ++--
+ fs/nfsd/nfsfh.c                 |    2 ++
+ include/linux/sunrpc/svc.h      |    2 +-
+ include/linux/sunrpc/svc_xprt.h |   16 ++++++++++++++++
+ net/sunrpc/svc_xprt.c           |   32 ++++++++++++++++----------------
+ 7 files changed, 38 insertions(+), 23 deletions(-)
+
+--- a/fs/nfs/callback.c
++++ b/fs/nfs/callback.c
+@@ -211,10 +211,6 @@ static struct svc_serv *nfs_callback_cre
+               return ERR_PTR(-ENOMEM);
+       }
+       cb_info->serv = serv;
+-      /* As there is only one thread we need to over-ride the
+-       * default maximum of 80 connections
+-       */
+-      serv->sv_maxconn = 1024;
+       dprintk("nfs_callback_create_svc: service created\n");
+       return serv;
+ }
+--- a/fs/nfs/callback_xdr.c
++++ b/fs/nfs/callback_xdr.c
+@@ -984,6 +984,7 @@ static __be32 nfs4_callback_compound(str
+                       nfs_put_client(cps.clp);
+                       goto out_invalidcred;
+               }
++              svc_xprt_set_valid(rqstp->rq_xprt);
+       }
+       cps.minorversion = hdr_arg.minorversion;
+--- a/fs/nfsd/netns.h
++++ b/fs/nfsd/netns.h
+@@ -129,8 +129,8 @@ struct nfsd_net {
+       unsigned char writeverf[8];
+       /*
+-       * Max number of connections this nfsd container will allow. Defaults
+-       * to '0' which is means that it bases this on the number of threads.
++       * Max number of non-validated connections this nfsd container
++       * will allow.  Defaults to '0' gets mapped to 64.
+        */
+       unsigned int max_connections;
+--- a/fs/nfsd/nfsfh.c
++++ b/fs/nfsd/nfsfh.c
+@@ -382,6 +382,8 @@ __fh_verify(struct svc_rqst *rqstp,
+       if (error)
+               goto out;
++      svc_xprt_set_valid(rqstp->rq_xprt);
++
+       /* Finally, check access permissions. */
+       error = nfsd_permission(cred, exp, dentry, access);
+ out:
+--- a/include/linux/sunrpc/svc.h
++++ b/include/linux/sunrpc/svc.h
+@@ -81,7 +81,7 @@ struct svc_serv {
+       unsigned int            sv_xdrsize;     /* XDR buffer size */
+       struct list_head        sv_permsocks;   /* all permanent sockets */
+       struct list_head        sv_tempsocks;   /* all temporary sockets */
+-      int                     sv_tmpcnt;      /* count of temporary sockets */
++      int                     sv_tmpcnt;      /* count of temporary "valid" sockets */
+       struct timer_list       sv_temptimer;   /* timer for aging temporary sockets */
+       char *                  sv_name;        /* service name */
+--- a/include/linux/sunrpc/svc_xprt.h
++++ b/include/linux/sunrpc/svc_xprt.h
+@@ -99,8 +99,24 @@ enum {
+       XPT_HANDSHAKE,          /* xprt requests a handshake */
+       XPT_TLS_SESSION,        /* transport-layer security established */
+       XPT_PEER_AUTH,          /* peer has been authenticated */
++      XPT_PEER_VALID,         /* peer has presented a filehandle that
++                               * it has access to.  It is NOT counted
++                               * in ->sv_tmpcnt.
++                               */
+ };
++static inline void svc_xprt_set_valid(struct svc_xprt *xpt)
++{
++      if (test_bit(XPT_TEMP, &xpt->xpt_flags) &&
++          !test_and_set_bit(XPT_PEER_VALID, &xpt->xpt_flags)) {
++              struct svc_serv *serv = xpt->xpt_server;
++
++              spin_lock(&serv->sv_lock);
++              serv->sv_tmpcnt -= 1;
++              spin_unlock(&serv->sv_lock);
++      }
++}
++
+ static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
+ {
+       spin_lock(&xpt->xpt_lock);
+--- a/net/sunrpc/svc_xprt.c
++++ b/net/sunrpc/svc_xprt.c
+@@ -606,7 +606,8 @@ int svc_port_is_privileged(struct sockad
+ }
+ /*
+- * Make sure that we don't have too many active connections. If we have,
++ * Make sure that we don't have too many connections that have not yet
++ * demonstrated that they have access to the NFS server. If we have,
+  * something must be dropped. It's not clear what will happen if we allow
+  * "too many" connections, but when dealing with network-facing software,
+  * we have to code defensively. Here we do that by imposing hard limits.
+@@ -625,27 +626,25 @@ int svc_port_is_privileged(struct sockad
+  */
+ static void svc_check_conn_limits(struct svc_serv *serv)
+ {
+-      unsigned int limit = serv->sv_maxconn ? serv->sv_maxconn :
+-                              (serv->sv_nrthreads+3) * 20;
++      unsigned int limit = serv->sv_maxconn ? serv->sv_maxconn : 64;
+       if (serv->sv_tmpcnt > limit) {
+-              struct svc_xprt *xprt = NULL;
++              struct svc_xprt *xprt = NULL, *xprti;
+               spin_lock_bh(&serv->sv_lock);
+               if (!list_empty(&serv->sv_tempsocks)) {
+-                      /* Try to help the admin */
+-                      net_notice_ratelimited("%s: too many open connections, consider increasing the %s\n",
+-                                             serv->sv_name, serv->sv_maxconn ?
+-                                             "max number of connections" :
+-                                             "number of threads");
+                       /*
+                        * Always select the oldest connection. It's not fair,
+-                       * but so is life
++                       * but nor is life.
+                        */
+-                      xprt = list_entry(serv->sv_tempsocks.prev,
+-                                        struct svc_xprt,
+-                                        xpt_list);
+-                      set_bit(XPT_CLOSE, &xprt->xpt_flags);
+-                      svc_xprt_get(xprt);
++                      list_for_each_entry_reverse(xprti, &serv->sv_tempsocks,
++                                                  xpt_list) {
++                              if (!test_bit(XPT_PEER_VALID, &xprti->xpt_flags)) {
++                                      xprt = xprti;
++                                      set_bit(XPT_CLOSE, &xprt->xpt_flags);
++                                      svc_xprt_get(xprt);
++                                      break;
++                              }
++                      }
+               }
+               spin_unlock_bh(&serv->sv_lock);
+@@ -1039,7 +1038,8 @@ static void svc_delete_xprt(struct svc_x
+       spin_lock_bh(&serv->sv_lock);
+       list_del_init(&xprt->xpt_list);
+-      if (test_bit(XPT_TEMP, &xprt->xpt_flags))
++      if (test_bit(XPT_TEMP, &xprt->xpt_flags) &&
++          !test_bit(XPT_PEER_VALID, &xprt->xpt_flags))
+               serv->sv_tmpcnt--;
+       spin_unlock_bh(&serv->sv_lock);
diff --git a/queue-6.12/nfsd-fix-nfsd_may_bypass_gss-and-nfsd_may_bypass_gss_on_root.patch b/queue-6.12/nfsd-fix-nfsd_may_bypass_gss-and-nfsd_may_bypass_gss_on_root.patch
new file mode 100644 (file)
index 0000000..0866ce0
--- /dev/null
@@ -0,0 +1,179 @@
+From stable+bounces-185862-greg=kroah.com@vger.kernel.org Thu Oct 16 00:08:56 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Oct 2025 18:08:42 -0400
+Subject: nfsd: Fix NFSD_MAY_BYPASS_GSS and NFSD_MAY_BYPASS_GSS_ON_ROOT
+To: stable@vger.kernel.org
+Cc: "Pali Rohár" <pali@kernel.org>, NeilBrown <neilb@suse.de>, "Chuck Lever" <chuck.lever@oracle.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20251015220846.1531878-1-sashal@kernel.org>
+
+From: Pali Rohár <pali@kernel.org>
+
+[ Upstream commit bb4f07f2409c26c01e97e6f9b432545f353e3b66 ]
+
+Currently NFSD_MAY_BYPASS_GSS and NFSD_MAY_BYPASS_GSS_ON_ROOT do not bypass
+only GSS, but bypass any method. This is a problem specially for NFS3
+AUTH_NULL-only exports.
+
+The purpose of NFSD_MAY_BYPASS_GSS_ON_ROOT is described in RFC 2623,
+section 2.3.2, to allow mounting NFS2/3 GSS-only export without
+authentication. So few procedures which do not expose security risk used
+during mount time can be called also with AUTH_NONE or AUTH_SYS, to allow
+client mount operation to finish successfully.
+
+The problem with current implementation is that for AUTH_NULL-only exports,
+the NFSD_MAY_BYPASS_GSS_ON_ROOT is active also for NFS3 AUTH_UNIX mount
+attempts which confuse NFS3 clients, and make them think that AUTH_UNIX is
+enabled and is working. Linux NFS3 client never switches from AUTH_UNIX to
+AUTH_NONE on active mount, which makes the mount inaccessible.
+
+Fix the NFSD_MAY_BYPASS_GSS and NFSD_MAY_BYPASS_GSS_ON_ROOT implementation
+and really allow to bypass only exports which have enabled some real
+authentication (GSS, TLS, or any other).
+
+The result would be: For AUTH_NULL-only export if client attempts to do
+mount with AUTH_UNIX flavor then it will receive access errors, which
+instruct client that AUTH_UNIX flavor is not usable and will either try
+other auth flavor (AUTH_NULL if enabled) or fails mount procedure.
+Similarly if client attempt to do mount with AUTH_NULL flavor and only
+AUTH_UNIX flavor is enabled then the client will receive access error.
+
+This should fix problems with AUTH_NULL-only or AUTH_UNIX-only exports if
+client attempts to mount it with other auth flavor (e.g. with AUTH_NULL for
+AUTH_UNIX-only export, or with AUTH_UNIX for AUTH_NULL-only export).
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Reviewed-by: NeilBrown <neilb@suse.de>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 898374fdd7f0 ("nfsd: unregister with rpcbind when deleting a transport")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfsd/export.c   |   21 ++++++++++++++++++++-
+ fs/nfsd/export.h   |    3 ++-
+ fs/nfsd/nfs4proc.c |    2 +-
+ fs/nfsd/nfs4xdr.c  |    2 +-
+ fs/nfsd/nfsfh.c    |    9 ++++++---
+ fs/nfsd/vfs.c      |    2 +-
+ 6 files changed, 31 insertions(+), 8 deletions(-)
+
+--- a/fs/nfsd/export.c
++++ b/fs/nfsd/export.c
+@@ -1078,12 +1078,14 @@ static struct svc_export *exp_find(struc
+  * check_nfsd_access - check if access to export is allowed.
+  * @exp: svc_export that is being accessed.
+  * @rqstp: svc_rqst attempting to access @exp (will be NULL for LOCALIO).
++ * @may_bypass_gss: reduce strictness of authorization check
+  *
+  * Return values:
+  *   %nfs_ok if access is granted, or
+  *   %nfserr_wrongsec if access is denied
+  */
+-__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
++__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
++                       bool may_bypass_gss)
+ {
+       struct exp_flavor_info *f, *end = exp->ex_flavors + exp->ex_nflavors;
+       struct svc_xprt *xprt;
+@@ -1140,6 +1142,23 @@ ok:
+       if (nfsd4_spo_must_allow(rqstp))
+               return nfs_ok;
++      /* Some calls may be processed without authentication
++       * on GSS exports. For example NFS2/3 calls on root
++       * directory, see section 2.3.2 of rfc 2623.
++       * For "may_bypass_gss" check that export has really
++       * enabled some flavor with authentication (GSS or any
++       * other) and also check that the used auth flavor is
++       * without authentication (none or sys).
++       */
++      if (may_bypass_gss && (
++           rqstp->rq_cred.cr_flavor == RPC_AUTH_NULL ||
++           rqstp->rq_cred.cr_flavor == RPC_AUTH_UNIX)) {
++              for (f = exp->ex_flavors; f < end; f++) {
++                      if (f->pseudoflavor >= RPC_AUTH_DES)
++                              return 0;
++              }
++      }
++
+ denied:
+       return nfserr_wrongsec;
+ }
+--- a/fs/nfsd/export.h
++++ b/fs/nfsd/export.h
+@@ -101,7 +101,8 @@ struct svc_expkey {
+ struct svc_cred;
+ int nfsexp_flags(struct svc_cred *cred, struct svc_export *exp);
+-__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp);
++__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
++                       bool may_bypass_gss);
+ /*
+  * Function declarations
+--- a/fs/nfsd/nfs4proc.c
++++ b/fs/nfsd/nfs4proc.c
+@@ -2799,7 +2799,7 @@ nfsd4_proc_compound(struct svc_rqst *rqs
+                       if (current_fh->fh_export &&
+                                       need_wrongsec_check(rqstp))
+-                              op->status = check_nfsd_access(current_fh->fh_export, rqstp);
++                              op->status = check_nfsd_access(current_fh->fh_export, rqstp, false);
+               }
+ encode_op:
+               if (op->status == nfserr_replay_me) {
+--- a/fs/nfsd/nfs4xdr.c
++++ b/fs/nfsd/nfs4xdr.c
+@@ -3784,7 +3784,7 @@ nfsd4_encode_entry4_fattr(struct nfsd4_r
+                       nfserr = nfserrno(err);
+                       goto out_put;
+               }
+-              nfserr = check_nfsd_access(exp, cd->rd_rqstp);
++              nfserr = check_nfsd_access(exp, cd->rd_rqstp, false);
+               if (nfserr)
+                       goto out_put;
+--- a/fs/nfsd/nfsfh.c
++++ b/fs/nfsd/nfsfh.c
+@@ -320,6 +320,7 @@ __fh_verify(struct svc_rqst *rqstp,
+ {
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+       struct svc_export *exp = NULL;
++      bool may_bypass_gss = false;
+       struct dentry   *dentry;
+       __be32          error;
+@@ -367,8 +368,10 @@ __fh_verify(struct svc_rqst *rqstp,
+        * which clients virtually always use auth_sys for,
+        * even while using RPCSEC_GSS for NFS.
+        */
+-      if (access & NFSD_MAY_LOCK || access & NFSD_MAY_BYPASS_GSS)
++      if (access & NFSD_MAY_LOCK)
+               goto skip_pseudoflavor_check;
++      if (access & NFSD_MAY_BYPASS_GSS)
++              may_bypass_gss = true;
+       /*
+        * Clients may expect to be able to use auth_sys during mount,
+        * even if they use gss for everything else; see section 2.3.2
+@@ -376,9 +379,9 @@ __fh_verify(struct svc_rqst *rqstp,
+        */
+       if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT
+                       && exp->ex_path.dentry == dentry)
+-              goto skip_pseudoflavor_check;
++              may_bypass_gss = true;
+-      error = check_nfsd_access(exp, rqstp);
++      error = check_nfsd_access(exp, rqstp, may_bypass_gss);
+       if (error)
+               goto out;
+--- a/fs/nfsd/vfs.c
++++ b/fs/nfsd/vfs.c
+@@ -321,7 +321,7 @@ nfsd_lookup(struct svc_rqst *rqstp, stru
+       err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry);
+       if (err)
+               return err;
+-      err = check_nfsd_access(exp, rqstp);
++      err = check_nfsd_access(exp, rqstp, false);
+       if (err)
+               goto out;
+       /*
diff --git a/queue-6.12/nfsd-refine-and-rename-nfsd_may_lock.patch b/queue-6.12/nfsd-refine-and-rename-nfsd_may_lock.patch
new file mode 100644 (file)
index 0000000..e65e63c
--- /dev/null
@@ -0,0 +1,144 @@
+From stable+bounces-185864-greg=kroah.com@vger.kernel.org Thu Oct 16 00:08:57 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Oct 2025 18:08:44 -0400
+Subject: nfsd: refine and rename NFSD_MAY_LOCK
+To: stable@vger.kernel.org
+Cc: NeilBrown <neilb@suse.de>, Chuck Lever <chuck.lever@oracle.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251015220846.1531878-3-sashal@kernel.org>
+
+From: NeilBrown <neilb@suse.de>
+
+[ Upstream commit 4cc9b9f2bf4dfe13fe573da978e626e2248df388 ]
+
+NFSD_MAY_LOCK means a few different things.
+- it means that GSS is not required.
+- it means that with NFSEXP_NOAUTHNLM, authentication is not required
+- it means that OWNER_OVERRIDE is allowed.
+
+None of these are specific to locking, they are specific to the NLM
+protocol.
+So:
+ - rename to NFSD_MAY_NLM
+ - set NFSD_MAY_OWNER_OVERRIDE and NFSD_MAY_BYPASS_GSS in nlm_fopen()
+   so that NFSD_MAY_NLM doesn't need to imply these.
+ - move the test on NFSEXP_NOAUTHNLM out of nfsd_permission() and
+   into fh_verify where other special-case tests on the MAY flags
+   happen.  nfsd_permission() can be called from other places than
+   fh_verify(), but none of these will have NFSD_MAY_NLM.
+
+Signed-off-by: NeilBrown <neilb@suse.de>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 898374fdd7f0 ("nfsd: unregister with rpcbind when deleting a transport")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfsd/lockd.c |   13 +++++++++++--
+ fs/nfsd/nfsfh.c |   12 ++++--------
+ fs/nfsd/trace.h |    2 +-
+ fs/nfsd/vfs.c   |   12 +-----------
+ fs/nfsd/vfs.h   |    2 +-
+ 5 files changed, 18 insertions(+), 23 deletions(-)
+
+--- a/fs/nfsd/lockd.c
++++ b/fs/nfsd/lockd.c
+@@ -38,11 +38,20 @@ nlm_fopen(struct svc_rqst *rqstp, struct
+       memcpy(&fh.fh_handle.fh_raw, f->data, f->size);
+       fh.fh_export = NULL;
++      /*
++       * Allow BYPASS_GSS as some client implementations use AUTH_SYS
++       * for NLM even when GSS is used for NFS.
++       * Allow OWNER_OVERRIDE as permission might have been changed
++       * after the file was opened.
++       * Pass MAY_NLM so that authentication can be completely bypassed
++       * if NFSEXP_NOAUTHNLM is set.  Some older clients use AUTH_NULL
++       * for NLM requests.
++       */
+       access = (mode == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ;
+-      access |= NFSD_MAY_LOCK;
++      access |= NFSD_MAY_NLM | NFSD_MAY_OWNER_OVERRIDE | NFSD_MAY_BYPASS_GSS;
+       nfserr = nfsd_open(rqstp, &fh, S_IFREG, access, filp);
+       fh_put(&fh);
+-      /* We return nlm error codes as nlm doesn't know
++      /* We return nlm error codes as nlm doesn't know
+        * about nfsd, but nfsd does know about nlm..
+        */
+       switch (nfserr) {
+--- a/fs/nfsd/nfsfh.c
++++ b/fs/nfsd/nfsfh.c
+@@ -363,13 +363,10 @@ __fh_verify(struct svc_rqst *rqstp,
+       if (error)
+               goto out;
+-      /*
+-       * pseudoflavor restrictions are not enforced on NLM,
+-       * which clients virtually always use auth_sys for,
+-       * even while using RPCSEC_GSS for NFS.
+-       */
+-      if (access & NFSD_MAY_LOCK)
+-              goto skip_pseudoflavor_check;
++      if ((access & NFSD_MAY_NLM) && (exp->ex_flags & NFSEXP_NOAUTHNLM))
++              /* NLM is allowed to fully bypass authentication */
++              goto out;
++
+       if (access & NFSD_MAY_BYPASS_GSS)
+               may_bypass_gss = true;
+       /*
+@@ -385,7 +382,6 @@ __fh_verify(struct svc_rqst *rqstp,
+       if (error)
+               goto out;
+-skip_pseudoflavor_check:
+       /* Finally, check access permissions. */
+       error = nfsd_permission(cred, exp, dentry, access);
+ out:
+--- a/fs/nfsd/trace.h
++++ b/fs/nfsd/trace.h
+@@ -79,7 +79,7 @@ DEFINE_NFSD_XDR_ERR_EVENT(cant_encode);
+               { NFSD_MAY_READ,                "READ" },               \
+               { NFSD_MAY_SATTR,               "SATTR" },              \
+               { NFSD_MAY_TRUNC,               "TRUNC" },              \
+-              { NFSD_MAY_LOCK,                "LOCK" },               \
++              { NFSD_MAY_NLM,                 "NLM" },                \
+               { NFSD_MAY_OWNER_OVERRIDE,      "OWNER_OVERRIDE" },     \
+               { NFSD_MAY_LOCAL_ACCESS,        "LOCAL_ACCESS" },       \
+               { NFSD_MAY_BYPASS_GSS_ON_ROOT,  "BYPASS_GSS_ON_ROOT" }, \
+--- a/fs/nfsd/vfs.c
++++ b/fs/nfsd/vfs.c
+@@ -2519,7 +2519,7 @@ nfsd_permission(struct svc_cred *cred, s
+               (acc & NFSD_MAY_EXEC)?  " exec"  : "",
+               (acc & NFSD_MAY_SATTR)? " sattr" : "",
+               (acc & NFSD_MAY_TRUNC)? " trunc" : "",
+-              (acc & NFSD_MAY_LOCK)?  " lock"  : "",
++              (acc & NFSD_MAY_NLM)?   " nlm"  : "",
+               (acc & NFSD_MAY_OWNER_OVERRIDE)? " owneroverride" : "",
+               inode->i_mode,
+               IS_IMMUTABLE(inode)?    " immut" : "",
+@@ -2544,16 +2544,6 @@ nfsd_permission(struct svc_cred *cred, s
+       if ((acc & NFSD_MAY_TRUNC) && IS_APPEND(inode))
+               return nfserr_perm;
+-      if (acc & NFSD_MAY_LOCK) {
+-              /* If we cannot rely on authentication in NLM requests,
+-               * just allow locks, otherwise require read permission, or
+-               * ownership
+-               */
+-              if (exp->ex_flags & NFSEXP_NOAUTHNLM)
+-                      return 0;
+-              else
+-                      acc = NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE;
+-      }
+       /*
+        * The file owner always gets access permission for accesses that
+        * would normally be checked at open time. This is to make
+--- a/fs/nfsd/vfs.h
++++ b/fs/nfsd/vfs.h
+@@ -20,7 +20,7 @@
+ #define NFSD_MAY_READ                 0x004 /* == MAY_READ */
+ #define NFSD_MAY_SATTR                        0x008
+ #define NFSD_MAY_TRUNC                        0x010
+-#define NFSD_MAY_LOCK                 0x020
++#define NFSD_MAY_NLM                  0x020 /* request is from lockd */
+ #define NFSD_MAY_MASK                 0x03f
+ /* extra hints to permission and open routines: */
diff --git a/queue-6.12/nfsd-replace-use-of-nfsd_may_lock-in-nfsd4_lock.patch b/queue-6.12/nfsd-replace-use-of-nfsd_may_lock-in-nfsd4_lock.patch
new file mode 100644 (file)
index 0000000..77a7958
--- /dev/null
@@ -0,0 +1,45 @@
+From stable+bounces-185863-greg=kroah.com@vger.kernel.org Thu Oct 16 00:08:56 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Oct 2025 18:08:43 -0400
+Subject: NFSD: Replace use of NFSD_MAY_LOCK in nfsd4_lock()
+To: stable@vger.kernel.org
+Cc: Chuck Lever <chuck.lever@oracle.com>, NeilBrown <neilb@suse.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251015220846.1531878-2-sashal@kernel.org>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 6640556b0c80edc66d6f50abe53f00311a873536 ]
+
+NFSv4 LOCK operations should not avoid the set of authorization
+checks that apply to all other NFSv4 operations. Also, the
+"no_auth_nlm" export option should apply only to NLM LOCK requests.
+It's not necessary or sensible to apply it to NFSv4 LOCK operations.
+
+Instead, set no permission bits when calling fh_verify(). Subsequent
+stateid processing handles authorization checks.
+
+Reported-by: NeilBrown <neilb@suse.de>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 898374fdd7f0 ("nfsd: unregister with rpcbind when deleting a transport")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfsd/nfs4state.c |    6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+--- a/fs/nfsd/nfs4state.c
++++ b/fs/nfsd/nfs4state.c
+@@ -7998,11 +7998,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struc
+       if (check_lock_length(lock->lk_offset, lock->lk_length))
+                return nfserr_inval;
+-      if ((status = fh_verify(rqstp, &cstate->current_fh,
+-                              S_IFREG, NFSD_MAY_LOCK))) {
+-              dprintk("NFSD: nfsd4_lock: permission denied!\n");
++      status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0);
++      if (status != nfs_ok)
+               return status;
+-      }
+       sb = cstate->current_fh.fh_dentry->d_sb;
+       if (lock->lk_is_new) {
diff --git a/queue-6.12/nfsd-unregister-with-rpcbind-when-deleting-a-transport.patch b/queue-6.12/nfsd-unregister-with-rpcbind-when-deleting-a-transport.patch
new file mode 100644 (file)
index 0000000..eecbf70
--- /dev/null
@@ -0,0 +1,88 @@
+From stable+bounces-185866-greg=kroah.com@vger.kernel.org Thu Oct 16 00:09:01 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Oct 2025 18:08:46 -0400
+Subject: nfsd: unregister with rpcbind when deleting a transport
+To: stable@vger.kernel.org
+Cc: Olga Kornievskaia <okorniev@redhat.com>, Chuck Lever <chuck.lever@oracle.com>, Jeff Layton <jlayton@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251015220846.1531878-5-sashal@kernel.org>
+
+From: Olga Kornievskaia <okorniev@redhat.com>
+
+[ Upstream commit 898374fdd7f06fa4c4a66e8be3135efeae6128d5 ]
+
+When a listener is added, a part of creation of transport also registers
+program/port with rpcbind. However, when the listener is removed,
+while transport goes away, rpcbind still has the entry for that
+port/type.
+
+When deleting the transport, unregister with rpcbind when appropriate.
+
+---v2 created a new xpt_flag XPT_RPCB_UNREG to mark TCP and UDP
+transport and at xprt destroy send rpcbind unregister if flag set.
+
+Suggested-by: Chuck Lever <chuck.lever@oracle.com>
+Fixes: d093c9089260 ("nfsd: fix management of listener transports")
+Cc: stable@vger.kernel.org
+Signed-off-by: Olga Kornievskaia <okorniev@redhat.com>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/sunrpc/svc_xprt.h |    3 +++
+ net/sunrpc/svc_xprt.c           |   13 +++++++++++++
+ net/sunrpc/svcsock.c            |    2 ++
+ 3 files changed, 18 insertions(+)
+
+--- a/include/linux/sunrpc/svc_xprt.h
++++ b/include/linux/sunrpc/svc_xprt.h
+@@ -103,6 +103,9 @@ enum {
+                                * it has access to.  It is NOT counted
+                                * in ->sv_tmpcnt.
+                                */
++      XPT_RPCB_UNREG,         /* transport that needs unregistering
++                               * with rpcbind (TCP, UDP) on destroy
++                               */
+ };
+ static inline void svc_xprt_set_valid(struct svc_xprt *xpt)
+--- a/net/sunrpc/svc_xprt.c
++++ b/net/sunrpc/svc_xprt.c
+@@ -1028,6 +1028,19 @@ static void svc_delete_xprt(struct svc_x
+       struct svc_serv *serv = xprt->xpt_server;
+       struct svc_deferred_req *dr;
++      /* unregister with rpcbind for when transport type is TCP or UDP.
++       */
++      if (test_bit(XPT_RPCB_UNREG, &xprt->xpt_flags)) {
++              struct svc_sock *svsk = container_of(xprt, struct svc_sock,
++                                                   sk_xprt);
++              struct socket *sock = svsk->sk_sock;
++
++              if (svc_register(serv, xprt->xpt_net, sock->sk->sk_family,
++                               sock->sk->sk_protocol, 0) < 0)
++                      pr_warn("failed to unregister %s with rpcbind\n",
++                              xprt->xpt_class->xcl_name);
++      }
++
+       if (test_and_set_bit(XPT_DEAD, &xprt->xpt_flags))
+               return;
+--- a/net/sunrpc/svcsock.c
++++ b/net/sunrpc/svcsock.c
+@@ -837,6 +837,7 @@ static void svc_udp_init(struct svc_sock
+       /* data might have come in before data_ready set up */
+       set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
+       set_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags);
++      set_bit(XPT_RPCB_UNREG, &svsk->sk_xprt.xpt_flags);
+       /* make sure we get destination address info */
+       switch (svsk->sk_sk->sk_family) {
+@@ -1357,6 +1358,7 @@ static void svc_tcp_init(struct svc_sock
+       if (sk->sk_state == TCP_LISTEN) {
+               strcpy(svsk->sk_xprt.xpt_remotebuf, "listener");
+               set_bit(XPT_LISTENER, &svsk->sk_xprt.xpt_flags);
++              set_bit(XPT_RPCB_UNREG, &svsk->sk_xprt.xpt_flags);
+               sk->sk_data_ready = svc_tcp_listen_data_ready;
+               set_bit(XPT_CONN, &svsk->sk_xprt.xpt_flags);
+       } else {
index 7ad2e113d9c07db1b78746df3ad3b002d17a5701..15bdf4ad91688b9ae2667aac84d9438d68eb092d 100644 (file)
@@ -240,3 +240,18 @@ statmount-don-t-call-path_put-under-namespace-semaphore.patch
 arm64-mte-do-not-flag-the-zero-page-as-pg_mte_tagged.patch
 x86-mtrr-rename-mtrr_overwrite_state-to-guest_force_mtrr_state.patch
 x86-kvm-force-legacy-pci-hole-to-uc-when-overriding-mtrrs-for-tdx-snp.patch
+nfsd-fix-nfsd_may_bypass_gss-and-nfsd_may_bypass_gss_on_root.patch
+nfsd-replace-use-of-nfsd_may_lock-in-nfsd4_lock.patch
+nfsd-refine-and-rename-nfsd_may_lock.patch
+nfsd-don-t-use-sv_nrthreads-in-connection-limiting-calculations.patch
+nfsd-unregister-with-rpcbind-when-deleting-a-transport.patch
+acpi-battery-allocate-driver-data-through-devm_-apis.patch
+acpi-battery-initialize-mutexes-through-devm_-apis.patch
+acpi-battery-check-for-error-code-from-devm_mutex_init-call.patch
+acpi-battery-add-synchronization-between-interface-updates.patch
+acpi-property-disregard-references-in-data-only-subnode-lists.patch
+acpi-property-add-code-comments-explaining-what-is-going-on.patch
+acpi-property-do-not-pass-null-handles-to-acpi_attach_data.patch
+mptcp-pm-in-kernel-usable-client-side-with-c-flag.patch
+ipmi-rework-user-message-limit-handling.patch
+ipmi-fix-handling-of-messages-with-provided-receive-message-pointer.patch