* [PATCH 6.18 000/109] 6.18.60-rc1 review
@ 2026-10-28 16:59 Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 001/109] eventfs: fix SRCU read lock leak in eventfs_iterate() Greg Kroah-Hartman
` (115 more replies)
0 siblings, 116 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, linux-kernel, torvalds, akpm, linux,
shuah, patches, lkft-triage, pavel, jonathanh, f.fainelli,
sudipm.mukherjee, rwarsow, conor, hargar, broonie, achill, sr
This is the start of the stable review cycle for the 6.18.60 release.
There are 109 patches in this series, all will be posted as a response
to this one. If anyone has any issues with these being applied, please
let me know.
Responses should be made by Fri, 30 Oct 2026 16:59:22 +0000.
Anything received after that time might be too late.
The whole patch series can be found in one patch at:
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.60-rc1.gz
or in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git linux-6.18.y
and the diffstat can be found below.
thanks,
greg k-h
-------------
Pseudo-Shortlog of commits:
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Linux 6.18.60-rc1
Rafael J. Wysocki <rafael.j.wysocki@intel.com>
ACPI: scan: Avoid registering platform devices with resource overlaps
Christian Brauner <brauner@kernel.org>
signal: allow taks to temporarily block TIF_NOTIFY_SIGNAL
Christian Brauner <brauner@kernel.org>
pid_namespace: prevent TIF_NOTIFY_SIGNAL from interrupting the reaper
Christian Brauner <brauner@kernel.org>
exit: hang up the tty before closing the files
Christian Brauner <brauner@kernel.org>
fork: release the files of a failed fork after sched_cancel_fork()
Christian Brauner <brauner@kernel.org>
ptrace: refuse to change the signal mask of a user worker
Christian Brauner <brauner@kernel.org>
signal: don't retarget shared signals in a dying thread group
Christian Brauner <brauner@kernel.org>
signal: only SIGKILL and the freezers interrupt a coredumping task
Christian Brauner <brauner@kernel.org>
coredump: hold RCU while releasing parked threads
Aaron Ahmed <aarnahmd@amazon.com>
ftrace: Initialize modname and offset in print_rec()
Weiming Shi <bestswngs@gmail.com>
asymmetric_keys: reject trust keys without IDs
Srish Srinivasan <ssrish@linux.ibm.com>
keys/trusted_keys: return immediately after TPM unseal failure
Stefano Garzarella <sgarzare@redhat.com>
KEYS: trusted: Fix blob allocation size in tpm2_key_decode()
Adrian Martinez <adrimg3196@gmail.com>
KEYS: Fix key_reject_and_link() race with keyring restriction
Cen Zhang <zzzccc427@gmail.com>
keys: Protect the read method check against type removal
Kyle Zeng <kylebot@openai.com>
keys: make keyring indexes independent of kernel addresses
Cen Zhang <zzzccc427@gmail.com>
keys: Protect the type name while describing a key
Cen Zhang <zzzccc427@gmail.com>
keys: Check the authorization payload in process keyring searches
Cen Zhang <zzzccc427@gmail.com>
keys: Avoid the owner account dereference in named keyring lookup
Jens Axboe <axboe@kernel.dk>
io_uring: keep the drain active when a deferred drain request is started
Jens Axboe <axboe@kernel.dk>
io_uring: end the drain when the drain request completes
Junyeong Lee <jylee5498@gmail.com>
io_uring/waitid: prevent cancel reference wrap
Austin Schuh <austin.linux@gmail.com>
io_uring/wait: wake real-time waiters from hard interrupt context
Tamaki Yanagawa <ty@000ty.net>
io_uring/net: keep recv buffer select length within MAX_RW_COUNT
Jens Axboe <axboe@kernel.dk>
io_uring/poll: don't loop on HUP with multiple references
Jens Axboe <axboe@kernel.dk>
io_uring/timeout: don't lose a cancel as io-wq arms a timeout
Jens Axboe <axboe@kernel.dk>
io_uring/timeout: don't flush with the sequence behind the last flush
Jens Axboe <axboe@kernel.dk>
io_uring/timeout: don't put a sequence target behind the last flush
Juanlu Herrero <juanlu@fastmail.com>
io_uring/poll: avoid retry wakeup loss when dropping ownership
Josef Bacik <josef@toxicpanda.com>
io_uring: look up the file before queueing to io-wq
Josef Bacik <josef@toxicpanda.com>
io_uring/timeout: don't end a linked timeout with 1
Josef Bacik <josef@toxicpanda.com>
io_uring/timeout: no ABS update of multishot timeouts
Josef Bacik <josef@toxicpanda.com>
io_uring/net: don't retry recv on uncommitted buffers
Josef Bacik <josef@toxicpanda.com>
io_uring: don't pass restart error codes to userspace
Josef Bacik <josef@toxicpanda.com>
io_uring/openclose: keep the lock in io_pipe_fixed()
Josef Bacik <josef@toxicpanda.com>
io_uring/sqpoll: let the thread sleep on disabled rings
Josef Bacik <josef@toxicpanda.com>
io_uring/kbuf: handle index wrap in last buffer check
Josef Bacik <josef@toxicpanda.com>
io_uring/zcrx: fix buffer offsets in areas above 4GB
Josef Bacik <josef@toxicpanda.com>
io_uring/napi: don't busy poll past an expired timeout
Josef Bacik <josef@toxicpanda.com>
io_uring/napi: make stale entries time out after 60s
Josef Bacik <josef@toxicpanda.com>
io_uring/wait: no early -ETIME if min_wait finds work
Josef Bacik <josef@toxicpanda.com>
io_uring/timeout: keep repeat count on multishot update
Josef Bacik <josef@toxicpanda.com>
io_uring/timeout: don't skip over reached sequences
Josef Bacik <josef@toxicpanda.com>
io_uring/fdinfo: fix CQE dump of CQE32 rings
Josef Bacik <josef@toxicpanda.com>
io_uring/register: handle index wrap when resizing
Josef Bacik <josef@toxicpanda.com>
io_uring/register: don't wrap 32b CQEs when resizing
Josef Bacik <josef@toxicpanda.com>
io_uring: drop the CQE cache if a refill fails
Josef Bacik <josef@toxicpanda.com>
io_uring/futex: allocate private hash before waiting
Josef Bacik <josef@toxicpanda.com>
io_uring/poll: drop stale double poll entry on rearm
Josef Bacik <josef@toxicpanda.com>
io_uring/wait: look at all CQ waiters on wake up
Josef Bacik <josef@toxicpanda.com>
io_uring: wake ring pollers for local task_work
Josef Bacik <josef@toxicpanda.com>
io_uring/cancel: let CANCEL_ALL find parked requests
Josef Bacik <josef@toxicpanda.com>
io_uring/cancel: recognize "no timeout" on 32-bit
Josef Bacik <josef@toxicpanda.com>
io_uring/cancel: don't forget that the work was running
Josef Bacik <josef@toxicpanda.com>
io_uring: don't lose a cancel as io-wq parks a request
Josef Bacik <josef@toxicpanda.com>
io_uring/poll: don't lose a cancel as io-wq arms poll
Josef Bacik <josef@toxicpanda.com>
io_uring/io-wq: let cancel find picked up hashed work
Josef Bacik <josef@toxicpanda.com>
io_uring/io-wq: don't exit with a creation pending
Josef Bacik <josef@toxicpanda.com>
io_uring/io-wq: don't strand work when a worker exits
Josef Bacik <josef@toxicpanda.com>
io_uring/io-wq: don't go to sleep on pending work
Josef Bacik <josef@toxicpanda.com>
io_uring: don't flood a cancelling task with task_work
Josef Bacik <josef@toxicpanda.com>
io_uring/io-wq: don't hide a link from cancellations
Josef Bacik <josef@toxicpanda.com>
io_uring/io-wq: don't lose the signal that ends work
Josef Bacik <josef@toxicpanda.com>
io_uring/wait: don't spin on a stuck overflow list
Josef Bacik <josef@toxicpanda.com>
io_uring/cmd: don't leave list poison in ->hash_node
Josef Bacik <josef@toxicpanda.com>
io_uring/waitid: own the request before clearing ->head
Christian Brauner <brauner@kernel.org>
io-wq: order the exit bit against worker creation task work
Zihan Xi <zihanx@nebusec.ai>
net: gso: limit recursive IP-in-IP segmentation
Lee Jones <lee@kernel.org>
tracing/histograms: Verify event_call identity when updating synth_event refcount
Donggeun Yoo <donggeunyoo.kernel@gmail.com>
tracing: Fix 64-bit and signed histogram fields on 32-bit kernels
Donggeun Yoo <donggeunyoo.kernel@gmail.com>
tracing: Unregister field variable histograms when the command fails
Donggeun Yoo <donggeunyoo.kernel@gmail.com>
tracing: Save the event file instead of the compatible histogram
Kyle Zeng <kylebot@openai.com>
tracing: Reject incomplete event filter expressions
David Carlier <devnexen@gmail.com>
tracing: Hold trace_types_lock when resetting last boot data from readers
David Carlier <devnexen@gmail.com>
tracing: Hold trace_types_lock when updating modules in persistent instances
Pauli Virtanen <pav@iki.fi>
Bluetooth: L2CAP: refuse __l2cap_chan_add if chan already has conn
Jonas Köppeler <j.koeppeler@tu-berlin.de>
net_sched: codel: fix stale state for empty flows in fq_codel
SJ Park <sj@kernel.org>
mm/damon/core: error damos_commit_quota_goal() for zero target_value
Damien Le Moal <dlemoal@kernel.org>
block: avoid potential deadlock on zone revalidation failure
Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25: don't free an incoming SABM skb left on the listener's queue
Bernard Pidoux <bernard.f6bvp@gmail.com>
rose: bring up the link of the preferred route passed over by a connect request
Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25: send unacknowledged I frames again when a link is re-established
Bernard Pidoux <bernard.f6bvp@gmail.com>
rose: clear the circuits of a neighbour that restarts the link
Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25: send a private copy of each I frame
Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25: don't accept frames on a device that is going down
Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25: wait for ax25_send_frame() before detaching the connections of a device
Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25: read the connection's ax25_dev only once when walking ax25_list
Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25: don't create two connections for the same link in ax25_send_frame()
Bernard Pidoux <bernard.f6bvp@gmail.com>
rose: process received frames and send with the socket locked
Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25: serialise the transmission of I frames on a connection
Bernard Pidoux <bernard.f6bvp@gmail.com>
rose: restart the link of a neighbour created over a live AX.25 connection
Bernard Pidoux <bernard.f6bvp@gmail.com>
rose: clear through routes when a neighbour is removed from the tables
Bernard Pidoux <bernard.f6bvp@gmail.com>
netrom: fix nr_node leak when nr_del_node() removes the last route
Bernard Pidoux <bernard.f6bvp@gmail.com>
netrom: clear nr_neigh->ax25 under the routing lock in nr_link_failed()
Junjie Cao <junjie.cao@intel.com>
netrom: fix reference count leak in nr_rt_device_down
Bernard Pidoux <bernard.f6bvp@gmail.com>
netrom: look up and update routes in one critical section
Bernard Pidoux <bernard.f6bvp@gmail.com>
netrom: use a single lock for the node and neighbour lists
Mashiro Chen <mashiro.chen@mailbox.org>
net: ax25: fix integer overflow in ax25_rx_fragment()
Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25: check frame length in ax25_decode() and ax25_rx_iframe()
Ashutosh Desai <ashutoshdesai993@gmail.com>
ax25: fix OOB read after address header strip in ax25_rcv()
Bernard Pidoux <bernard.f6bvp@gmail.com>
netrom: bound fragment reassembly in nr_queue_rx_frame()
Mashiro Chen <mashiro.chen@mailbox.org>
net: netrom: validate source address in nr_find_socket()
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
netrom: require full NET/ROM headers in nr_route_frame()
Stanislav Fort <disclosure@aisle.com>
netrom: linearize and validate lengths in nr_rx_frame()
Bernard Pidoux <bernard.f6bvp@gmail.com>
rose: clear stale N(R)/N(S) before (re)transmitting a data frame
Larysa Zaremba <larysa.zaremba@intel.com>
ice: change XDP RxQ frag_size from DMA write length to xdp.frame_sz
Larysa Zaremba <larysa.zaremba@intel.com>
ice: fix rxq info registering in mbuf packets
Rafael J. Wysocki <rafael.j.wysocki@intel.com>
ACPI: scan: Register platform devices for fixed event buttons
Joseph Qi <joseph.qi@linux.alibaba.com>
eventfs: fix SRCU read lock leak in eventfs_iterate()
-------------
Diffstat:
Makefile | 4 +-
block/blk-zoned.c | 37 +-
block/blk.h | 4 +-
block/genhd.c | 2 +-
crypto/asymmetric_keys/restrict.c | 8 +-
drivers/acpi/acpi_platform.c | 75 +++-
drivers/acpi/scan.c | 4 +
drivers/net/ethernet/intel/ice/ice_base.c | 33 +-
drivers/net/ethernet/intel/ice/ice_ethtool.c | 1 +
drivers/net/ethernet/intel/ice/ice_txrx.c | 4 +-
drivers/net/ethernet/intel/ice/ice_xsk.c | 3 +
fs/coredump.c | 16 +-
fs/tracefs/event_inode.c | 6 +-
include/linux/io_uring_types.h | 5 +
include/linux/key-type.h | 1 +
include/linux/key.h | 5 +
include/linux/sched.h | 2 +-
include/linux/sched/signal.h | 27 +-
include/net/ax25.h | 6 +
include/net/codel_impl.h | 1 +
include/net/gso.h | 8 +
include/net/netrom.h | 18 +-
include/net/rose.h | 5 +
io_uring/cancel.c | 29 +-
io_uring/cancel.h | 2 +
io_uring/fdinfo.c | 14 +-
io_uring/futex.c | 32 +-
io_uring/io-wq.c | 126 ++++++-
io_uring/io_uring.c | 231 ++++++++++---
io_uring/io_uring.h | 44 ++-
io_uring/kbuf.c | 2 +-
io_uring/napi.c | 4 +-
io_uring/net.c | 26 +-
io_uring/openclose.c | 11 +-
io_uring/poll.c | 38 +-
io_uring/register.c | 20 +-
io_uring/rw.c | 18 -
io_uring/sqpoll.c | 7 +
io_uring/timeout.c | 91 +++--
io_uring/uring_cmd.c | 2 +-
io_uring/waitid.c | 19 +-
io_uring/zcrx.c | 2 +-
kernel/exit.c | 5 +-
kernel/fork.c | 9 +-
kernel/pid_namespace.c | 3 +-
kernel/ptrace.c | 6 +
kernel/signal.c | 4 +
kernel/trace/ftrace.c | 4 +-
kernel/trace/trace.c | 21 +-
kernel/trace/trace_events_filter.c | 6 +
kernel/trace/trace_events_hist.c | 33 +-
kernel/trace/trace_events_synth.c | 10 +
kernel/trace/trace_synth.h | 1 +
mm/damon/core.c | 22 +-
mm/damon/sysfs.c | 19 +
net/ax25/af_ax25.c | 81 ++++-
net/ax25/ax25_ds_in.c | 2 +
net/ax25/ax25_in.c | 55 ++-
net/ax25/ax25_out.c | 121 +++++--
net/ax25/ax25_std_in.c | 8 +
net/ax25/ax25_subr.c | 44 ++-
net/bluetooth/l2cap_core.c | 7 +-
net/bluetooth/l2cap_sock.c | 2 +
net/core/gso.c | 1 +
net/ipv4/af_inet.c | 3 +
net/ipv6/ip6_offload.c | 4 +
net/netrom/af_netrom.c | 33 +-
net/netrom/nr_in.c | 26 ++
net/netrom/nr_route.c | 383 +++++++++++----------
net/rose/af_rose.c | 5 +
net/rose/rose_in.c | 31 ++
net/rose/rose_link.c | 42 +++
net/rose/rose_loopback.c | 2 +-
net/rose/rose_out.c | 1 +
net/rose/rose_route.c | 119 ++++++-
security/keys/key.c | 13 +-
security/keys/keyctl.c | 15 +-
security/keys/keyring.c | 150 ++++----
security/keys/process_keys.c | 5 +-
security/keys/trusted-keys/trusted_tpm1.c | 4 +-
security/keys/trusted-keys/trusted_tpm2.c | 2 +-
.../test.d/filter/event-filter-incomplete.tc | 24 ++
82 files changed, 1722 insertions(+), 597 deletions(-)
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 001/109] eventfs: fix SRCU read lock leak in eventfs_iterate()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 002/109] ACPI: scan: Register platform devices for fixed event buttons Greg Kroah-Hartman
` (114 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Joseph Qi, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joseph Qi <joseph.qi@linux.alibaba.com>
Commit 2b80707b9e27 ("eventfs: Use children field for rcu head and add
memory barriers") added an early bail out to the ei->children loop:
smp_rmb();
if (ei->is_freed)
return -EINVAL;
Upstream wraps eventfs_iterate() in guard(srcu)(&eventfs_srcu), so that
return still drops the read lock. The 6.18.y backport instead brackets the
function with srcu_read_lock() and a shared "out" label that calls
srcu_read_unlock(), so this path returns while still holding eventfs_srcu
for reading.
The unbalanced reader prevents the grace period from ever completing, so
the call_srcu() callback queued by free_ei() never runs and the
eventfs_inode is leaked. Every readdir that races with removal adds
another reader.
Set ret and jump to "out" like the other bail out paths in this function
do.
Fixes: 2b80707b9e27 ("eventfs: Use children field for rcu head and add memory barriers")
Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/tracefs/event_inode.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
index a992c2919f79a..e060dfe7cdac5 100644
--- a/fs/tracefs/event_inode.c
+++ b/fs/tracefs/event_inode.c
@@ -678,8 +678,10 @@ static int eventfs_iterate(struct file *file, struct dir_context *ctx)
* Matches the smp_wmb() in free_ei()
*/
smp_rmb();
- if (ei->is_freed)
- return -EINVAL;
+ if (ei->is_freed) {
+ ret = -EINVAL;
+ goto out;
+ }
if (c > 0) {
c--;
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 002/109] ACPI: scan: Register platform devices for fixed event buttons
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 001/109] eventfs: fix SRCU read lock leak in eventfs_iterate() Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 003/109] ice: fix rxq info registering in mbuf packets Greg Kroah-Hartman
` (113 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Rafael J. Wysocki, Ting-Han Hou,
Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
[ Upstream commit ab06eb9204010c61e754bf4e71ee6f554584714d ]
On platforms using ACPI, power and sleep buttons may be so called "fixed
event devices" in which case they are hooked up directly to the Fixed
Events register in the platform via dedicated lines and there are no
corresponding device objects in the ACPI namespace. Nevertheless, in
Linux they get corresponding struct acpi_device objects with special
device IDs, either LNXPWRBN or LNXSLPBN, which are then used for driver
binding in a ususal way.
However, the function creating those struct acpi_device objects for
"fixed event device" buttons, acpi_bus_scan_fixed(), does not register
platform devices for them, unlike the generic code handling device
enumeration based on the ACPI namespace. Consequently, if an ACPI power
or sleep button is represented by a device object in the ACPI namespace,
it will get a corresponding platform device, but if it is a "fixed event
device", it will not get one, which is inconsistent and prevents the
ACPI power button driver from being converted into a platform driver.
For the sake of consistency and to allow the ACPI power button driver to
become a platform one going forward, modify acpi_bus_scan_fixed() to
register platform devices for "fixed event device" buttons and update
ACPI platform device registration code to work with non-device ACPI
object types, so it can handle the buttons in question.
No intentional functional impact.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://patch.msgid.link/3731144.R56niFO833@rafael.j.wysocki
[ Ting-Han: 52d864019636 ("ACPI: button: Convert the driver to a platform
one") was backported to 6.18.57 as a dependency of fe80251152fe
("ACPI: button: Fix ACPI GPE handler leak during removal") without this
prerequisite, so fixed event power buttons (LNXPWRBN, e.g. on QEMU q35)
have no platform device and are no longer bound to any driver. ]
Signed-off-by: Ting-Han Hou <ue081723@gmail.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/acpi/acpi_platform.c | 38 ++++++++++++++++++++----------------
drivers/acpi/scan.c | 4 ++++
2 files changed, 25 insertions(+), 17 deletions(-)
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
index 48d15dd785f6e..52c8d602f3a57 100644
--- a/drivers/acpi/acpi_platform.c
+++ b/drivers/acpi/acpi_platform.c
@@ -114,10 +114,8 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev,
struct platform_device *pdev = NULL;
struct platform_device_info pdevinfo;
const struct acpi_device_id *match;
- struct resource_entry *rentry;
- struct list_head resource_list;
struct resource *resources = NULL;
- int count;
+ int count = 0;
/* If the ACPI node already has a physical device attached, skip it. */
if (adev->physical_node_count)
@@ -137,22 +135,28 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev,
}
}
- INIT_LIST_HEAD(&resource_list);
- count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
- if (count < 0)
- return NULL;
- if (count > 0) {
- resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
- if (!resources) {
+ if (adev->device_type == ACPI_BUS_TYPE_DEVICE) {
+ struct list_head resource_list = LIST_HEAD_INIT(resource_list);
+
+ count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
+ if (count < 0)
+ return ERR_PTR(-ENODATA);
+
+ if (count > 0) {
+ struct resource_entry *rentry;
+
+ resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
+ if (!resources) {
+ acpi_dev_free_resource_list(&resource_list);
+ return ERR_PTR(-ENOMEM);
+ }
+ count = 0;
+ list_for_each_entry(rentry, &resource_list, node)
+ acpi_platform_fill_resource(adev, rentry->res,
+ &resources[count++]);
+
acpi_dev_free_resource_list(&resource_list);
- return ERR_PTR(-ENOMEM);
}
- count = 0;
- list_for_each_entry(rentry, &resource_list, node)
- acpi_platform_fill_resource(adev, rentry->res,
- &resources[count++]);
-
- acpi_dev_free_resource_list(&resource_list);
}
memset(&pdevinfo, 0, sizeof(pdevinfo));
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 7f9dd2704f638..c382bed50d6cb 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2644,6 +2644,8 @@ static void acpi_bus_scan_fixed(void)
device_init_wakeup(&adev->dev, true);
else
dev_dbg(&adev->dev, "No driver\n");
+
+ acpi_default_enumeration(adev);
}
}
@@ -2656,6 +2658,8 @@ static void acpi_bus_scan_fixed(void)
adev->flags.match_driver = true;
if (device_attach(&adev->dev) < 0)
dev_dbg(&adev->dev, "No driver\n");
+
+ acpi_default_enumeration(adev);
}
}
}
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 003/109] ice: fix rxq info registering in mbuf packets
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 001/109] eventfs: fix SRCU read lock leak in eventfs_iterate() Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 002/109] ACPI: scan: Register platform devices for fixed event buttons Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 004/109] ice: change XDP RxQ frag_size from DMA write length to xdp.frame_sz Greg Kroah-Hartman
` (112 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Aleksandr Loktionov, Larysa Zaremba,
Jakub Kicinski, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Larysa Zaremba <larysa.zaremba@intel.com>
[ Upstream commit 02852b47c706772af795d3e28fca99fc9b923b2c ]
XDP RxQ info contains frag_size, which depends on the MTU. This makes the
old way of registering RxQ info before calculating new buffer sizes
invalid. Currently, it leads to frag_size being outdated, making it
sometimes impossible to grow tailroom in a mbuf packet. E.g. fragments are
actually 3K+, but frag size is still as if MTU was 1500.
Always register new XDP RxQ info after reconfiguring memory pools.
Fixes: 2fba7dc5157b ("ice: Add support for XDP multi-buffer on Rx side")
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
Link: https://patch.msgid.link/20260305111253.2317394-4-larysa.zaremba@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/net/ethernet/intel/ice/ice_base.c | 26 ++++++--------------
drivers/net/ethernet/intel/ice/ice_ethtool.c | 1 +
drivers/net/ethernet/intel/ice/ice_txrx.c | 4 ++-
drivers/net/ethernet/intel/ice/ice_xsk.c | 3 +++
4 files changed, 14 insertions(+), 20 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c
index 2d3d23450783c..cefd0a18c4c04 100644
--- a/drivers/net/ethernet/intel/ice/ice_base.c
+++ b/drivers/net/ethernet/intel/ice/ice_base.c
@@ -604,23 +604,12 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
if (ring->vsi->type == ICE_VSI_PF || ring->vsi->type == ICE_VSI_SF ||
ring->vsi->type == ICE_VSI_LB) {
- if (!xdp_rxq_info_is_reg(&ring->xdp_rxq)) {
- err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
- ring->q_index,
- ring->q_vector->napi.napi_id,
- ring->rx_buf_len);
- if (err)
- return err;
- }
-
ice_rx_xsk_pool(ring);
err = ice_realloc_rx_xdp_bufs(ring, ring->xsk_pool);
if (err)
return err;
if (ring->xsk_pool) {
- xdp_rxq_info_unreg(&ring->xdp_rxq);
-
rx_buf_len =
xsk_pool_get_rx_frame_size(ring->xsk_pool);
err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
@@ -648,14 +637,13 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
ring->truesize = fq.truesize;
ring->rx_buf_len = fq.buf_len;
- if (!xdp_rxq_info_is_reg(&ring->xdp_rxq)) {
- err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
- ring->q_index,
- ring->q_vector->napi.napi_id,
- ring->rx_buf_len);
- if (err)
- goto err_destroy_fq;
- }
+ err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
+ ring->q_index,
+ ring->q_vector->napi.napi_id,
+ ring->rx_buf_len);
+ if (err)
+ goto err_destroy_fq;
+
xdp_rxq_info_attach_page_pool(&ring->xdp_rxq,
ring->pp);
}
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c
index 428118475e2ff..53284959eda01 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c
@@ -3313,6 +3313,7 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,
rx_rings[i].cached_phctime = pf->ptp.cached_phc_time;
rx_rings[i].desc = NULL;
rx_rings[i].xdp_buf = NULL;
+ rx_rings[i].xdp_rxq = (struct xdp_rxq_info){ };
/* this is to allow wr32 to have something to write to
* during early allocation of Rx buffers
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c
index cc3c07847c35c..9752d61a0a5f1 100644
--- a/drivers/net/ethernet/intel/ice/ice_txrx.c
+++ b/drivers/net/ethernet/intel/ice/ice_txrx.c
@@ -542,7 +542,9 @@ void ice_clean_rx_ring(struct ice_rx_ring *rx_ring)
i = 0;
}
- if (rx_ring->vsi->type == ICE_VSI_PF &&
+ if ((rx_ring->vsi->type == ICE_VSI_PF ||
+ rx_ring->vsi->type == ICE_VSI_SF ||
+ rx_ring->vsi->type == ICE_VSI_LB) &&
xdp_rxq_info_is_reg(&rx_ring->xdp_rxq)) {
xdp_rxq_info_detach_mem_model(&rx_ring->xdp_rxq);
xdp_rxq_info_unreg(&rx_ring->xdp_rxq);
diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.c b/drivers/net/ethernet/intel/ice/ice_xsk.c
index 989ff1fd91103..102631398af3c 100644
--- a/drivers/net/ethernet/intel/ice/ice_xsk.c
+++ b/drivers/net/ethernet/intel/ice/ice_xsk.c
@@ -900,6 +900,9 @@ void ice_xsk_clean_rx_ring(struct ice_rx_ring *rx_ring)
u16 ntc = rx_ring->next_to_clean;
u16 ntu = rx_ring->next_to_use;
+ if (xdp_rxq_info_is_reg(&rx_ring->xdp_rxq))
+ xdp_rxq_info_unreg(&rx_ring->xdp_rxq);
+
while (ntc != ntu) {
struct xdp_buff *xdp = *ice_xdp_buf(rx_ring, ntc);
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 004/109] ice: change XDP RxQ frag_size from DMA write length to xdp.frame_sz
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (2 preceding siblings ...)
2026-10-28 16:59 ` [PATCH 6.18 003/109] ice: fix rxq info registering in mbuf packets Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 005/109] rose: clear stale N(R)/N(S) before (re)transmitting a data frame Greg Kroah-Hartman
` (111 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Aleksandr Loktionov, Larysa Zaremba,
Jakub Kicinski, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Larysa Zaremba <larysa.zaremba@intel.com>
[ Upstream commit e142dc4ef0f451b7ef99d09aaa84e9389af629d7 ]
The only user of frag_size field in XDP RxQ info is
bpf_xdp_frags_increase_tail(). It clearly expects whole buff size instead
of DMA write size. Different assumptions in ice driver configuration lead
to negative tailroom.
This allows to trigger kernel panic, when using
XDP_ADJUST_TAIL_GROW_MULTI_BUFF xskxceiver test and changing packet size to
6912 and the requested offset to a huge value, e.g.
XSK_UMEM__MAX_FRAME_SIZE * 100.
Due to other quirks of the ZC configuration in ice, panic is not observed
in ZC mode, but tailroom growing still fails when it should not.
Use fill queue buffer truesize instead of DMA write size in XDP RxQ info.
Fix ZC mode too by using the new helper.
Fixes: 2fba7dc5157b ("ice: Add support for XDP multi-buffer on Rx side")
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
Link: https://patch.msgid.link/20260305111253.2317394-5-larysa.zaremba@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/net/ethernet/intel/ice/ice_base.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c
index cefd0a18c4c04..2e31199546a82 100644
--- a/drivers/net/ethernet/intel/ice/ice_base.c
+++ b/drivers/net/ethernet/intel/ice/ice_base.c
@@ -599,7 +599,6 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
};
struct device *dev = ice_pf_to_dev(ring->vsi->back);
u32 num_bufs = ICE_DESC_UNUSED(ring);
- u32 rx_buf_len;
int err;
if (ring->vsi->type == ICE_VSI_PF || ring->vsi->type == ICE_VSI_SF ||
@@ -610,12 +609,12 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
return err;
if (ring->xsk_pool) {
- rx_buf_len =
- xsk_pool_get_rx_frame_size(ring->xsk_pool);
+ u32 frag_size =
+ xsk_pool_get_rx_frag_step(ring->xsk_pool);
err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
ring->q_index,
ring->q_vector->napi.napi_id,
- rx_buf_len);
+ frag_size);
if (err)
return err;
err = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq,
@@ -640,7 +639,7 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
ring->q_index,
ring->q_vector->napi.napi_id,
- ring->rx_buf_len);
+ ring->truesize);
if (err)
goto err_destroy_fq;
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 005/109] rose: clear stale N(R)/N(S) before (re)transmitting a data frame
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (3 preceding siblings ...)
2026-10-28 16:59 ` [PATCH 6.18 004/109] ice: change XDP RxQ frag_size from DMA write length to xdp.frame_sz Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 006/109] netrom: linearize and validate lengths in nr_rx_frame() Greg Kroah-Hartman
` (110 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
rose_kick() transmits a skb_clone() of each data frame and keeps the
original on ack_queue. The clone shares its data buffer with the
original, so the sequence numbers that rose_send_iframe() ORs into
byte 2 of the header are also left in the queued original.
After a RESET, rose_requeue_frames() puts those originals back on the
write queue and rose_kick() sends them again with vs/vr restarted at
zero. rose_send_iframe() only ORs the new values into byte 2, so the
retransmitted frame carries the bitwise OR of the old and new N(R) and
N(S). The peer rejects the invalid N(R) with a new RESET REQUEST, the
frames are requeued again, and the two nodes reset each other forever.
This was observed on a live FPAC network: a White Pages circuit between
two nodes looping on RESET REQUEST / RESET CONFIRMATION / DATA at about
2000 frames per second, with DATA frames resent as "R3 S7" right after
a reset instead of "R0 S0", until the application was restarted.
Clear everything but the M bit before setting the sequence numbers, as
x25_send_iframe() does for modulo 8 circuits.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit b16b091aeb78faac22cf2b850246826d23f4a335)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/rose/rose_out.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/rose/rose_out.c b/net/rose/rose_out.c
index 9050e33c96042..6672fc65a7651 100644
--- a/net/rose/rose_out.c
+++ b/net/rose/rose_out.c
@@ -34,6 +34,7 @@ static void rose_send_iframe(struct sock *sk, struct sk_buff *skb)
if (skb == NULL)
return;
+ skb->data[2] &= ROSE_M_BIT;
skb->data[2] |= (rose->vr << 5) & 0xE0;
skb->data[2] |= (rose->vs << 1) & 0x0E;
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 006/109] netrom: linearize and validate lengths in nr_rx_frame()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (4 preceding siblings ...)
2026-10-28 16:59 ` [PATCH 6.18 005/109] rose: clear stale N(R)/N(S) before (re)transmitting a data frame Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 007/109] netrom: require full NET/ROM headers in nr_route_frame() Greg Kroah-Hartman
` (109 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Eric Dumazet, Stanislav Fort,
Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stanislav Fort <disclosure@aisle.com>
Linearize skb and add targeted length checks in nr_rx_frame() to
prevent out-of-bounds reads when processing malformed NET/ROM frames.
- Require skb to be linear and at least NR_NETWORK_LEN +
NR_TRANSPORT_LEN (20 bytes) before reading network/transport fields.
- For the existing sockets path, ensure NR_CONNACK includes the window
byte (>= 21 bytes), which nr_state1_machine() reads.
- For CONNREQ handling, ensure window (byte 20) and user address
(bytes 21-27) are present (>= 28 bytes), and do so before
nr_find_listener() so no listener reference can leak.
- Preserve existing BPQ extension handling:
- NR_CONNACK len == 22 -> 1 extra byte (TTL)
- NR_CONNREQ len == 37 -> 2 extra bytes (timeout)
nr_route_frame() only guarantees 15 bytes before handing a frame
addressed to a local node to nr_rx_frame(), so a short frame received
from any AX.25 neighbour is enough to trigger the over-read.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Suggested-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Stanislav Fort <disclosure@aisle.com>
[F6BVP: v4 never reached mainline (whitespace-damaged posting, subsystem
removed in 7.1); rebased onto mod-orphan, added comments. Tested on a
two-VM QEMU bench (6.12.94, mkiss link): an AX.25 peer sends CONNREQ
frames truncated to 20 and 27 bytes; without this patch both are
accepted as connections, the user callsign being read past the end of
the frame; with it only the complete 35-byte CONNREQ is accepted.
v2: apply the CONNACK length check to a plain CONNACK only, so that
20-byte CONNACK|CHOKE refusals and resets are no longer dropped
(reported by Sasha Levin). Same fix submitted to mod-orphan.]
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit db51201afd46992c9206fa79878badb2236bb7d3)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/netrom/af_netrom.c | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index 33468124d53db..0e70d149abcd5 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -885,6 +885,11 @@ int nr_rx_frame(struct sk_buff *skb, struct net_device *dev)
* skb->data points to the netrom frame start
*/
+ if (skb_linearize(skb))
+ return 0;
+ if (skb->len < NR_NETWORK_LEN + NR_TRANSPORT_LEN)
+ return 0;
+
src = (ax25_address *)(skb->data + 0);
dest = (ax25_address *)(skb->data + 7);
@@ -927,6 +932,17 @@ int nr_rx_frame(struct sk_buff *skb, struct net_device *dev)
}
if (sk != NULL) {
+ /*
+ * nr_state1_machine() reads the window size at byte 20, but
+ * only for a plain CONNACK. A CONNACK with the choke flag set
+ * is a refusal or a reset, sent as a 20-byte frame by
+ * __nr_transmit_reply(), and must not be dropped here.
+ */
+ if (frametype == NR_CONNACK && !flags &&
+ skb->len < NR_NETWORK_LEN + NR_TRANSPORT_LEN + 1) {
+ sock_put(sk);
+ return 0;
+ }
bh_lock_sock(sk);
skb_reset_transport_header(skb);
@@ -961,10 +977,14 @@ int nr_rx_frame(struct sk_buff *skb, struct net_device *dev)
return 0;
}
- sk = nr_find_listener(dest);
+ /* Need window (byte 20) and user address (bytes 21-27) */
+ if (skb->len < NR_NETWORK_LEN + NR_TRANSPORT_LEN + 1 + AX25_ADDR_LEN)
+ return 0;
user = (ax25_address *)(skb->data + 21);
+ sk = nr_find_listener(dest);
+
if (sk == NULL || sk_acceptq_is_full(sk) ||
(make = nr_make_new(sk)) == NULL) {
nr_transmit_refusal(skb, 0);
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 007/109] netrom: require full NET/ROM headers in nr_route_frame()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (5 preceding siblings ...)
2026-10-28 16:59 ` [PATCH 6.18 006/109] netrom: linearize and validate lengths in nr_rx_frame() Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 008/109] net: netrom: validate source address in nr_find_socket() Greg Kroah-Hartman
` (108 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
nr_route_frame() only checked that an incoming frame was long enough
for the two network layer addresses and the time-to-live byte, and did
so against skb->len without making sure the bytes were in the linear
part of the skb.
A valid NET/ROM network frame always carries the 5-byte transport
header as well, so anything shorter than NR_NETWORK_LEN +
NR_TRANSPORT_LEN (20 bytes) is malformed, whether it is for this node
or in transit. Use pskb_may_pull() for that length, as suggested in
review, so the header fields are also guaranteed to be linear before
they are read here or later in nr_rx_frame().
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
[F6BVP: taken from the nr_route.c part of Greg's "netrom: do some
basic forms of validation on incoming frames"; the nr_rx_frame() part
is handled separately. Switched from a skb->len test to
pskb_may_pull() following Simon Horman's review.]
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit dc06212ff6bb781fad2b5082e3ecb7cc93336da0)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/netrom/nr_route.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index 9cc29ae85b06f..7f55030e4b166 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -755,10 +755,11 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
struct sk_buff *nskb, *oskb;
/*
- * Reject malformed packets early. Check that it contains at least 2
- * addresses and 1 byte more for Time-To-Live
+ * Reject malformed packets early. Check that it contains at least
+ * the network and transport headers (20 bytes), and that they are
+ * in the linear part of the skb.
*/
- if (skb->len < 2 * sizeof(ax25_address) + 1)
+ if (!pskb_may_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN))
return 0;
nr_src = (ax25_address *)(skb->data + 0);
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 008/109] net: netrom: validate source address in nr_find_socket()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (6 preceding siblings ...)
2026-10-28 16:59 ` [PATCH 6.18 007/109] netrom: require full NET/ROM headers in nr_route_frame() Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 009/109] netrom: bound fragment reassembly in nr_queue_rx_frame() Greg Kroah-Hartman
` (107 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Mashiro Chen, Bernard Pidoux,
Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mashiro Chen <mashiro.chen@mailbox.org>
nr_find_socket() dispatches incoming NR_INFO frames into a connected
socket by matching the frame's circuit index/id pair (bytes[15-16])
against the socket's my_index/my_id. It performs no validation of
the frame's source callsign against the socket's dest_addr.
This means any node on the network can craft an NR_INFO frame with
a guessed or brute-forced circuit index/id pair and have it accepted
into an arbitrary STATE_3 connection as if it came from the legitimate
peer. Circuit IDs are assigned sequentially starting at (1,1), making
them predictable in practice.
Combined with other receive path bugs, this lets a third party feed
frames into a circuit it does not own. nr_find_peer() already checks
the source address for CONNREQ; do the same for the other frame types.
Fix by adding a source address parameter to nr_find_socket() and
requiring it to match the socket's recorded dest_addr for all
frame-dispatch lookups. The internal nr_find_next_circuit() caller,
which only checks for circuit ID availability, passes NULL to skip
the source check.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Mashiro Chen <mashiro.chen@mailbox.org>
[F6BVP: rebased on mod-orphan; dropped the reference to an unassigned
CVE from the changelog. Checked that every caller other than
nr_find_next_circuit() is in nr_rx_frame() for non-CONNREQ frames,
whose network source is the remote node recorded in dest_addr.]
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit 70bf32b0eda85781a416cb929b7c6b4761abd2a8)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/netrom/af_netrom.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index 0e70d149abcd5..d9c484705c9e7 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -162,7 +162,8 @@ static struct sock *nr_find_listener(ax25_address *addr)
/*
* Find a connected NET/ROM socket given my circuit IDs.
*/
-static struct sock *nr_find_socket(unsigned char index, unsigned char id)
+static struct sock *nr_find_socket(unsigned char index, unsigned char id,
+ const ax25_address *src)
{
struct sock *s;
@@ -170,7 +171,8 @@ static struct sock *nr_find_socket(unsigned char index, unsigned char id)
sk_for_each(s, &nr_list) {
struct nr_sock *nr = nr_sk(s);
- if (nr->my_index == index && nr->my_id == id) {
+ if (nr->my_index == index && nr->my_id == id &&
+ (!src || !ax25cmp(&nr->dest_addr, src))) {
sock_hold(s);
goto found;
}
@@ -219,7 +221,8 @@ static unsigned short nr_find_next_circuit(void)
j = id % 256;
if (i != 0 && j != 0) {
- if ((sk=nr_find_socket(i, j)) == NULL)
+ sk = nr_find_socket(i, j, NULL);
+ if (!sk)
break;
sock_put(sk);
}
@@ -928,7 +931,7 @@ int nr_rx_frame(struct sk_buff *skb, struct net_device *dev)
if (frametype == NR_CONNREQ)
sk = nr_find_peer(circuit_index, circuit_id, src);
else
- sk = nr_find_socket(circuit_index, circuit_id);
+ sk = nr_find_socket(circuit_index, circuit_id, src);
}
if (sk != NULL) {
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 009/109] netrom: bound fragment reassembly in nr_queue_rx_frame()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (7 preceding siblings ...)
2026-10-28 16:59 ` [PATCH 6.18 008/109] net: netrom: validate source address in nr_find_socket() Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 010/109] ax25: fix OOB read after address header strip in ax25_rcv() Greg Kroah-Hartman
` (106 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Mashiro Chen, Bernard Pidoux,
Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
nr_sock.fraglen is an unsigned short, but nr_sendmsg() accepts messages
of up to 65536 bytes, which nr_output() then splits into NR_INFO frames
with the MORE flag. Reassembling such a message on the receiving side
makes fraglen wrap to 0, so alloc_skb() returns a buffer far smaller
than the data copied into it just after. The same happens with any
stream of MORE fragments that adds up to more than 65535 bytes.
Mashiro Chen proposed rejecting fragments once the total would exceed
USHRT_MAX by returning 1 to the caller. Review pointed out that this
return value makes nr_state3_machine() put the frame back on the
resequencing queue after nr_queue_rx_frame() has already pulled its
headers, and that the limit would reject a legitimate 65536-byte
message.
Instead:
- make fraglen an unsigned int, so a full 64 KiB message fits;
- when a message would grow beyond 65536 bytes, drop what has been
gathered so far, consume the frame and return 0, so the caller does
not retry it;
- discard the remaining fragments of that message up to its last
fragment, using a new NR_COND_FRAG_DISCARD condition bit, rather
than delivering its tail as a separate message.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: Mashiro Chen <mashiro.chen@mailbox.org>
Closes: https://patchwork.kernel.org/project/netdevbpf/patch/20260407171600.102988-2-mashiro.chen@mailbox.org/
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit 6ad8be739b22495d6edc3f6d7b916098ceb3fdf0)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
include/net/netrom.h | 3 ++-
net/netrom/nr_in.c | 26 ++++++++++++++++++++++++++
2 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/include/net/netrom.h b/include/net/netrom.h
index f0565a5987d1b..30ecf812b4ab4 100644
--- a/include/net/netrom.h
+++ b/include/net/netrom.h
@@ -46,6 +46,7 @@ enum {
#define NR_COND_REJECT 0x02
#define NR_COND_PEER_RX_BUSY 0x04
#define NR_COND_OWN_RX_BUSY 0x08
+#define NR_COND_FRAG_DISCARD 0x10
#define NR_DEFAULT_T1 120000 /* Outstanding frames - 120 seconds */
#define NR_DEFAULT_T2 5000 /* Response delay - 5 seconds */
@@ -74,7 +75,7 @@ struct nr_sock {
unsigned short vs, vr, va, vl;
unsigned char n2, n2count;
unsigned long t1, t2, t4, idle;
- unsigned short fraglen;
+ unsigned int fraglen;
struct timer_list t1timer;
struct timer_list t2timer;
struct timer_list t4timer;
diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c
index 97944db6b5ac6..b90a47dcf165e 100644
--- a/net/netrom/nr_in.c
+++ b/net/netrom/nr_in.c
@@ -26,6 +26,9 @@
#include <linux/interrupt.h>
#include <net/netrom.h>
+/* nr_sendmsg() never sends more than this in one message */
+#define NR_MAX_REASM_LEN 65536
+
static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
{
struct sk_buff *skbo, *skbn = skb;
@@ -35,6 +38,29 @@ static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
nr_start_idletimer(sk);
+ /* Rest of a message already found to be too long: drop it */
+ if (nr->condition & NR_COND_FRAG_DISCARD) {
+ if (!more)
+ nr->condition &= ~NR_COND_FRAG_DISCARD;
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /*
+ * Give up on a message that would grow beyond what a peer may
+ * send. The frame is consumed rather than handed back to the
+ * caller, which would queue it again after skb_pull().
+ */
+ if ((more || nr->fraglen > 0) &&
+ nr->fraglen + skb->len > NR_MAX_REASM_LEN) {
+ skb_queue_purge(&nr->frag_queue);
+ nr->fraglen = 0;
+ if (more)
+ nr->condition |= NR_COND_FRAG_DISCARD;
+ kfree_skb(skb);
+ return 0;
+ }
+
if (more) {
nr->fraglen += skb->len;
skb_queue_tail(&nr->frag_queue, skb);
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 010/109] ax25: fix OOB read after address header strip in ax25_rcv()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (8 preceding siblings ...)
2026-10-28 16:59 ` [PATCH 6.18 009/109] netrom: bound fragment reassembly in nr_queue_rx_frame() Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 011/109] ax25: check frame length in ax25_decode() and ax25_rx_iframe() Greg Kroah-Hartman
` (105 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Ashutosh Desai, Bernard Pidoux,
Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Desai <ashutoshdesai993@gmail.com>
A crafted AX.25 frame with a valid address header but no control byte
causes skb->len to reach zero after skb_pull() strips the header.
The subsequent reads of skb->data[0] (control) and skb->data[1] (PID)
are then out of bounds.
Linearize the skb after confirming the device is an AX.25 interface.
Guard with skb->len < 1 after the pull - one byte suffices for LAPB
control frames which have no PID byte. Add a separate skb->len < 2
check inside the UI branch before accessing the PID byte.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Ashutosh Desai <ashutoshdesai993@gmail.com>
[F6BVP: v5 as posted on netdev; the author withdrew it only because the
subsystem was being removed from the kernel that day. Applied unchanged.]
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit e653f27001ca2a31a445ee20d9f90be65b85d7e3)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/ax25/ax25_in.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c
index f2d66af863595..f105e638611fa 100644
--- a/net/ax25/ax25_in.c
+++ b/net/ax25/ax25_in.c
@@ -199,6 +199,9 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
goto free;
+ if (skb_linearize(skb))
+ goto free;
+
/*
* Parse the address header.
*/
@@ -217,6 +220,9 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
*/
skb_pull(skb, ax25_addr_size(&dp));
+ if (skb->len < 1)
+ goto free;
+
/* For our port addresses ? */
if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi)
mine = 1;
@@ -227,6 +233,9 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
/* UI frame - bypass LAPB processing */
if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) {
+ if (skb->len < 2)
+ goto free;
+
skb_set_transport_header(skb, 2); /* skip control and pid */
ax25_send_to_raw(&dest, skb, skb->data[1]);
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 011/109] ax25: check frame length in ax25_decode() and ax25_rx_iframe()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (9 preceding siblings ...)
2026-10-28 16:59 ` [PATCH 6.18 010/109] ax25: fix OOB read after address header strip in ax25_rcv() Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 012/109] net: ax25: fix integer overflow in ax25_rx_fragment() Greg Kroah-Hartman
` (104 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
Two more reads on the receive path assume bytes that a short frame
does not have:
- On a connection set up with SABME (modulo 128), ax25_decode() reads
a two-byte control field for I and S frames (frame[1]) without
checking that the second byte is there.
- ax25_rx_iframe() reads the PID byte of an I frame without checking
that anything is left after the control field, so an I frame made of
its control field alone is dispatched on a byte past its end. The
same function also receives reassembled segments.
Return AX25_ILLEGAL from ax25_decode() when the control field is
incomplete, which the state machines already handle as a malformed
frame, and drop an I frame that has no PID byte. The first check in
ax25_decode() also covers an empty frame on its own, independently of
the length check proposed for ax25_rcv().
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit 5b010290c0e627fd09b92eb8d690f0726d7cfe7c)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/ax25/ax25_in.c | 4 ++++
net/ax25/ax25_subr.c | 7 +++++++
2 files changed, 11 insertions(+)
diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c
index f105e638611fa..217f9a9b07631 100644
--- a/net/ax25/ax25_in.c
+++ b/net/ax25/ax25_in.c
@@ -104,6 +104,10 @@ int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
if (skb == NULL) return 0;
+ /* An I frame must at least carry its PID byte */
+ if (skb->len < 1)
+ return 0;
+
ax25_start_idletimer(ax25);
pid = *skb->data;
diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c
index bff4b203a893b..12c027beadb69 100644
--- a/net/ax25/ax25_subr.c
+++ b/net/ax25/ax25_subr.c
@@ -102,6 +102,9 @@ int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf)
frame = skb->data;
*ns = *nr = *pf = 0;
+ if (skb->len < 1)
+ return frametype;
+
if (ax25->modulus == AX25_MODULUS) {
if ((frame[0] & AX25_S) == 0) {
frametype = AX25_I; /* I frame - carries NR/NS/PF */
@@ -118,6 +121,10 @@ int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf)
}
skb_pull(skb, 1);
} else {
+ /* I and S frames carry a two-byte control field here */
+ if ((frame[0] & AX25_U) != 3 && skb->len < 2)
+ return frametype;
+
if ((frame[0] & AX25_S) == 0) {
frametype = AX25_I; /* I frame - carries NR/NS/PF */
*ns = (frame[0] >> 1) & 0x7F;
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 012/109] net: ax25: fix integer overflow in ax25_rx_fragment()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (10 preceding siblings ...)
2026-10-28 16:59 ` [PATCH 6.18 011/109] ax25: check frame length in ax25_decode() and ax25_rx_iframe() Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 013/109] netrom: use a single lock for the node and neighbour lists Greg Kroah-Hartman
` (103 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Mashiro Chen, Bernard Pidoux,
Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mashiro Chen <mashiro.chen@mailbox.org>
ax25_rx_fragment() accumulates fragment lengths into ax25_cb->fraglen,
which is an unsigned short. When the total exceeds 65535, fraglen wraps
around to a small value. The subsequent alloc_skb(fraglen) allocates a
too-small buffer, and skb_put() in the copy loop triggers skb_over_panic().
Add pskb_may_pull(skb, 1) at function entry to ensure the segmentation
header byte is in the linear data area before dereferencing skb->data.
This also rejects zero-length skbs, which the original code did not
check for.
Two issues in the overflow error path are also fixed:
First, the current skb, after skb_pull(skb, 1), is neither enqueued
nor freed before returning 1, leaking it. Add kfree_skb(skb) before
the return.
Second, ax25->fraglen is not reset after skb_queue_purge(). Add
ax25->fraglen = 0 to restore a consistent state.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Mashiro Chen <mashiro.chen@mailbox.org>
[F6BVP: v4 as posted on netdev; it was set aside only because the
subsystem was being removed from the kernel. Applied unchanged.]
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit 0674e0f082be20adb8e7eb03718d85e71d8b05de)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/ax25/ax25_in.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c
index 217f9a9b07631..18e15c3c26e46 100644
--- a/net/ax25/ax25_in.c
+++ b/net/ax25/ax25_in.c
@@ -35,12 +35,22 @@ static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
{
struct sk_buff *skbn, *skbo;
+ if (!pskb_may_pull(skb, 1))
+ return 0;
+
if (ax25->fragno != 0) {
if (!(*skb->data & AX25_SEG_FIRST)) {
if ((ax25->fragno - 1) == (*skb->data & AX25_SEG_REM)) {
/* Enqueue fragment */
ax25->fragno = *skb->data & AX25_SEG_REM;
skb_pull(skb, 1); /* skip fragno */
+ if (ax25->fraglen + skb->len > USHRT_MAX) {
+ kfree_skb(skb);
+ skb_queue_purge(&ax25->frag_queue);
+ ax25->fragno = 0;
+ ax25->fraglen = 0;
+ return 1;
+ }
ax25->fraglen += skb->len;
skb_queue_tail(&ax25->frag_queue, skb);
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 013/109] netrom: use a single lock for the node and neighbour lists
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (11 preceding siblings ...)
2026-10-28 16:59 ` [PATCH 6.18 012/109] net: ax25: fix integer overflow in ax25_rx_fragment() Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 014/109] netrom: look up and update routes in one critical section Greg Kroah-Hartman
` (102 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Jakub Kicinski, Bernard Pidoux,
Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
NET/ROM routing uses three kinds of locks: nr_node_list_lock,
nr_neigh_list_lock and a lock in every nr_node. They are taken in
opposite orders on different paths:
- nr_del_node() and nr_dec_obs() take the node list lock, then a node
lock, then the neighbour list lock (through nr_remove_neigh());
- nr_add_node(), called for every frame received from a neighbour,
holds a node lock and takes the neighbour list lock;
- nr_rt_device_down() takes the neighbour list lock, then the node
list lock, then each node lock.
Taking an interface down while routes are being changed or frames are
coming in can therefore deadlock, which is what the open syzbot
reports against nr_del_node(), nr_remove_neigh() and
nr_rt_device_down() show. Fixing the order case by case was tried
several times and never covered every combination.
As suggested by Jakub, replace all three with one nr_route_lock that
covers both lists and the routes of every node. The per-node lock goes
away: with the lists and routes[] under the same lock it no longer
protects anything, and routing tables are small and change rarely.
nr_remove_neigh() gets a variant for callers that already hold the
lock. Node refcounting is unchanged apart from the hold/put pair that
nr_node_lock()/nr_node_unlock() used to add around each critical
section.
nr_route_frame() still calls ax25_send_frame() with the lock held, as
it did under the node lock, so the lock is documented as never to be
taken with an AX.25 list lock held; nr_link_failed() is called from
AX.25 with only linkfail_lock held.
No change in behaviour otherwise; the other issues found on the way
(unlocked updates of nr_neigh->ax25 and failed, duplicate entries from
concurrent nr_add_node(), a reference leak in nr_rt_device_down()) are
left for separate patches.
Link: https://patchwork.kernel.org/project/netdevbpf/patch/20260406114904.89088-1-mashiro.chen@mailbox.org/
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit 602c964d5e3d006cbd6e514aa971c5ab7257d66a)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
include/net/netrom.h | 15 -----
net/netrom/nr_route.c | 133 +++++++++++++++++++-----------------------
2 files changed, 59 insertions(+), 89 deletions(-)
diff --git a/include/net/netrom.h b/include/net/netrom.h
index 30ecf812b4ab4..2b4685d52bd81 100644
--- a/include/net/netrom.h
+++ b/include/net/netrom.h
@@ -115,7 +115,6 @@ struct nr_node {
unsigned char count;
struct nr_route routes[3];
refcount_t refcount;
- spinlock_t node_lock;
};
/*********************************************************************
@@ -145,20 +144,6 @@ static __inline__ void nr_neigh_put(struct nr_neigh *nr_neigh)
}
}
-/* nr_node_lock and nr_node_unlock also hold/put the node's refcounter.
- */
-static __inline__ void nr_node_lock(struct nr_node *nr_node)
-{
- nr_node_hold(nr_node);
- spin_lock_bh(&nr_node->node_lock);
-}
-
-static __inline__ void nr_node_unlock(struct nr_node *nr_node)
-{
- spin_unlock_bh(&nr_node->node_lock);
- nr_node_put(nr_node);
-}
-
#define nr_neigh_for_each(__nr_neigh, list) \
hlist_for_each_entry(__nr_neigh, list, neigh_node)
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index 7f55030e4b166..e3effc85d3106 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -36,24 +36,30 @@
static unsigned int nr_neigh_no = 1;
+/*
+ * nr_route_lock protects both lists and the routing data hanging off them:
+ * which, count and routes[] of every node. It is taken with bottom halves
+ * disabled, is never nested with itself, and may be held while calling into
+ * AX.25 (nr_route_frame() -> ax25_send_frame()), so it must never be taken
+ * with an AX.25 list lock held.
+ */
+static DEFINE_SPINLOCK(nr_route_lock);
static HLIST_HEAD(nr_node_list);
-static DEFINE_SPINLOCK(nr_node_list_lock);
static HLIST_HEAD(nr_neigh_list);
-static DEFINE_SPINLOCK(nr_neigh_list_lock);
static struct nr_node *nr_node_get(ax25_address *callsign)
{
struct nr_node *found = NULL;
struct nr_node *nr_node;
- spin_lock_bh(&nr_node_list_lock);
+ spin_lock_bh(&nr_route_lock);
nr_node_for_each(nr_node, &nr_node_list)
if (ax25cmp(callsign, &nr_node->callsign) == 0) {
nr_node_hold(nr_node);
found = nr_node;
break;
}
- spin_unlock_bh(&nr_node_list_lock);
+ spin_unlock_bh(&nr_route_lock);
return found;
}
@@ -63,7 +69,7 @@ static struct nr_neigh *nr_neigh_get_dev(ax25_address *callsign,
struct nr_neigh *found = NULL;
struct nr_neigh *nr_neigh;
- spin_lock_bh(&nr_neigh_list_lock);
+ spin_lock_bh(&nr_route_lock);
nr_neigh_for_each(nr_neigh, &nr_neigh_list)
if (ax25cmp(callsign, &nr_neigh->callsign) == 0 &&
nr_neigh->dev == dev) {
@@ -71,11 +77,11 @@ static struct nr_neigh *nr_neigh_get_dev(ax25_address *callsign,
found = nr_neigh;
break;
}
- spin_unlock_bh(&nr_neigh_list_lock);
+ spin_unlock_bh(&nr_route_lock);
return found;
}
-static void nr_remove_neigh(struct nr_neigh *);
+static void nr_remove_neigh_locked(struct nr_neigh *nr_neigh);
/* re-sort the routes in quality order. */
static void re_sort_routes(struct nr_node *nr_node, int x, int y)
@@ -121,16 +127,14 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
if (nr_neigh != NULL && nr_neigh->failed != 0 && quality == 0) {
struct nr_node *nr_nodet;
- spin_lock_bh(&nr_node_list_lock);
+ spin_lock_bh(&nr_route_lock);
nr_node_for_each(nr_nodet, &nr_node_list) {
- nr_node_lock(nr_nodet);
for (i = 0; i < nr_nodet->count; i++)
if (nr_nodet->routes[i].neighbour == nr_neigh)
if (i < nr_nodet->which)
nr_nodet->which = i;
- nr_node_unlock(nr_nodet);
}
- spin_unlock_bh(&nr_node_list_lock);
+ spin_unlock_bh(&nr_route_lock);
}
if (nr_neigh != NULL)
@@ -172,10 +176,10 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
}
}
- spin_lock_bh(&nr_neigh_list_lock);
+ spin_lock_bh(&nr_route_lock);
hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list);
nr_neigh_hold(nr_neigh);
- spin_unlock_bh(&nr_neigh_list_lock);
+ spin_unlock_bh(&nr_route_lock);
}
if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked)
@@ -194,7 +198,6 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
nr_node->which = 0;
nr_node->count = 1;
refcount_set(&nr_node->refcount, 1);
- spin_lock_init(&nr_node->node_lock);
nr_node->routes[0].quality = quality;
nr_node->routes[0].obs_count = obs_count;
@@ -203,15 +206,15 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
nr_neigh_hold(nr_neigh);
nr_neigh->count++;
- spin_lock_bh(&nr_node_list_lock);
+ spin_lock_bh(&nr_route_lock);
hlist_add_head(&nr_node->node_node, &nr_node_list);
/* refcount initialized at 1 */
- spin_unlock_bh(&nr_node_list_lock);
+ spin_unlock_bh(&nr_route_lock);
nr_neigh_put(nr_neigh);
return 0;
}
- nr_node_lock(nr_node);
+ spin_lock_bh(&nr_route_lock);
if (quality != 0)
strscpy(nr_node->mnemonic, mnemonic);
@@ -246,7 +249,7 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
nr_neigh_put(nr_node->routes[2].neighbour);
if (nr_node->routes[2].neighbour->count == 0 && !nr_node->routes[2].neighbour->locked)
- nr_remove_neigh(nr_node->routes[2].neighbour);
+ nr_remove_neigh_locked(nr_node->routes[2].neighbour);
nr_node->routes[2].quality = quality;
nr_node->routes[2].obs_count = obs_count;
@@ -280,33 +283,32 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
}
nr_neigh_put(nr_neigh);
- nr_node_unlock(nr_node);
+ spin_unlock_bh(&nr_route_lock);
nr_node_put(nr_node);
return 0;
}
static void nr_remove_node_locked(struct nr_node *nr_node)
{
- lockdep_assert_held(&nr_node_list_lock);
+ lockdep_assert_held(&nr_route_lock);
hlist_del_init(&nr_node->node_node);
nr_node_put(nr_node);
}
-static inline void __nr_remove_neigh(struct nr_neigh *nr_neigh)
+static void nr_remove_neigh_locked(struct nr_neigh *nr_neigh)
{
+ lockdep_assert_held(&nr_route_lock);
+
hlist_del_init(&nr_neigh->neigh_node);
nr_neigh_put(nr_neigh);
}
-#define nr_remove_neigh_locked(__neigh) \
- __nr_remove_neigh(__neigh)
-
static void nr_remove_neigh(struct nr_neigh *nr_neigh)
{
- spin_lock_bh(&nr_neigh_list_lock);
- __nr_remove_neigh(nr_neigh);
- spin_unlock_bh(&nr_neigh_list_lock);
+ spin_lock_bh(&nr_route_lock);
+ nr_remove_neigh_locked(nr_neigh);
+ spin_unlock_bh(&nr_route_lock);
}
/*
@@ -331,15 +333,14 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n
return -EINVAL;
}
- spin_lock_bh(&nr_node_list_lock);
- nr_node_lock(nr_node);
+ spin_lock_bh(&nr_route_lock);
for (i = 0; i < nr_node->count; i++) {
if (nr_node->routes[i].neighbour == nr_neigh) {
nr_neigh->count--;
nr_neigh_put(nr_neigh);
if (nr_neigh->count == 0 && !nr_neigh->locked)
- nr_remove_neigh(nr_neigh);
+ nr_remove_neigh_locked(nr_neigh);
nr_neigh_put(nr_neigh);
nr_node->count--;
@@ -359,15 +360,13 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n
}
nr_node_put(nr_node);
}
- nr_node_unlock(nr_node);
- spin_unlock_bh(&nr_node_list_lock);
+ spin_unlock_bh(&nr_route_lock);
return 0;
}
}
nr_neigh_put(nr_neigh);
- nr_node_unlock(nr_node);
- spin_unlock_bh(&nr_node_list_lock);
+ spin_unlock_bh(&nr_route_lock);
nr_node_put(nr_node);
return -EINVAL;
@@ -412,10 +411,10 @@ static int __must_check nr_add_neigh(ax25_address *callsign,
}
}
- spin_lock_bh(&nr_neigh_list_lock);
+ spin_lock_bh(&nr_route_lock);
hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list);
/* refcount is initialized at 1 */
- spin_unlock_bh(&nr_neigh_list_lock);
+ spin_unlock_bh(&nr_route_lock);
return 0;
}
@@ -454,9 +453,8 @@ static int nr_dec_obs(void)
struct hlist_node *nodet;
int i;
- spin_lock_bh(&nr_node_list_lock);
+ spin_lock_bh(&nr_route_lock);
nr_node_for_each_safe(s, nodet, &nr_node_list) {
- nr_node_lock(s);
for (i = 0; i < s->count; i++) {
switch (s->routes[i].obs_count) {
case 0: /* A locked entry */
@@ -469,7 +467,7 @@ static int nr_dec_obs(void)
nr_neigh_put(nr_neigh);
if (nr_neigh->count == 0 && !nr_neigh->locked)
- nr_remove_neigh(nr_neigh);
+ nr_remove_neigh_locked(nr_neigh);
s->count--;
@@ -494,9 +492,8 @@ static int nr_dec_obs(void)
if (s->count <= 0)
nr_remove_node_locked(s);
- nr_node_unlock(s);
}
- spin_unlock_bh(&nr_node_list_lock);
+ spin_unlock_bh(&nr_route_lock);
return 0;
}
@@ -511,12 +508,10 @@ void nr_rt_device_down(struct net_device *dev)
struct nr_node *t;
int i;
- spin_lock_bh(&nr_neigh_list_lock);
+ spin_lock_bh(&nr_route_lock);
nr_neigh_for_each_safe(s, nodet, &nr_neigh_list) {
if (s->dev == dev) {
- spin_lock_bh(&nr_node_list_lock);
nr_node_for_each_safe(t, node2t, &nr_node_list) {
- nr_node_lock(t);
for (i = 0; i < t->count; i++) {
if (t->routes[i].neighbour == s) {
t->count--;
@@ -536,14 +531,12 @@ void nr_rt_device_down(struct net_device *dev)
if (t->count <= 0)
nr_remove_node_locked(t);
- nr_node_unlock(t);
}
- spin_unlock_bh(&nr_node_list_lock);
nr_remove_neigh_locked(s);
}
}
- spin_unlock_bh(&nr_neigh_list_lock);
+ spin_unlock_bh(&nr_route_lock);
}
/*
@@ -707,7 +700,7 @@ void nr_link_failed(ax25_cb *ax25, int reason)
struct nr_neigh *s, *nr_neigh = NULL;
struct nr_node *nr_node = NULL;
- spin_lock_bh(&nr_neigh_list_lock);
+ spin_lock_bh(&nr_route_lock);
nr_neigh_for_each(s, &nr_neigh_list) {
if (s->ax25 == ax25) {
nr_neigh_hold(s);
@@ -715,7 +708,7 @@ void nr_link_failed(ax25_cb *ax25, int reason)
break;
}
}
- spin_unlock_bh(&nr_neigh_list_lock);
+ spin_unlock_bh(&nr_route_lock);
if (nr_neigh == NULL)
return;
@@ -727,15 +720,13 @@ void nr_link_failed(ax25_cb *ax25, int reason)
nr_neigh_put(nr_neigh);
return;
}
- spin_lock_bh(&nr_node_list_lock);
+ spin_lock_bh(&nr_route_lock);
nr_node_for_each(nr_node, &nr_node_list) {
- nr_node_lock(nr_node);
if (nr_node->which < nr_node->count &&
nr_node->routes[nr_node->which].neighbour == nr_neigh)
nr_node->which++;
- nr_node_unlock(nr_node);
}
- spin_unlock_bh(&nr_node_list_lock);
+ spin_unlock_bh(&nr_route_lock);
nr_neigh_put(nr_neigh);
}
@@ -793,10 +784,10 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
nr_node = nr_node_get(nr_dest);
if (nr_node == NULL)
return 0;
- nr_node_lock(nr_node);
+ spin_lock_bh(&nr_route_lock);
if (nr_node->which >= nr_node->count) {
- nr_node_unlock(nr_node);
+ spin_unlock_bh(&nr_route_lock);
nr_node_put(nr_node);
return 0;
}
@@ -804,7 +795,7 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
nr_neigh = nr_node->routes[nr_node->which].neighbour;
if ((dev = nr_dev_first()) == NULL) {
- nr_node_unlock(nr_node);
+ spin_unlock_bh(&nr_route_lock);
nr_node_put(nr_node);
return 0;
}
@@ -815,7 +806,7 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
nskb = skb_copy_expand(skb, dev->hard_header_len, 0, GFP_ATOMIC);
if (!nskb) {
- nr_node_unlock(nr_node);
+ spin_unlock_bh(&nr_route_lock);
nr_node_put(nr_node);
dev_put(dev);
return 0;
@@ -837,7 +828,7 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
dev_put(dev);
ret = (nr_neigh->ax25 != NULL);
- nr_node_unlock(nr_node);
+ spin_unlock_bh(&nr_route_lock);
nr_node_put(nr_node);
if (ret)
@@ -849,9 +840,9 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
#ifdef CONFIG_PROC_FS
static void *nr_node_start(struct seq_file *seq, loff_t *pos)
- __acquires(&nr_node_list_lock)
+ __acquires(&nr_route_lock)
{
- spin_lock_bh(&nr_node_list_lock);
+ spin_lock_bh(&nr_route_lock);
return seq_hlist_start_head(&nr_node_list, *pos);
}
@@ -861,9 +852,9 @@ static void *nr_node_next(struct seq_file *seq, void *v, loff_t *pos)
}
static void nr_node_stop(struct seq_file *seq, void *v)
- __releases(&nr_node_list_lock)
+ __releases(&nr_route_lock)
{
- spin_unlock_bh(&nr_node_list_lock);
+ spin_unlock_bh(&nr_route_lock);
}
static int nr_node_show(struct seq_file *seq, void *v)
@@ -878,7 +869,6 @@ static int nr_node_show(struct seq_file *seq, void *v)
struct nr_node *nr_node = hlist_entry(v, struct nr_node,
node_node);
- nr_node_lock(nr_node);
seq_printf(seq, "%-9s %-7s %d %d",
ax2asc(buf, &nr_node->callsign),
(nr_node->mnemonic[0] == '\0') ? "*" : nr_node->mnemonic,
@@ -891,7 +881,6 @@ static int nr_node_show(struct seq_file *seq, void *v)
nr_node->routes[i].obs_count,
nr_node->routes[i].neighbour->number);
}
- nr_node_unlock(nr_node);
seq_puts(seq, "\n");
}
@@ -906,9 +895,9 @@ const struct seq_operations nr_node_seqops = {
};
static void *nr_neigh_start(struct seq_file *seq, loff_t *pos)
- __acquires(&nr_neigh_list_lock)
+ __acquires(&nr_route_lock)
{
- spin_lock_bh(&nr_neigh_list_lock);
+ spin_lock_bh(&nr_route_lock);
return seq_hlist_start_head(&nr_neigh_list, *pos);
}
@@ -918,9 +907,9 @@ static void *nr_neigh_next(struct seq_file *seq, void *v, loff_t *pos)
}
static void nr_neigh_stop(struct seq_file *seq, void *v)
- __releases(&nr_neigh_list_lock)
+ __releases(&nr_route_lock)
{
- spin_unlock_bh(&nr_neigh_list_lock);
+ spin_unlock_bh(&nr_route_lock);
}
static int nr_neigh_show(struct seq_file *seq, void *v)
@@ -971,12 +960,9 @@ void nr_rt_free(void)
struct nr_node *t = NULL;
struct hlist_node *nodet;
- spin_lock_bh(&nr_neigh_list_lock);
- spin_lock_bh(&nr_node_list_lock);
+ spin_lock_bh(&nr_route_lock);
nr_node_for_each_safe(t, nodet, &nr_node_list) {
- nr_node_lock(t);
nr_remove_node_locked(t);
- nr_node_unlock(t);
}
nr_neigh_for_each_safe(s, nodet, &nr_neigh_list) {
while(s->count) {
@@ -985,6 +971,5 @@ void nr_rt_free(void)
}
nr_remove_neigh_locked(s);
}
- spin_unlock_bh(&nr_node_list_lock);
- spin_unlock_bh(&nr_neigh_list_lock);
+ spin_unlock_bh(&nr_route_lock);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 014/109] netrom: look up and update routes in one critical section
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (12 preceding siblings ...)
2026-10-28 16:59 ` [PATCH 6.18 013/109] netrom: use a single lock for the node and neighbour lists Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 015/109] netrom: fix reference count leak in nr_rt_device_down Greg Kroah-Hartman
` (101 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, syzbot+2860e75836a08b172755,
syzbot+df52f4216bf7b4d768e7, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
nr_add_node(), nr_del_node(), nr_add_neigh() and nr_del_neigh() look up
a node or neighbour with nr_node_get()/nr_neigh_get_dev(), which take
and drop the routing lock, and only then take the lock again to change
the routes. In between, another task can unlink the object they found.
nr_add_node() can then store a neighbour that is no longer on
nr_neigh_list in a node's routes; that route's reference becomes the
last one, and the next time the route is dropped the neighbour is freed
and read again straight away:
nr_neigh->count--;
nr_neigh_put(nr_neigh); /* frees it */
if (nr_neigh->count == 0 && ...) /* use after free */
This is what syzbot reports in nr_add_node() (evicting the worst of
three routes) and in nr_dec_obs() through nr_rt_ioctl(). Lizhi Xu
analysed the first race; the fix posted then filtered neighbours by
refcount and was not taken.
Now that one lock covers both lists and the routes, do the lookup and
the update in a single critical section in all four functions, using
new __nr_node_get()/__nr_neigh_get_dev() variants that expect the lock
to be held. A node or neighbour found there stays linked while it is
used, so a route can no longer outlive its neighbour's list entry.
Concurrent additions of the same node or neighbour no longer create
duplicate entries either.
Also factor the "drop one route's use of a neighbour" sequence into
nr_neigh_route_put_locked(), which reads count and locked before
dropping the reference, and use it in nr_add_node(), nr_del_node() and
nr_dec_obs().
nr_add_node() is also called from nr_route_frame() in softirq context,
where the kmemdup(GFP_KERNEL) of the digipeater list could sleep; the
neighbour creation shared with nr_add_neigh() now allocates with
GFP_ATOMIC, like the rest of nr_add_node() already did.
This depends on "netrom: use a single lock for the node and neighbour
lists".
Reported-by: syzbot+2860e75836a08b172755@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=2860e75836a08b172755
Reported-by: syzbot+df52f4216bf7b4d768e7@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=df52f4216bf7b4d768e7
Link: https://patchwork.kernel.org/project/netdevbpf/patch/20251023135032.3759443-1-lizhi.xu@windriver.com/
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit 1292cb18feed7503f861695134206c99347cc7bb)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/netrom/nr_route.c | 264 ++++++++++++++++++++++--------------------
1 file changed, 138 insertions(+), 126 deletions(-)
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index e3effc85d3106..10294cf068f8a 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -47,38 +47,44 @@ static DEFINE_SPINLOCK(nr_route_lock);
static HLIST_HEAD(nr_node_list);
static HLIST_HEAD(nr_neigh_list);
-static struct nr_node *nr_node_get(ax25_address *callsign)
+static struct nr_node *__nr_node_get(ax25_address *callsign)
{
- struct nr_node *found = NULL;
struct nr_node *nr_node;
- spin_lock_bh(&nr_route_lock);
+ lockdep_assert_held(&nr_route_lock);
+
nr_node_for_each(nr_node, &nr_node_list)
if (ax25cmp(callsign, &nr_node->callsign) == 0) {
nr_node_hold(nr_node);
- found = nr_node;
- break;
+ return nr_node;
}
+ return NULL;
+}
+
+static struct nr_node *nr_node_get(ax25_address *callsign)
+{
+ struct nr_node *found;
+
+ spin_lock_bh(&nr_route_lock);
+ found = __nr_node_get(callsign);
spin_unlock_bh(&nr_route_lock);
return found;
}
-static struct nr_neigh *nr_neigh_get_dev(ax25_address *callsign,
- struct net_device *dev)
+static struct nr_neigh *__nr_neigh_get_dev(ax25_address *callsign,
+ struct net_device *dev)
{
- struct nr_neigh *found = NULL;
struct nr_neigh *nr_neigh;
- spin_lock_bh(&nr_route_lock);
+ lockdep_assert_held(&nr_route_lock);
+
nr_neigh_for_each(nr_neigh, &nr_neigh_list)
if (ax25cmp(callsign, &nr_neigh->callsign) == 0 &&
nr_neigh->dev == dev) {
nr_neigh_hold(nr_neigh);
- found = nr_neigh;
- break;
+ return nr_neigh;
}
- spin_unlock_bh(&nr_route_lock);
- return found;
+ return NULL;
}
static void nr_remove_neigh_locked(struct nr_neigh *nr_neigh);
@@ -96,6 +102,66 @@ static void re_sort_routes(struct nr_node *nr_node, int x, int y)
}
}
+/*
+ * Allocate a neighbour and link it into nr_neigh_list. The list holds one
+ * reference and the caller gets another one. Called with nr_route_lock held.
+ */
+static struct nr_neigh *nr_neigh_create_locked(ax25_address *callsign,
+ ax25_digi *ax25_digi,
+ struct net_device *dev,
+ unsigned int quality,
+ unsigned char locked)
+{
+ struct nr_neigh *nr_neigh;
+
+ lockdep_assert_held(&nr_route_lock);
+
+ nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC);
+ if (!nr_neigh)
+ return NULL;
+
+ nr_neigh->callsign = *callsign;
+ nr_neigh->digipeat = NULL;
+ nr_neigh->ax25 = NULL;
+ nr_neigh->dev = dev;
+ nr_neigh->quality = quality;
+ nr_neigh->locked = locked;
+ nr_neigh->count = 0;
+ nr_neigh->number = nr_neigh_no++;
+ nr_neigh->failed = 0;
+ refcount_set(&nr_neigh->refcount, 1);
+
+ if (ax25_digi && ax25_digi->ndigi > 0) {
+ nr_neigh->digipeat = kmemdup(ax25_digi, sizeof(*ax25_digi),
+ GFP_ATOMIC);
+ if (!nr_neigh->digipeat) {
+ kfree(nr_neigh);
+ return NULL;
+ }
+ }
+
+ hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list);
+ nr_neigh_hold(nr_neigh);
+
+ return nr_neigh;
+}
+
+/*
+ * Drop one route's use of a neighbour. The neighbour is unlinked when no
+ * route uses it any more and it was not added explicitly; count and locked
+ * are read before the route's reference is dropped, since that reference
+ * may be the last one. Called with nr_route_lock held.
+ */
+static void nr_neigh_route_put_locked(struct nr_neigh *nr_neigh)
+{
+ lockdep_assert_held(&nr_route_lock);
+
+ nr_neigh->count--;
+ if (nr_neigh->count == 0 && !nr_neigh->locked)
+ nr_remove_neigh_locked(nr_neigh);
+ nr_neigh_put(nr_neigh);
+}
+
/*
* Add a new route to a node, and in the process add the node and the
* neighbour if it is new.
@@ -106,7 +172,7 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
{
struct nr_node *nr_node;
struct nr_neigh *nr_neigh;
- int i, found;
+ int i, found, ret = 0;
struct net_device *odev;
if ((odev=nr_dev_get(nr)) != NULL) { /* Can't add routes to ourself */
@@ -114,9 +180,15 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
return -EINVAL;
}
- nr_node = nr_node_get(nr);
+ /*
+ * Look up and update under one critical section, so that a node or
+ * neighbour found here cannot be unlinked before it is used.
+ */
+ spin_lock_bh(&nr_route_lock);
+
+ nr_node = __nr_node_get(nr);
- nr_neigh = nr_neigh_get_dev(ax25, dev);
+ nr_neigh = __nr_neigh_get_dev(ax25, dev);
/*
* The L2 link to a neighbour has failed in the past
@@ -127,14 +199,12 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
if (nr_neigh != NULL && nr_neigh->failed != 0 && quality == 0) {
struct nr_node *nr_nodet;
- spin_lock_bh(&nr_route_lock);
nr_node_for_each(nr_nodet, &nr_node_list) {
for (i = 0; i < nr_nodet->count; i++)
if (nr_nodet->routes[i].neighbour == nr_neigh)
if (i < nr_nodet->which)
nr_nodet->which = i;
}
- spin_unlock_bh(&nr_route_lock);
}
if (nr_neigh != NULL)
@@ -143,43 +213,19 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
if (quality == 0 && nr_neigh != NULL && nr_node != NULL) {
nr_neigh_put(nr_neigh);
nr_node_put(nr_node);
- return 0;
+ goto out;
}
if (nr_neigh == NULL) {
- if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) {
+ unsigned int q = READ_ONCE(sysctl_netrom_default_path_quality);
+
+ nr_neigh = nr_neigh_create_locked(ax25, ax25_digi, dev, q, 0);
+ if (!nr_neigh) {
if (nr_node)
nr_node_put(nr_node);
- return -ENOMEM;
- }
-
- nr_neigh->callsign = *ax25;
- nr_neigh->digipeat = NULL;
- nr_neigh->ax25 = NULL;
- nr_neigh->dev = dev;
- nr_neigh->quality = READ_ONCE(sysctl_netrom_default_path_quality);
- nr_neigh->locked = 0;
- nr_neigh->count = 0;
- nr_neigh->number = nr_neigh_no++;
- nr_neigh->failed = 0;
- refcount_set(&nr_neigh->refcount, 1);
-
- if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
- nr_neigh->digipeat = kmemdup(ax25_digi,
- sizeof(*ax25_digi),
- GFP_KERNEL);
- if (nr_neigh->digipeat == NULL) {
- kfree(nr_neigh);
- if (nr_node)
- nr_node_put(nr_node);
- return -ENOMEM;
- }
+ ret = -ENOMEM;
+ goto out;
}
-
- spin_lock_bh(&nr_route_lock);
- hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list);
- nr_neigh_hold(nr_neigh);
- spin_unlock_bh(&nr_route_lock);
}
if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked)
@@ -187,9 +233,9 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
if (nr_node == NULL) {
if ((nr_node = kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL) {
- if (nr_neigh)
- nr_neigh_put(nr_neigh);
- return -ENOMEM;
+ nr_neigh_put(nr_neigh);
+ ret = -ENOMEM;
+ goto out;
}
nr_node->callsign = *nr;
@@ -206,15 +252,12 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
nr_neigh_hold(nr_neigh);
nr_neigh->count++;
- spin_lock_bh(&nr_route_lock);
hlist_add_head(&nr_node->node_node, &nr_node_list);
/* refcount initialized at 1 */
- spin_unlock_bh(&nr_route_lock);
nr_neigh_put(nr_neigh);
- return 0;
+ goto out;
}
- spin_lock_bh(&nr_route_lock);
if (quality != 0)
strscpy(nr_node->mnemonic, mnemonic);
@@ -245,11 +288,7 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
} else {
/* It must be better than the worst */
if (quality > nr_node->routes[2].quality) {
- nr_node->routes[2].neighbour->count--;
- nr_neigh_put(nr_node->routes[2].neighbour);
-
- if (nr_node->routes[2].neighbour->count == 0 && !nr_node->routes[2].neighbour->locked)
- nr_remove_neigh_locked(nr_node->routes[2].neighbour);
+ nr_neigh_route_put_locked(nr_node->routes[2].neighbour);
nr_node->routes[2].quality = quality;
nr_node->routes[2].obs_count = obs_count;
@@ -283,9 +322,10 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
}
nr_neigh_put(nr_neigh);
- spin_unlock_bh(&nr_route_lock);
nr_node_put(nr_node);
- return 0;
+out:
+ spin_unlock_bh(&nr_route_lock);
+ return ret;
}
static void nr_remove_node_locked(struct nr_node *nr_node)
@@ -304,13 +344,6 @@ static void nr_remove_neigh_locked(struct nr_neigh *nr_neigh)
nr_neigh_put(nr_neigh);
}
-static void nr_remove_neigh(struct nr_neigh *nr_neigh)
-{
- spin_lock_bh(&nr_route_lock);
- nr_remove_neigh_locked(nr_neigh);
- spin_unlock_bh(&nr_route_lock);
-}
-
/*
* "Delete" a node. Strictly speaking remove a route to a node. The node
* is only deleted if no routes are left to it.
@@ -319,28 +352,25 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n
{
struct nr_node *nr_node;
struct nr_neigh *nr_neigh;
- int i;
+ int i, ret = -EINVAL;
- nr_node = nr_node_get(callsign);
+ spin_lock_bh(&nr_route_lock);
+
+ nr_node = __nr_node_get(callsign);
if (nr_node == NULL)
- return -EINVAL;
+ goto out;
- nr_neigh = nr_neigh_get_dev(neighbour, dev);
+ nr_neigh = __nr_neigh_get_dev(neighbour, dev);
if (nr_neigh == NULL) {
nr_node_put(nr_node);
- return -EINVAL;
+ goto out;
}
- spin_lock_bh(&nr_route_lock);
for (i = 0; i < nr_node->count; i++) {
if (nr_node->routes[i].neighbour == nr_neigh) {
- nr_neigh->count--;
- nr_neigh_put(nr_neigh);
-
- if (nr_neigh->count == 0 && !nr_neigh->locked)
- nr_remove_neigh_locked(nr_neigh);
+ nr_neigh_route_put_locked(nr_neigh);
nr_neigh_put(nr_neigh);
nr_node->count--;
@@ -360,16 +390,15 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n
}
nr_node_put(nr_node);
}
- spin_unlock_bh(&nr_route_lock);
-
- return 0;
+ ret = 0;
+ goto out;
}
}
nr_neigh_put(nr_neigh);
- spin_unlock_bh(&nr_route_lock);
nr_node_put(nr_node);
-
- return -EINVAL;
+out:
+ spin_unlock_bh(&nr_route_lock);
+ return ret;
}
/*
@@ -379,44 +408,25 @@ static int __must_check nr_add_neigh(ax25_address *callsign,
ax25_digi *ax25_digi, struct net_device *dev, unsigned int quality)
{
struct nr_neigh *nr_neigh;
+ int ret = 0;
- nr_neigh = nr_neigh_get_dev(callsign, dev);
+ spin_lock_bh(&nr_route_lock);
+
+ nr_neigh = __nr_neigh_get_dev(callsign, dev);
if (nr_neigh) {
nr_neigh->quality = quality;
nr_neigh->locked = 1;
- nr_neigh_put(nr_neigh);
- return 0;
- }
-
- if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
- return -ENOMEM;
-
- nr_neigh->callsign = *callsign;
- nr_neigh->digipeat = NULL;
- nr_neigh->ax25 = NULL;
- nr_neigh->dev = dev;
- nr_neigh->quality = quality;
- nr_neigh->locked = 1;
- nr_neigh->count = 0;
- nr_neigh->number = nr_neigh_no++;
- nr_neigh->failed = 0;
- refcount_set(&nr_neigh->refcount, 1);
-
- if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
- nr_neigh->digipeat = kmemdup(ax25_digi, sizeof(*ax25_digi),
- GFP_KERNEL);
- if (nr_neigh->digipeat == NULL) {
- kfree(nr_neigh);
- return -ENOMEM;
- }
+ } else {
+ nr_neigh = nr_neigh_create_locked(callsign, ax25_digi, dev,
+ quality, 1);
+ if (!nr_neigh)
+ ret = -ENOMEM;
}
+ if (nr_neigh)
+ nr_neigh_put(nr_neigh);
- spin_lock_bh(&nr_route_lock);
- hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list);
- /* refcount is initialized at 1 */
spin_unlock_bh(&nr_route_lock);
-
- return 0;
+ return ret;
}
/*
@@ -427,17 +437,23 @@ static int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned
{
struct nr_neigh *nr_neigh;
- nr_neigh = nr_neigh_get_dev(callsign, dev);
+ spin_lock_bh(&nr_route_lock);
+
+ nr_neigh = __nr_neigh_get_dev(callsign, dev);
- if (nr_neigh == NULL) return -EINVAL;
+ if (nr_neigh == NULL) {
+ spin_unlock_bh(&nr_route_lock);
+ return -EINVAL;
+ }
nr_neigh->quality = quality;
nr_neigh->locked = 0;
if (nr_neigh->count == 0)
- nr_remove_neigh(nr_neigh);
+ nr_remove_neigh_locked(nr_neigh);
nr_neigh_put(nr_neigh);
+ spin_unlock_bh(&nr_route_lock);
return 0;
}
@@ -463,11 +479,7 @@ static int nr_dec_obs(void)
case 1: /* From 1 -> 0 */
nr_neigh = s->routes[i].neighbour;
- nr_neigh->count--;
- nr_neigh_put(nr_neigh);
-
- if (nr_neigh->count == 0 && !nr_neigh->locked)
- nr_remove_neigh_locked(nr_neigh);
+ nr_neigh_route_put_locked(nr_neigh);
s->count--;
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 015/109] netrom: fix reference count leak in nr_rt_device_down
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (13 preceding siblings ...)
2026-10-28 16:59 ` [PATCH 6.18 014/109] netrom: look up and update routes in one critical section Greg Kroah-Hartman
@ 2026-10-28 16:59 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 016/109] netrom: clear nr_neigh->ax25 under the routing lock in nr_link_failed() Greg Kroah-Hartman
` (100 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 16:59 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Junjie Cao, Bernard Pidoux,
Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junjie Cao <junjie.cao@intel.com>
When a device goes down, nr_rt_device_down() clears the routes that
use this device. However, it fails to drop the per-route reference to
the neighbour (nr_neigh): neither nr_neigh->count nor its refcount
is decremented when the route is removed.
Mirror the behaviour of nr_dec_obs() / nr_del_node() by decrementing
nr_neigh->count and calling nr_neigh_put(), so that the neighbour
reference is properly released when a device goes down.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
[F6BVP: rebased on the single nr_route_lock; the list reference is
still dropped last by nr_remove_neigh_locked(), so the neighbour stays
valid while its routes are removed.]
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit b0f6830b24b75618e299c8e5451367e8742272da)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/netrom/nr_route.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index 10294cf068f8a..67d9ed0006b40 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -526,6 +526,9 @@ void nr_rt_device_down(struct net_device *dev)
nr_node_for_each_safe(t, node2t, &nr_node_list) {
for (i = 0; i < t->count; i++) {
if (t->routes[i].neighbour == s) {
+ s->count--;
+ nr_neigh_put(s);
+
t->count--;
switch (i) {
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 016/109] netrom: clear nr_neigh->ax25 under the routing lock in nr_link_failed()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (14 preceding siblings ...)
2026-10-28 16:59 ` [PATCH 6.18 015/109] netrom: fix reference count leak in nr_rt_device_down Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 017/109] netrom: fix nr_node leak when nr_del_node() removes the last route Greg Kroah-Hartman
` (99 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
nr_route_frame() swaps nr_neigh->ax25 for the ax25_cb returned by
ax25_send_frame() and puts the old one while holding nr_route_lock.
nr_link_failed(), called by AX.25 when that link fails, looks the
neighbour up under the lock but then clears nr_neigh->ax25, puts the
ax25_cb and bumps nr_neigh->failed after dropping it. If a frame is
routed to the same neighbour at that moment, both paths can see the
same ax25_cb and put it twice.
Do the whole of nr_link_failed() in one critical section. The
neighbour no longer needs a temporary reference, since it cannot be
unlinked while the lock is held.
This depends on "netrom: use a single lock for the node and neighbour
lists".
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit 6e6f80bff5c901f317c3750cdf375766800ecc1a)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/netrom/nr_route.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index 67d9ed0006b40..98ba317ada637 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -715,34 +715,35 @@ void nr_link_failed(ax25_cb *ax25, int reason)
struct nr_neigh *s, *nr_neigh = NULL;
struct nr_node *nr_node = NULL;
+ /*
+ * nr_route_frame() replaces nr_neigh->ax25 under nr_route_lock;
+ * clear it and drop its reference under the same lock, so that the
+ * two paths cannot both put the same ax25_cb.
+ */
spin_lock_bh(&nr_route_lock);
nr_neigh_for_each(s, &nr_neigh_list) {
if (s->ax25 == ax25) {
- nr_neigh_hold(s);
nr_neigh = s;
break;
}
}
- spin_unlock_bh(&nr_route_lock);
if (nr_neigh == NULL)
- return;
+ goto out;
nr_neigh->ax25 = NULL;
ax25_cb_put(ax25);
- if (++nr_neigh->failed < READ_ONCE(sysctl_netrom_link_fails_count)) {
- nr_neigh_put(nr_neigh);
- return;
- }
- spin_lock_bh(&nr_route_lock);
+ if (++nr_neigh->failed < READ_ONCE(sysctl_netrom_link_fails_count))
+ goto out;
+
nr_node_for_each(nr_node, &nr_node_list) {
if (nr_node->which < nr_node->count &&
nr_node->routes[nr_node->which].neighbour == nr_neigh)
nr_node->which++;
}
+out:
spin_unlock_bh(&nr_route_lock);
- nr_neigh_put(nr_neigh);
}
/*
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 017/109] netrom: fix nr_node leak when nr_del_node() removes the last route
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (15 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 016/109] netrom: clear nr_neigh->ax25 under the routing lock in nr_link_failed() Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 018/109] rose: clear through routes when a neighbour is removed from the tables Greg Kroah-Hartman
` (98 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
nr_del_node() takes a reference on the node with its lookup. When the
route it deletes is not the last one, it drops that reference before
returning. When it is the last one, it only unlinks the node with
nr_remove_node_locked(), which drops the list's reference, and returns
still holding its own: the unlinked node is never freed.
Seen with kmemleak on a live node after adding and then deleting a
route to a dummy node with nrparms (SIOCADDRT then SIOCDELRT): the
96-byte nr_node allocated by nr_add_node(), callsign and mnemonic of
the dummy node in its hex dump, is reported as unreferenced.
Drop the lookup reference in both cases.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit a4b96115783fdefae9599f9a84a39e24fb288d6b)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/netrom/nr_route.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index 98ba317ada637..288c4a91ea9b7 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -388,8 +388,9 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n
case 2:
break;
}
- nr_node_put(nr_node);
}
+ /* Drop the reference taken by __nr_node_get() */
+ nr_node_put(nr_node);
ret = 0;
goto out;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 018/109] rose: clear through routes when a neighbour is removed from the tables
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (16 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 017/109] netrom: fix nr_node leak when nr_del_node() removes the last route Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 019/109] rose: restart the link of a neighbour created over a live AX.25 connection Greg Kroah-Hartman
` (97 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
When the last route using a neighbour is deleted (SIOCDELRT) or all
routes are cleared (SIOCRSCLRRT), rose_del_node() and
rose_clear_routes() take the neighbour out of rose_neigh_list, but the
through routes in rose_route_list that still use it are left alone.
No frame can reach them any more, so they stay in the table for ever,
holding their neighbour references, and neither end of the relayed
connection is told: the caller and the called station keep a circuit
that goes nowhere. rose_clear_routes() even says it keeps neighbours
with active connections, which it does not.
This shows up on every node that relays traffic when its routing
daemon restarts: FPAC clears and reloads the routes, the neighbours are
recreated under new numbers, and the old through routes remain listed
with neighbours that no longer exist.
Clear them when the neighbour is removed, as rose_link_failed() does
when a link fails, but send the CLEAR to both halves since the removed
neighbour's link may still be up, and cut the local connections using
it. rose_route_list_lock is taken under rose_node_list_lock and
rose_neigh_list_lock, the order rose_route_frame() and
rose_link_failed() already use.
Tested in QEMU with a lockdep and KASAN kernel, three nodes A - B - C
with A calling C through B: before, the through route stays on B after
SIOCDELRT or SIOCRSCLRRT and A is never told; after, the entry is gone
and both A and C get a CLEAR (cause 0x09) within a second. No lockdep,
KASAN or refcount report.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit 065ae03f519c147d5ee7b87acb2d21fe93ae0007)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/rose/rose_route.c | 49 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c
index fd196f2e2fd64..87844cc195f8d 100644
--- a/net/rose/rose_route.c
+++ b/net/rose/rose_route.c
@@ -285,6 +285,49 @@ static void rose_remove_route(struct rose_route *rose_route)
}
}
+/*
+ * A neighbour is being removed from the tables while its link may still
+ * be up (route deleted or routes cleared by the configuration). Clear the
+ * through routes using it, on both sides since both can still be reached,
+ * instead of leaving them in rose_route_list for ever: no frame can reach
+ * them once the neighbour is gone. Local connections using it are cut too,
+ * as rose_link_failed() does.
+ *
+ * Caller is holding rose_node_list_lock and rose_neigh_list_lock, taken
+ * before rose_route_list_lock as in rose_route_frame() and
+ * rose_link_failed().
+ */
+static void rose_neigh_clear_routes(struct rose_neigh *rose_neigh)
+{
+ struct rose_route *rose_route, *s;
+
+ spin_lock_bh(&rose_route_list_lock);
+
+ rose_route = rose_route_list;
+ while (rose_route != NULL) {
+ s = rose_route->next;
+
+ if (rose_route->neigh1 == rose_neigh ||
+ rose_route->neigh2 == rose_neigh) {
+ if (rose_route->neigh1 != NULL)
+ rose_transmit_clear_request(rose_route->neigh1,
+ rose_route->lci1,
+ ROSE_OUT_OF_ORDER, 0);
+ if (rose_route->neigh2 != NULL)
+ rose_transmit_clear_request(rose_route->neigh2,
+ rose_route->lci2,
+ ROSE_OUT_OF_ORDER, 0);
+ rose_remove_route(rose_route);
+ }
+
+ rose_route = s;
+ }
+
+ spin_unlock_bh(&rose_route_list_lock);
+
+ rose_kill_by_neigh(rose_neigh);
+}
+
/*
* "Delete" a node. Strictly speaking remove a route to a node. The node
* is only deleted if no routes are left to it.
@@ -333,6 +376,7 @@ static int rose_del_node(struct rose_route_struct *rose_route,
rose_neigh_put(rose_neigh);
if (rose_neigh->count == 0) {
+ rose_neigh_clear_routes(rose_neigh);
rose_remove_neigh(rose_neigh);
rose_neigh_put(rose_neigh);
}
@@ -547,8 +591,8 @@ void rose_route_device_down(struct net_device *dev)
#endif
/*
- * Clear all nodes and neighbours out, except for neighbours with
- * active connections going through them.
+ * Clear all nodes and neighbours out, with the through routes and
+ * connections using those neighbours.
* Do not clear loopback neighbour and nodes.
*/
static int rose_clear_routes(void)
@@ -579,6 +623,7 @@ static int rose_clear_routes(void)
rose_neigh = rose_neigh->next;
if (!s->loopback) {
+ rose_neigh_clear_routes(s);
rose_remove_neigh(s);
rose_neigh_put(s);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 019/109] rose: restart the link of a neighbour created over a live AX.25 connection
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (17 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 018/109] rose: clear through routes when a neighbour is removed from the tables Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 020/109] ax25: serialise the transmission of I frames on a connection Greg Kroah-Hartman
` (96 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
A new neighbour starts with restarted == 0 and waits for the first frame
sent to it to start the restart procedure. But when a neighbour is
removed from the routing tables and added back (SIOCDELRT then
SIOCADDRT, or SIOCRSCLRRT followed by a reload of the routes), the AX.25
connection to it outlives the old entry. The other end has seen nothing:
it still regards the link as restarted and will not restart it. On this
end rose_get_neigh() only hands out restarted neighbours to through
calls, so no frame is ever sent to the new neighbour and nothing starts
the restart either. Every call relayed through that neighbour is then
refused until one of the two ends happens to originate a call of its own
over the link, or the AX.25 connection drops.
This happens on a relay node each time its routing daemon restarts: FPAC
clears and reloads the routes, the neighbours come back as "restart no"
while /proc/net/ax25 still shows their connections established.
When rose_add_node() creates a neighbour and an AX.25 connection to it
already exists, send the RESTART REQUEST right away and start T0, as
rose_transmit_link() does for the first frame. Nothing changes when
there is no connection yet: the link is still brought up on demand.
Tested in QEMU with a lockdep and KASAN kernel, three nodes A - B - C
with A calling C through B, then B removing and re-adding its route to
C (or clearing and reloading all its routes): before, the neighbour
stays "restart no" on B and A's calls to C are refused for as long as
the test runs; after, the neighbour is restarted within three seconds
and the first call goes through. No lockdep, KASAN or refcount report.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit 411b950c8862277a53f4afc6c6c5b3ab00b814be)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
include/net/rose.h | 1 +
net/rose/rose_link.c | 20 ++++++++++++++++++++
net/rose/rose_route.c | 10 ++++++++++
3 files changed, 31 insertions(+)
diff --git a/include/net/rose.h b/include/net/rose.h
index 49140668b3dd0..f5b23c8a11e4d 100644
--- a/include/net/rose.h
+++ b/include/net/rose.h
@@ -237,6 +237,7 @@ void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *,
unsigned short);
void rose_transmit_clear_request(struct rose_neigh *, unsigned int,
unsigned char, unsigned char);
+void rose_link_restart(struct rose_neigh *neigh);
void rose_transmit_link(struct sk_buff *, struct rose_neigh *);
/* rose_loopback.c */
diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c
index 36b6778d8e545..20057a39bafaa 100644
--- a/net/rose/rose_link.c
+++ b/net/rose/rose_link.c
@@ -259,6 +259,26 @@ void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, uns
kfree_skb(skb);
}
+/*
+ * A neighbour has just been created while an AX.25 connection to it is
+ * already up, e.g. it was removed from the routing tables and added back.
+ * The other end still regards the link as restarted and will not restart it,
+ * and through calls only use restarted neighbours, so nothing would ever
+ * restart it from this end either: do it now.
+ */
+void rose_link_restart(struct rose_neigh *neigh)
+{
+ if (neigh->loopback || neigh->restarted || rose_t0timer_running(neigh))
+ return;
+
+ if (!rose_link_up(neigh))
+ return;
+
+ rose_transmit_restart_request(neigh);
+ neigh->dce_mode = 0;
+ rose_start_t0timer(neigh);
+}
+
void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh)
{
unsigned char *dptr;
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c
index 87844cc195f8d..7ae3552c19267 100644
--- a/net/rose/rose_route.c
+++ b/net/rose/rose_route.c
@@ -53,6 +53,7 @@ static int __must_check rose_add_node(struct rose_route_struct *rose_route,
{
struct rose_node *rose_node, *rose_tmpn, *rose_tmpp;
struct rose_neigh *rose_neigh;
+ bool new_neigh = false;
int i, res = 0;
spin_lock_bh(&rose_node_list_lock);
@@ -125,6 +126,7 @@ static int __must_check rose_add_node(struct rose_route_struct *rose_route,
rose_neigh->next = rose_neigh_list;
rose_neigh_list = rose_neigh;
+ new_neigh = true;
}
/*
@@ -192,6 +194,14 @@ static int __must_check rose_add_node(struct rose_route_struct *rose_route,
}
out:
+ /*
+ * The AX.25 connection of a neighbour that was removed from the tables
+ * outlives it. If it is still there, the other end will not restart
+ * the link: do it from here.
+ */
+ if (new_neigh && res == 0)
+ rose_link_restart(rose_neigh);
+
spin_unlock_bh(&rose_neigh_list_lock);
spin_unlock_bh(&rose_node_list_lock);
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 020/109] ax25: serialise the transmission of I frames on a connection
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (18 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 019/109] rose: restart the link of a neighbour created over a live AX.25 connection Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 021/109] rose: process received frames and send with the socket locked Greg Kroah-Hartman
` (95 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25_kick() takes frames off the write queue, numbers them from V(S)
and moves them to the ack queue, with no lock. It is called from
process context by the senders (ax25_output()) and from softirq context
by the receive path and the timers. A connection used by ROSE or NET/ROM
carries several circuits, so ax25_output() can run for the same
connection on two CPUs at once, and against the receive path. Both then
read the same V(S) and two different I frames are sent under the same
N(S). The peer keeps the first and rejects the second; the go-back-N
that follows resends whatever the ack queue holds for that number, and
one of the two frames is never sent again while the AX.25 counters look
consistent on both sides. The layer above sees a hole: a ROSE circuit
receives its frames out of sequence, ignores them, and stays frozen
with its window full.
Seen on a live ROSE node relaying several circuits over one AX.25 link:
fm F6BVP-9 to F6BVP-13 ctl I55^ pid=1(X.25) len 254 LCI 030 DATA S2
fm F6BVP-9 to F6BVP-13 ctl I55^ pid=1(X.25) len 254 LCI 02E DATA S2
fm F6BVP-13 to F6BVP-9 ctl REJ6v
Add a lock to the ax25_cb covering V(S), V(A) and the write and ack
queues, taken in ax25_kick(), ax25_frames_acked() and
ax25_requeue_frames(). The frames are not sent with the lock held,
since ax25_transmit_buffer() may end up in ax25_disconnect(): ax25_kick()
puts the numbered frames on a transmit queue which is emptied, in
order, by one context at a time once the lock is dropped.
Tested in QEMU, two 4-CPU nodes with lockdep and KASAN over a KISS
serial link, four ROSE circuits each sending 400 numbered messages at
the same time: before, every circuit freezes and 300 to 600 of the 1600
messages get through; after, no sender is blocked and more than 1500
get through (the ROSE side needs "rose: process received frames and
send with the socket locked" for the rest: 1600/1600 in order on six
runs out of six with both). No lockdep, KASAN or refcount report.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
[F6BVP: v2: in ax25_frames_acked(), loop on what skb_dequeue() returns
instead of on skb_peek(): ax25_clear_queues() purges the ack queue
without ax25->lock, so skb_dequeue() could return NULL and
__skb_queue_tail() dereference it in softirq context (reported by
Sasha Levin). Same fix submitted to mod-orphan.]
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit 3f42b974059565c9e60229a9ff1ca21d10334671)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
include/net/ax25.h | 5 ++++
net/ax25/af_ax25.c | 2 ++
net/ax25/ax25_out.c | 56 ++++++++++++++++++++++++++++++++++++++++----
net/ax25/ax25_subr.c | 37 ++++++++++++++++++++++-------
4 files changed, 88 insertions(+), 12 deletions(-)
diff --git a/include/net/ax25.h b/include/net/ax25.h
index a7bba42dde153..0fe92fa6ef075 100644
--- a/include/net/ax25.h
+++ b/include/net/ax25.h
@@ -252,6 +252,11 @@ typedef struct ax25_cb {
struct sk_buff_head reseq_queue;
struct sk_buff_head ack_queue;
struct sk_buff_head frag_queue;
+ /* I frames numbered by ax25_kick(), waiting to go to the device */
+ struct sk_buff_head xmit_queue;
+ /* serialises V(S)/V(A) and the write, ack and xmit queues */
+ spinlock_t lock;
+ unsigned char xmit_running;
unsigned char window;
struct timer_list timer, dtimer;
struct sock *sk; /* Backlink to socket */
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index 23c558ff96822..d76fe0cbc57b3 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -537,6 +537,8 @@ ax25_cb *ax25_create_cb(void)
skb_queue_head_init(&ax25->frag_queue);
skb_queue_head_init(&ax25->ack_queue);
skb_queue_head_init(&ax25->reseq_queue);
+ skb_queue_head_init(&ax25->xmit_queue);
+ spin_lock_init(&ax25->lock);
ax25_setup_timers(ax25);
diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c
index 8bca2ace98e51..1b9a91b9c9831 100644
--- a/net/ax25/ax25_out.c
+++ b/net/ax25/ax25_out.c
@@ -218,6 +218,11 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
* This procedure is passed a buffer descriptor for an iframe. It builds
* the rest of the control part of the frame and then writes it out.
*/
+/*
+ * Number an I frame and queue it for the device. Called by ax25_kick()
+ * with ax25->lock held: the frame is sent by ax25_run_xmit_queue() once
+ * the lock is dropped.
+ */
static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit)
{
unsigned char *frame;
@@ -245,7 +250,34 @@ static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit)
ax25_start_idletimer(ax25);
- ax25_transmit_buffer(ax25, skb, AX25_COMMAND);
+ skb_queue_tail(&ax25->xmit_queue, skb);
+}
+
+/*
+ * Hand the frames numbered by ax25_kick() to the device, in order. Only
+ * one context does it at a time, so that frames queued by two senders
+ * cannot overtake each other; the one already running picks up whatever
+ * the others have queued. The lock is not held while transmitting.
+ */
+static void ax25_run_xmit_queue(ax25_cb *ax25)
+{
+ struct sk_buff *skb;
+
+ spin_lock_bh(&ax25->lock);
+ if (ax25->xmit_running) {
+ spin_unlock_bh(&ax25->lock);
+ return;
+ }
+ ax25->xmit_running = 1;
+
+ while ((skb = skb_dequeue(&ax25->xmit_queue)) != NULL) {
+ spin_unlock_bh(&ax25->lock);
+ ax25_transmit_buffer(ax25, skb, AX25_COMMAND);
+ spin_lock_bh(&ax25->lock);
+ }
+
+ ax25->xmit_running = 0;
+ spin_unlock_bh(&ax25->lock);
}
void ax25_kick(ax25_cb *ax25)
@@ -260,14 +292,25 @@ void ax25_kick(ax25_cb *ax25)
if (ax25->condition & AX25_COND_PEER_RX_BUSY)
return;
+ /*
+ * ax25_kick() is called from process context by the senders and
+ * from softirq context by the receive path and the timers, possibly
+ * for several ROSE or NET/ROM circuits sharing this connection. Two
+ * of them running here at the same time would read the same V(S) and
+ * send two different frames under the same N(S); the peer keeps one
+ * and the other is never sent again. Take the frames off the write
+ * queue and number them under the lock.
+ */
+ spin_lock_bh(&ax25->lock);
+
if (skb_peek(&ax25->write_queue) == NULL)
- return;
+ goto out;
start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs;
end = (ax25->va + ax25->window) % ax25->modulus;
if (start == end)
- return;
+ goto out;
/*
* Transmit data until either we're out of data to send or
@@ -281,7 +324,7 @@ void ax25_kick(ax25_cb *ax25)
*/
skb = skb_dequeue(&ax25->write_queue);
if (!skb)
- return;
+ goto out;
ax25->vs = start;
@@ -331,6 +374,11 @@ void ax25_kick(ax25_cb *ax25)
ax25_calculate_t1(ax25);
ax25_start_t1timer(ax25);
}
+
+out:
+ spin_unlock_bh(&ax25->lock);
+
+ ax25_run_xmit_queue(ax25);
}
void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type)
diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c
index 12c027beadb69..a1e33092e6d88 100644
--- a/net/ax25/ax25_subr.c
+++ b/net/ax25/ax25_subr.c
@@ -34,6 +34,7 @@ void ax25_clear_queues(ax25_cb *ax25)
{
skb_queue_purge(&ax25->write_queue);
skb_queue_purge(&ax25->ack_queue);
+ skb_queue_purge(&ax25->xmit_queue);
skb_queue_purge(&ax25->reseq_queue);
skb_queue_purge(&ax25->frag_queue);
}
@@ -45,31 +46,51 @@ void ax25_clear_queues(ax25_cb *ax25)
*/
void ax25_frames_acked(ax25_cb *ax25, unsigned short nr)
{
+ struct sk_buff_head acked;
struct sk_buff *skb;
+ __skb_queue_head_init(&acked);
+
/*
- * Remove all the ack-ed frames from the ack queue.
+ * Remove all the ack-ed frames from the ack queue. ax25_kick() may be
+ * filling it from another CPU: move V(A) and the queue together, and
+ * free the frames once the lock is dropped.
*/
- if (ax25->va != nr) {
- while (skb_peek(&ax25->ack_queue) != NULL && ax25->va != nr) {
- skb = skb_dequeue(&ax25->ack_queue);
- kfree_skb(skb);
- ax25->va = (ax25->va + 1) % ax25->modulus;
- }
+ spin_lock_bh(&ax25->lock);
+ /*
+ * ax25_clear_queues() purges the ack queue without ax25->lock: test
+ * what skb_dequeue() returns, not what skb_peek() saw before it.
+ */
+ while (ax25->va != nr && (skb = skb_dequeue(&ax25->ack_queue)) != NULL) {
+ __skb_queue_tail(&acked, skb);
+ ax25->va = (ax25->va + 1) % ax25->modulus;
}
+ spin_unlock_bh(&ax25->lock);
+
+ __skb_queue_purge(&acked);
}
void ax25_requeue_frames(ax25_cb *ax25)
{
+ struct sk_buff_head stale;
struct sk_buff *skb;
+ __skb_queue_head_init(&stale);
+
/*
* Requeue all the un-ack-ed frames on the output queue to be picked
* up by ax25_kick called from the timer. This arrangement handles the
- * possibility of an empty output queue.
+ * possibility of an empty output queue. Copies that ax25_kick() has
+ * numbered but not sent yet are stale: they will be sent again.
*/
+ spin_lock_bh(&ax25->lock);
+ while ((skb = skb_dequeue(&ax25->xmit_queue)) != NULL)
+ __skb_queue_tail(&stale, skb);
while ((skb = skb_dequeue_tail(&ax25->ack_queue)) != NULL)
skb_queue_head(&ax25->write_queue, skb);
+ spin_unlock_bh(&ax25->lock);
+
+ __skb_queue_purge(&stale);
}
/*
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 021/109] rose: process received frames and send with the socket locked
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (19 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 020/109] ax25: serialise the transmission of I frames on a connection Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 022/109] ax25: dont create two connections for the same link in ax25_send_frame() Greg Kroah-Hartman
` (94 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
rose_kick() takes frames off the write queue, numbers them from V(S)
and moves them to the ack queue. It runs from rose_sendmsg() in process
context and from rose_process_rx_frame() in softirq context, and neither
holds the socket lock. When a frame for the socket is received on one
CPU while a process is sending on another, both read the same V(S) and
two different frames go out under the same N(S), or the queues and
V(S)/V(A) are updated by one while the other walks them. The peer drops
the frames it sees as out of sequence, and since ROSE has no REJ and no
retransmission in state 3 the data is lost for good: messages go
missing from the stream, or the window fills and the circuit freezes.
The timers already take the socket lock and retry when a process owns
it. Do the same for the two remaining paths: rose_sendmsg() calls
rose_kick() under lock_sock(), and received frames go through
rose_rcv_frame(), which takes bh_lock_sock() and queues the frame on the
socket backlog when a process owns the socket, as NET/ROM and X.25 do.
Tested in QEMU, two 4-CPU nodes with lockdep and KASAN over a KISS
serial link, four ROSE circuits each sending 400 numbered messages at
the same time (together with the AX.25 fix "ax25: serialise the
transmission of I frames on a connection", without which the same
happens one layer down): before, 60 to 90 of the 1600 messages are
missing from the streams; after, 1600/1600 delivered in order on six
runs out of six. No lockdep, KASAN or refcount report.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit 831b23b977e1fdf14e51dd9652fd27f30965ecb0)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
include/net/rose.h | 2 ++
net/rose/af_rose.c | 5 +++++
net/rose/rose_in.c | 31 +++++++++++++++++++++++++++++++
net/rose/rose_loopback.c | 2 +-
net/rose/rose_route.c | 2 +-
5 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/include/net/rose.h b/include/net/rose.h
index f5b23c8a11e4d..6ce065480a4e6 100644
--- a/include/net/rose.h
+++ b/include/net/rose.h
@@ -227,6 +227,8 @@ void rose_setup(struct net_device *);
/* rose_in.c */
int rose_process_rx_frame(struct sock *, struct sk_buff *);
+int rose_rcv_frame(struct sock *sk, struct sk_buff *skb);
+int rose_backlog_rcv(struct sock *sk, struct sk_buff *skb);
/* rose_link.c */
void rose_start_ftimer(struct rose_neigh *);
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c
index 48b82fe3bee18..4fca64156640e 100644
--- a/net/rose/af_rose.c
+++ b/net/rose/af_rose.c
@@ -598,6 +598,7 @@ static int rose_create(struct net *net, struct socket *sock, int protocol,
rose = rose_sk(sk);
sock_init_data(sock, sk);
+ sk->sk_backlog_rcv = rose_backlog_rcv;
skb_queue_head_init(&rose->ack_queue);
#ifdef M_BIT
@@ -637,6 +638,7 @@ static struct sock *rose_make_new(struct sock *osk)
rose = rose_sk(sk);
sock_init_data(NULL, sk);
+ sk->sk_backlog_rcv = rose_backlog_rcv;
skb_queue_head_init(&rose->ack_queue);
#ifdef M_BIT
@@ -1326,7 +1328,10 @@ static int rose_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
skb_queue_tail(&sk->sk_write_queue, skb); /* Shove it onto the queue */
#endif
+ /* The receive path and the timers run rose_kick() too */
+ lock_sock(sk);
rose_kick(sk);
+ release_sock(sk);
return len;
}
diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c
index ac20f318008d4..4eec54b24afb0 100644
--- a/net/rose/rose_in.c
+++ b/net/rose/rose_in.c
@@ -310,3 +310,34 @@ int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb)
return queued;
}
+
+/*
+ * Frames for a socket are processed with the socket locked, as the timers
+ * already do: rose_kick() may be running in rose_sendmsg() on another CPU,
+ * and two of them reading the same V(S) send two different frames under the
+ * same N(S). If a process owns the socket the frame goes to its backlog and
+ * is processed when the socket is released. Returns non-zero if the skb has
+ * been consumed.
+ */
+int rose_rcv_frame(struct sock *sk, struct sk_buff *skb)
+{
+ int queued;
+
+ bh_lock_sock(sk);
+ if (!sock_owned_by_user(sk))
+ queued = rose_process_rx_frame(sk, skb);
+ else
+ queued = !sk_add_backlog(sk, skb, READ_ONCE(sk->sk_rcvbuf) +
+ READ_ONCE(sk->sk_sndbuf));
+ bh_unlock_sock(sk);
+
+ return queued;
+}
+
+int rose_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ if (!rose_process_rx_frame(sk, skb))
+ kfree_skb(skb);
+
+ return 0;
+}
diff --git a/net/rose/rose_loopback.c b/net/rose/rose_loopback.c
index fd4e4795776a3..4e4a473910cc8 100644
--- a/net/rose/rose_loopback.c
+++ b/net/rose/rose_loopback.c
@@ -107,7 +107,7 @@ static void rose_loopback_timer(struct timer_list *unused)
sk = rose_find_socket(lci_o, rose_loopback_neigh);
if (sk) {
- if (rose_process_rx_frame(sk, skb) == 0)
+ if (rose_rcv_frame(sk, skb) == 0)
kfree_skb(skb);
continue;
}
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c
index 7ae3552c19267..883b204121566 100644
--- a/net/rose/rose_route.c
+++ b/net/rose/rose_route.c
@@ -1017,7 +1017,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
}
else {
skb_reset_transport_header(skb);
- res = rose_process_rx_frame(sk, skb);
+ res = rose_rcv_frame(sk, skb);
goto out;
}
}
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 022/109] ax25: dont create two connections for the same link in ax25_send_frame()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (20 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 021/109] rose: process received frames and send with the socket locked Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 023/109] ax25: read the connections ax25_dev only once when walking ax25_list Greg Kroah-Hartman
` (93 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25_send_frame() looks for an existing connection with ax25_find_cb()
and, when there is none, creates one and adds it to the list. The
lookup and the insertion are not done under the same lock, so two
callers sending to a neighbour at the same time both find nothing and
both create a control block: the same link ends up with several
connections, each sending its own SABM, and the frames queued on the
ones that lose are never delivered.
ROSE and NET/ROM call ax25_send_frame() for every frame they send to a
neighbour, from process context and from softirq context, so this
happens as soon as several circuits start together on a link that is
not established yet.
Add ax25_cb_add_unique(), which searches the list and inserts the new
control block under ax25_list_lock, and returns the existing connection
when another sender got there first; ax25_send_frame() then drops its
own control block and queues the frame on that connection. The SABM is
only sent once the control block is known to be the one in the list.
Tested in QEMU, two 4-CPU nodes with lockdep and KASAN over a KISS
serial link, eight ROSE calls started at the same instant with no AX.25
connection up: before, /proc/net/ax25 shows four connections for the
same pair of callsigns on three runs out of three; after, one. No
lockdep, KASAN or refcount report.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit 2649f91b6dd2c48348975d03e8f94d8e6a09cf71)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
include/net/ax25.h | 1 +
net/ax25/af_ax25.c | 49 ++++++++++++++++++++++++++++++++++++++-------
net/ax25/ax25_out.c | 16 ++++++++++++---
3 files changed, 56 insertions(+), 10 deletions(-)
diff --git a/include/net/ax25.h b/include/net/ax25.h
index 0fe92fa6ef075..e3c10a9395944 100644
--- a/include/net/ax25.h
+++ b/include/net/ax25.h
@@ -311,6 +311,7 @@ static inline __be16 ax25_type_trans(struct sk_buff *skb, struct net_device *dev
extern struct hlist_head ax25_list;
extern spinlock_t ax25_list_lock;
void ax25_cb_add(ax25_cb *);
+ax25_cb *ax25_cb_add_unique(ax25_cb *ax25);
struct sock *ax25_find_listener(ax25_address *, int, struct net_device *, int);
struct sock *ax25_get_socket(ax25_address *, ax25_address *, int);
ax25_cb *ax25_find_cb(const ax25_address *, ax25_address *, ax25_digi *,
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index d76fe0cbc57b3..28b7a89afaf6e 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -222,12 +222,12 @@ struct sock *ax25_get_socket(ax25_address *my_addr, ax25_address *dest_addr,
* Find an AX.25 control block given both ends. It will only pick up
* floating AX.25 control blocks or non Raw socket bound control blocks.
*/
-ax25_cb *ax25_find_cb(const ax25_address *src_addr, ax25_address *dest_addr,
- ax25_digi *digi, struct net_device *dev)
+static ax25_cb *__ax25_find_cb(const ax25_address *src_addr,
+ ax25_address *dest_addr, ax25_digi *digi,
+ struct net_device *dev)
{
ax25_cb *s;
- spin_lock_bh(&ax25_list_lock);
ax25_for_each(s, &ax25_list) {
if (s->sk && s->sk->sk_type != SOCK_SEQPACKET)
continue;
@@ -243,19 +243,54 @@ ax25_cb *ax25_find_cb(const ax25_address *src_addr, ax25_address *dest_addr,
if (s->digipeat != NULL && s->digipeat->ndigi != 0)
continue;
}
- ax25_cb_hold(s);
- spin_unlock_bh(&ax25_list_lock);
-
return s;
}
}
- spin_unlock_bh(&ax25_list_lock);
return NULL;
}
+ax25_cb *ax25_find_cb(const ax25_address *src_addr, ax25_address *dest_addr,
+ ax25_digi *digi, struct net_device *dev)
+{
+ ax25_cb *s;
+
+ spin_lock_bh(&ax25_list_lock);
+ s = __ax25_find_cb(src_addr, dest_addr, digi, dev);
+ if (s)
+ ax25_cb_hold(s);
+ spin_unlock_bh(&ax25_list_lock);
+
+ return s;
+}
+
EXPORT_SYMBOL(ax25_find_cb);
+/*
+ * Add a new connection to the list unless one already exists for the
+ * same addresses, path and device. Returns NULL when the control block
+ * has been added, or the existing one, held, when there is one: looking
+ * it up and adding it are done under the same lock, so that two senders
+ * cannot both create a connection for the same link.
+ */
+ax25_cb *ax25_cb_add_unique(ax25_cb *ax25)
+{
+ ax25_cb *s;
+
+ spin_lock_bh(&ax25_list_lock);
+ s = __ax25_find_cb(&ax25->source_addr, &ax25->dest_addr,
+ ax25->digipeat, ax25->ax25_dev->dev);
+ if (s) {
+ ax25_cb_hold(s);
+ } else {
+ ax25_cb_hold(ax25);
+ hlist_add_head(&ax25->ax25_node, &ax25_list);
+ }
+ spin_unlock_bh(&ax25_list_lock);
+
+ return s;
+}
+
void ax25_send_to_raw(ax25_address *addr, struct sk_buff *skb, int proto)
{
ax25_cb *s;
diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c
index 1b9a91b9c9831..eeb2aa1e2186f 100644
--- a/net/ax25/ax25_out.c
+++ b/net/ax25/ax25_out.c
@@ -32,7 +32,7 @@ static DEFINE_SPINLOCK(ax25_frag_lock);
ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, const ax25_address *src, ax25_address *dest, ax25_digi *digi, struct net_device *dev)
{
ax25_dev *ax25_dev;
- ax25_cb *ax25;
+ ax25_cb *ax25, *existing;
/*
* Take the default packet length for the device if zero is
@@ -82,6 +82,18 @@ ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, const ax25_address *sr
}
}
+ /*
+ * Another sender may have created the connection since the lookup
+ * above: the list is searched again and the new control block added
+ * under one lock. If we lost, use the other one.
+ */
+ existing = ax25_cb_add_unique(ax25);
+ if (existing) {
+ ax25_cb_put(ax25);
+ ax25_output(existing, paclen, skb);
+ return existing;
+ }
+
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
@@ -104,8 +116,6 @@ ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, const ax25_address *sr
*/
ax25_cb_hold(ax25);
- ax25_cb_add(ax25);
-
ax25->state = AX25_STATE_1;
ax25_start_heartbeat(ax25);
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 023/109] ax25: read the connections ax25_dev only once when walking ax25_list
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (21 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 022/109] ax25: dont create two connections for the same link in ax25_send_frame() Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 024/109] ax25: wait for ax25_send_frame() before detaching the connections of a device Greg Kroah-Hartman
` (92 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25_kill_by_device() detaches a connection from a device that goes
down by setting s->ax25_dev to NULL. It does so after it has dropped
ax25_list_lock, while the connection is still on ax25_list.
__ax25_find_cb() and ax25_find_listener() walk that list and test
s->ax25_dev against NULL before they dereference it, but they load the
pointer a second time for the dereference. ax25_send_to_raw()
dereferences it without any test. If ax25_kill_by_device() clears the
pointer in between, the walker dereferences NULL:
Oops: general protection fault, probably for non-canonical address
KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017]
RIP: 0010:ax25_find_cb+0x121/0x330 [ax25]
ax25_send_frame+0xdc/0x980 [ax25]
rose_send_frame+0xce/0x270 [rose]
rose_transmit_restart_request+0x1b3/0x260 [rose]
rose_transmit_link+0x31d/0x560 [rose]
rose_write_internal+0x3ab/0x15b0 [rose]
rose_release+0x2b2/0x4b0 [rose]
Load the pointer once in the three walkers and use that value, and add
the missing NULL test to ax25_send_to_raw(). The ax25_dev itself cannot
go away under the walker: ax25_kill_by_device() has to take
ax25_list_lock again, in ax25_cb_del(), before the device teardown gets
as far as freeing it.
The oops above was obtained on 6.12.94 with KASAN, after 382 seconds of
the syzbot C reproducer of the ax25_find_cb() use-after-free report,
with the interface brought up again in a loop, while testing an earlier
version of the next patch. With this series the same test runs for 600
seconds without an oops.
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
Assisted-by: Claude:claude-opus-5-5
(cherry picked from commit 3696f3cf8690e335a2cc7e8103f018bc205de738)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/ax25/af_ax25.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index 28b7a89afaf6e..76f6848fba7d6 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -91,7 +91,7 @@ static void ax25_kill_by_device(struct net_device *dev)
if (!sk) {
spin_unlock_bh(&ax25_list_lock);
ax25_disconnect(s, ENETUNREACH);
- s->ax25_dev = NULL;
+ WRITE_ONCE(s->ax25_dev, NULL);
ax25_cb_del(s);
spin_lock_bh(&ax25_list_lock);
goto again;
@@ -100,7 +100,7 @@ static void ax25_kill_by_device(struct net_device *dev)
spin_unlock_bh(&ax25_list_lock);
lock_sock(sk);
ax25_disconnect(s, ENETUNREACH);
- s->ax25_dev = NULL;
+ WRITE_ONCE(s->ax25_dev, NULL);
if (sk->sk_socket) {
netdev_put(ax25_dev->dev,
&s->dev_tracker);
@@ -172,6 +172,7 @@ void ax25_cb_add(ax25_cb *ax25)
struct sock *ax25_find_listener(ax25_address *addr, int digi,
struct net_device *dev, int type)
{
+ ax25_dev *ax25_dev;
ax25_cb *s;
spin_lock(&ax25_list_lock);
@@ -181,7 +182,8 @@ struct sock *ax25_find_listener(ax25_address *addr, int digi,
if (s->sk && !ax25cmp(&s->source_addr, addr) &&
s->sk->sk_type == type && s->sk->sk_state == TCP_LISTEN) {
/* If device is null we match any device */
- if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) {
+ ax25_dev = READ_ONCE(s->ax25_dev);
+ if (ax25_dev == NULL || ax25_dev->dev == dev) {
sock_hold(s->sk);
spin_unlock(&ax25_list_lock);
return s->sk;
@@ -226,14 +228,16 @@ static ax25_cb *__ax25_find_cb(const ax25_address *src_addr,
ax25_address *dest_addr, ax25_digi *digi,
struct net_device *dev)
{
+ ax25_dev *ax25_dev;
ax25_cb *s;
ax25_for_each(s, &ax25_list) {
if (s->sk && s->sk->sk_type != SOCK_SEQPACKET)
continue;
- if (s->ax25_dev == NULL)
+ ax25_dev = READ_ONCE(s->ax25_dev);
+ if (ax25_dev == NULL)
continue;
- if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) {
+ if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && ax25_dev->dev == dev) {
if (digi != NULL && digi->ndigi != 0) {
if (s->digipeat == NULL)
continue;
@@ -293,15 +297,17 @@ ax25_cb *ax25_cb_add_unique(ax25_cb *ax25)
void ax25_send_to_raw(ax25_address *addr, struct sk_buff *skb, int proto)
{
+ ax25_dev *ax25_dev;
ax25_cb *s;
struct sk_buff *copy;
spin_lock(&ax25_list_lock);
ax25_for_each(s, &ax25_list) {
+ ax25_dev = READ_ONCE(s->ax25_dev);
if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 &&
s->sk->sk_type == SOCK_RAW &&
s->sk->sk_protocol == proto &&
- s->ax25_dev->dev == skb->dev &&
+ ax25_dev && ax25_dev->dev == skb->dev &&
atomic_read(&s->sk->sk_rmem_alloc) <= s->sk->sk_rcvbuf) {
if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL)
continue;
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 024/109] ax25: wait for ax25_send_frame() before detaching the connections of a device
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (22 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 023/109] ax25: read the connections ax25_dev only once when walking ax25_list Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 025/109] ax25: dont accept frames on a device that is going down Greg Kroah-Hartman
` (91 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, syzbot+caa052a0958a9146870d,
Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
A connection created by ax25_send_frame(), for ROSE, NET/ROM or IP
over AX.25, points to the ax25_dev of its device without holding a
reference on it. It relies on ax25_kill_by_device() to detach it when
the device goes down, before ax25_dev_device_down() frees the ax25_dev.
On NETDEV_DOWN the two run one after the other. In between, the
ax25_dev is still on ax25_dev_list, so ax25_send_frame() still finds
it, creates a connection and adds it to ax25_list after
ax25_kill_by_device() has finished walking that list. The connection is
left with a pointer to an ax25_dev that is freed a moment later, which
ax25_find_cb() and the AX.25 timers then dereference:
BUG: KASAN: slab-use-after-free in ax25_find_cb+0x2ed/0x330 [ax25]
Read of size 8 at addr ffff8880031e8c10 by task repro/3287
ax25_find_cb+0x2ed/0x330 [ax25]
rose_transmit_link+0xea/0x560 [rose]
rose_write_internal+0x3ab/0x15b0 [rose]
rose_release+0x2b2/0x4b0 [rose]
Allocated by: ax25_dev_device_up, ax25_device_event
Freed by: kfree_rcu_monitor, queued from ax25_device_event
The buggy address is located 16 bytes inside of
freed 256-byte region, cache kmalloc-256
This is the trace of the syzbot report below. An earlier commit, "rose:
use timer_shutdown_sync() for t0timer teardown", attributed that
report to the ROSE t0 timer; the freed object in the report is the
ax25_dev, as it is here.
A connection that is on the list in time is not safe either while it
is still being set up: ax25_kill_by_device() can detach it, clearing
its ax25_dev pointer, between the moment ax25_send_frame() adds it and
the moment it starts the link and queues the frame, which then
dereference NULL.
Make the two exclude each other with RCU. ax25_send_frame() now runs
entirely under rcu_read_lock() and gives up when the device is no
longer marked up. ax25_kill_by_device() clears device_up and calls
synchronize_rcu() before it walks the list: once that returns, every
connection ax25_send_frame() was working on is complete and on the
list, and no new one can appear behind the walk. This adds one RCU
grace period to taking an AX.25 interface down.
Tested on 6.12.94 with KASAN and lockdep, with the modules of this
tree and the rest of this series:
- with a 100 ms delay inserted between ax25_kill_by_device() and
ax25_dev_device_down(), a single "ifconfig bpq0 down" during eight
ROSE call requests left connections on the list and ended in a
use-after-free in ax25_heartbeat_expiry(); with the series the list
is empty after the down and there is no report;
- the syzbot C reproducer, with the interface brought up again in a
loop, caused 217 KASAN reports and a panic after 41 seconds; with
the series it ran for 600 seconds without any report;
- 60 plain "ifconfig bpq0 down" / "up" under 20 ROSE call requests
per second, and a ROSE call through a three node chain over mkiss,
behave as before;
- eight ROSE calls started at the same instant still end up on a
single AX.25 connection.
Reported-by: syzbot+caa052a0958a9146870d@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=caa052a0958a9146870d
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
Assisted-by: Claude:claude-opus-5-5
(cherry picked from commit 45071a40f7d5db10350dbd177decd3f68fdaaa01)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/ax25/af_ax25.c | 12 +++++++++++-
net/ax25/ax25_out.c | 38 ++++++++++++++++++++------------------
2 files changed, 31 insertions(+), 19 deletions(-)
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index 76f6848fba7d6..81c9631e4c41e 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -81,7 +81,17 @@ static void ax25_kill_by_device(struct net_device *dev)
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
return;
- ax25_dev->device_up = false;
+ WRITE_ONCE(ax25_dev->device_up, false);
+
+ /*
+ * ax25_send_frame() creates connections that hold no reference on
+ * the ax25_dev: they rely on the walk below to detach them before the
+ * ax25_dev is freed. It runs under rcu_read_lock() and gives up when
+ * device_up is clear, so once this returns every connection it was
+ * setting up is complete and on the list, and no new one can appear
+ * behind the walk.
+ */
+ synchronize_rcu();
spin_lock_bh(&ax25_list_lock);
again:
diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c
index eeb2aa1e2186f..f082b69126623 100644
--- a/net/ax25/ax25_out.c
+++ b/net/ax25/ax25_out.c
@@ -34,34 +34,34 @@ ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, const ax25_address *sr
ax25_dev *ax25_dev;
ax25_cb *ax25, *existing;
+ /*
+ * The connection made or used here holds no reference on the
+ * ax25_dev. ax25_kill_by_device() clears device_up, waits for the RCU
+ * readers and only then detaches the connections of the device: stay
+ * under the RCU read lock until the frame is queued, and do not start
+ * anything on a device that is going down.
+ */
+ rcu_read_lock();
+ ax25_dev = ax25_dev_ax25dev(dev);
+ if (!ax25_dev || !READ_ONCE(ax25_dev->device_up)) {
+ rcu_read_unlock();
+ return NULL;
+ }
+
/*
* Take the default packet length for the device if zero is
* specified.
*/
- if (paclen == 0) {
- rcu_read_lock();
- ax25_dev = ax25_dev_ax25dev(dev);
- if (!ax25_dev) {
- rcu_read_unlock();
- return NULL;
- }
+ if (paclen == 0)
paclen = ax25_dev->values[AX25_VALUES_PACLEN];
- rcu_read_unlock();
- }
/*
* Look for an existing connection.
*/
if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) {
ax25_output(ax25, paclen, skb);
- return ax25; /* It already existed */
- }
-
- rcu_read_lock();
- ax25_dev = ax25_dev_ax25dev(dev);
- if (!ax25_dev) {
rcu_read_unlock();
- return NULL;
+ return ax25; /* It already existed */
}
if ((ax25 = ax25_create_cb()) == NULL) {
@@ -69,7 +69,6 @@ ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, const ax25_address *sr
return NULL;
}
ax25_fillin_cb(ax25, ax25_dev);
- rcu_read_unlock();
ax25->source_addr = *src;
ax25->dest_addr = *dest;
@@ -78,6 +77,7 @@ ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, const ax25_address *sr
ax25->digipeat = kmemdup(digi, sizeof(*digi), GFP_ATOMIC);
if (ax25->digipeat == NULL) {
ax25_cb_put(ax25);
+ rcu_read_unlock();
return NULL;
}
}
@@ -91,10 +91,11 @@ ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, const ax25_address *sr
if (existing) {
ax25_cb_put(ax25);
ax25_output(existing, paclen, skb);
+ rcu_read_unlock();
return existing;
}
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+ switch (ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
ax25_std_establish_data_link(ax25);
@@ -121,6 +122,7 @@ ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, const ax25_address *sr
ax25_start_heartbeat(ax25);
ax25_output(ax25, paclen, skb);
+ rcu_read_unlock();
return ax25; /* We had to create it */
}
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 025/109] ax25: dont accept frames on a device that is going down
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (23 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 024/109] ax25: wait for ax25_send_frame() before detaching the connections of a device Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 026/109] ax25: send a private copy of each I frame Greg Kroah-Hartman
` (90 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25_rcv() creates a connection on an incoming SABM, for a listening
socket or for a callsign registered by ROSE or NET/ROM. Like the ones
made by ax25_send_frame(), it holds no reference on the ax25_dev and
relies on ax25_kill_by_device() to detach it before the ax25_dev is
freed. If it is added to ax25_list after ax25_kill_by_device() has
walked the list, it is left pointing to a freed ax25_dev.
ax25_rcv() is called from the packet type handler, under
rcu_read_lock(), and ax25_kill_by_device() now clears device_up and
waits for the RCU readers before its walk. Drop the frame when
device_up is clear: a frame that gets past the test is fully processed
before the walk starts, and none is processed behind it.
The window is much smaller than for ax25_send_frame(): the core already
drops what is received on a device that is no longer running, so only a
frame that was queued before the device was stopped and is processed
late can hit it. I could not exercise it. The change was checked for
regressions only: incoming connections still work in the ROSE tests of
the previous patch, where every AX.25 link is opened by an incoming
SABM on one side.
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
Assisted-by: Claude:claude-opus-5-5
(cherry picked from commit 748551f72b27325043eafe969311b15ae1eb0c7c)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/ax25/af_ax25.c | 12 ++++++------
net/ax25/ax25_in.c | 9 +++++++++
2 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index 81c9631e4c41e..32114f659b8d9 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -84,12 +84,12 @@ static void ax25_kill_by_device(struct net_device *dev)
WRITE_ONCE(ax25_dev->device_up, false);
/*
- * ax25_send_frame() creates connections that hold no reference on
- * the ax25_dev: they rely on the walk below to detach them before the
- * ax25_dev is freed. It runs under rcu_read_lock() and gives up when
- * device_up is clear, so once this returns every connection it was
- * setting up is complete and on the list, and no new one can appear
- * behind the walk.
+ * ax25_send_frame() and ax25_rcv() create connections that hold no
+ * reference on the ax25_dev: they rely on the walk below to detach
+ * them before the ax25_dev is freed. Both run under rcu_read_lock()
+ * and give up when device_up is clear, so once this returns every
+ * connection they were setting up is complete and on the list, and
+ * no new one can appear behind the walk.
*/
synchronize_rcu();
diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c
index 18e15c3c26e46..c81bc8c4563b5 100644
--- a/net/ax25/ax25_in.c
+++ b/net/ax25/ax25_in.c
@@ -216,6 +216,15 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
if (skb_linearize(skb))
goto free;
+ /*
+ * A connection made below on an incoming SABM holds no reference on
+ * the ax25_dev either. We are called under rcu_read_lock(), which
+ * ax25_kill_by_device() waits for after it has cleared device_up:
+ * leave a device that is going down alone.
+ */
+ if (!READ_ONCE(ax25_dev->device_up))
+ goto free;
+
/*
* Parse the address header.
*/
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 026/109] ax25: send a private copy of each I frame
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (24 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 025/109] ax25: dont accept frames on a device that is going down Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 027/109] rose: clear the circuits of a neighbour that restarts the link Greg Kroah-Hartman
` (89 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
ax25_kick() keeps every I frame on the ack queue until it is
acknowledged and hands a clone of it to the device. After a REJ or a T1
timeout the frame goes back to the write queue and ax25_kick() clones it
again. A clone shares its data with the original, so ax25_send_iframe()
and ax25_transmit_buffer() write the control field and the address
header of the new clone in the very bytes used by the previous one. If
the previous clone is still waiting in the device queue, its control
field is rewritten there: it leaves with the N(R) current at the time
of the retransmission, not the one it was sent with.
Frames queued behind it, an RR for instance, were built in between with
a lower N(R). The peer then receives an N(R) lower than one it has
already been given. ax25_validate_nr() rejects it and
ax25_std_nr_error_recovery() resets the link with a SABM, in the middle
of a transfer. The layers above pay for it: on a ROSE circuit, the
frames replayed after the reset carry stale sequence numbers and the
circuit is reset as well, with loss of data.
Hand a private copy to the device instead. The frames are small (256
bytes of data by default) and the link is slow, the extra copy is not
measurable.
Found on a test bench: two virtual machines with four CPUs each, a
6.12.94 kernel with KASAN and lockdep, one ROSE circuit over an AX.25
link on a serial line (mkiss), 192 messages each way in bursts of 24,
and three REJ frames injected by one side. Instrumentation showed that
frames reached the driver in the order they were given to
dev_queue_xmit(), that V(R) never went backwards on the sender, and
that the driver nevertheless saw frames whose N(R) was lower than the
one before.
Counting, on the receiver, the frames whose N(R) is lower than the
previous one:
- this code plus the two pending AX.25 fixes used on the bench (REJ
guard, requeue on re-establishment): 4 runs, 13 such frames, 4 link
resets; with this patch: 6 runs, none, no link reset;
- the same with the transmit serialisation patches for AX.25 and ROSE
on top: link resets in most runs before; with this patch: 6 runs,
none, no link reset, no ROSE reset, 192/192 messages both ways in
every run.
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
Assisted-by: Claude:claude-opus-5-5
(cherry picked from commit 1b464137f3173fadf81a7a1bf4a9584ebaf72c7b)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/ax25/ax25_out.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c
index f082b69126623..7c4ebef5ae6c2 100644
--- a/net/ax25/ax25_out.c
+++ b/net/ax25/ax25_out.c
@@ -341,7 +341,16 @@ void ax25_kick(ax25_cb *ax25)
ax25->vs = start;
do {
- if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+ /*
+ * Send a private copy, not a clone. The frame stays on the
+ * ack queue and is sent again after a REJ or a T1 timeout,
+ * while the previous copy may still be waiting in the device
+ * queue. A clone shares its data with the original: building
+ * the frame again would rewrite the control field, and so
+ * the N(R), of the copy already queued.
+ */
+ skbn = skb_copy(skb, GFP_ATOMIC);
+ if (!skbn) {
skb_queue_head(&ax25->write_queue, skb);
break;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 027/109] rose: clear the circuits of a neighbour that restarts the link
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (25 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 026/109] ax25: send a private copy of each I frame Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 028/109] ax25: send unacknowledged I frames again when a link is re-established Greg Kroah-Hartman
` (88 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
A Restart Request is the X.25 way to say that every virtual circuit of
a link is gone. rose_link_rx_restart() only marks the neighbour as
restarted and answers with a Restart Confirmation: the sockets and the
through routes that used the link are left as they were.
That is harmless on a link coming up, nothing is established yet. It
is not when the other end restarts its AX.25 and ROSE stack while we
keep the link: the first thing it sends is a SABM, which resets our
AX.25 connection without bringing it down, then a Restart Request. Its
circuits are gone, ours stay in state 3 forever:
- a frame for a logical channel that the other end does not know is
dropped silently by rose_route_frame(), so no Clear Request ever
comes back;
- no timer watches the acknowledgement of data in state 3, and the
idle timer is off by default;
- the through routes stay in /proc/net/rose_routes and the remote
ends of those calls are never told.
The stale entries are not just a leak. The restarted end allocates its
logical channel numbers afresh, so a new circuit can get the number of
a stale through route on the same link, and whatever still arrives on
the stale half is then relayed into the new circuit.
Seen on a live network: a node that restarts its stack from cron left,
on each of three neighbours, a data circuit with five unacknowledged
frames that never moved again, and five through routes on the node
that relays for the others.
Clear them when a Restart Request arrives on a link that is already
up: send a Clear Request to the other half of each through route and
drop the route, and disconnect the local sockets, as rose_link_failed()
does when the link goes down. The loop of rose_del_route_by_neigh() is
moved to a helper for that, since the caller here already holds
rose_route_list_lock. Nothing is sent to the restarting neighbour: it
has no circuit left, and a Clear Request could hit a call it has just
placed on a reused channel number.
A link that is not up yet is left alone: nothing can be established
over it, and the sockets found there are calls waiting in neigh->queue
for the restart to complete.
Tested with three QEMU guests in a line, A - B - C, on a kernel with
lockdep and KASAN. A calls C through B and B calls C, both circuits
stay open, then C destroys and recreates its AX.25 interface, reloads
its routes and calls B. Before: both circuits are still open a minute
later, B keeps the through route, nobody is told. After: A and B get
the circuit cleared (cause 0x09) within a second of the Restart
Request, B has no through route left, and the new call from C goes
through at the first attempt. Four runs, no lockdep, KASAN or refcount
report.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
Assisted-by: Claude:claude-opus-5-5
(cherry picked from commit 34de1b990c7c690c19594a49ee5b9a74883baf2b)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
include/net/rose.h | 1 +
net/rose/rose_link.c | 8 ++++++++
net/rose/rose_route.c | 44 +++++++++++++++++++++++++++++++++----------
3 files changed, 43 insertions(+), 10 deletions(-)
diff --git a/include/net/rose.h b/include/net/rose.h
index 6ce065480a4e6..2820b7e4697e6 100644
--- a/include/net/rose.h
+++ b/include/net/rose.h
@@ -269,6 +269,7 @@ struct rose_neigh *rose_get_neigh(rose_address *, unsigned char *,
unsigned char *, int);
int rose_rt_ioctl(unsigned int, void __user *);
void rose_link_failed(ax25_cb *, int);
+void rose_link_restarted(struct rose_neigh *);
int rose_route_frame(struct sk_buff *, ax25_cb *);
void rose_rt_free(void);
diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c
index 20057a39bafaa..f2d9e5ffa4ea6 100644
--- a/net/rose/rose_link.c
+++ b/net/rose/rose_link.c
@@ -142,6 +142,14 @@ void rose_link_rx_restart(struct sk_buff *skb, struct rose_neigh *neigh, unsigne
switch (frametype) {
case ROSE_RESTART_REQUEST:
+ /*
+ * A Restart Request on a link that is already up means that
+ * the other end has lost its virtual circuits, clear ours.
+ * Nothing is established over a link that is not up yet, and
+ * the calls waiting in neigh->queue must be left alone.
+ */
+ if (neigh->restarted)
+ rose_link_restarted(neigh);
rose_stop_t0timer(neigh);
neigh->restarted = 1;
neigh->dce_mode = (skb->data[3] == ROSE_DTE_ORIGINATED);
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c
index 883b204121566..68a4f2385277b 100644
--- a/net/rose/rose_route.c
+++ b/net/rose/rose_route.c
@@ -827,19 +827,15 @@ int rose_rt_ioctl(unsigned int cmd, void __user *arg)
return 0;
}
-static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh)
+/*
+ * Blow away the through routes using this neighbour: the half on the
+ * neighbour is dropped and the other half is told with a Clear Request.
+ * The caller holds rose_route_list_lock.
+ */
+static void rose_clear_routes_by_neigh(struct rose_neigh *rose_neigh)
{
struct rose_route *rose_route, *s;
- rose_neigh->restarted = 0;
-
- rose_stop_t0timer(rose_neigh);
- rose_start_ftimer(rose_neigh);
-
- skb_queue_purge(&rose_neigh->queue);
-
- spin_lock_bh(&rose_route_list_lock);
-
rose_route = rose_route_list;
while (rose_route != NULL) {
@@ -866,9 +862,37 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh)
rose_route = rose_route->next;
}
+}
+
+static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh)
+{
+ rose_neigh->restarted = 0;
+
+ rose_stop_t0timer(rose_neigh);
+ rose_start_ftimer(rose_neigh);
+
+ skb_queue_purge(&rose_neigh->queue);
+
+ spin_lock_bh(&rose_route_list_lock);
+ rose_clear_routes_by_neigh(rose_neigh);
spin_unlock_bh(&rose_route_list_lock);
}
+/*
+ * The neighbour has restarted a link that was up: it has lost every
+ * virtual circuit that ran over it. Blow away the through routes and
+ * the connections using this neighbour, the link itself stays up.
+ * Called from rose_route_frame() with rose_neigh_list_lock and
+ * rose_route_list_lock held.
+ */
+void rose_link_restarted(struct rose_neigh *rose_neigh)
+{
+ lockdep_assert_held(&rose_route_list_lock);
+
+ rose_clear_routes_by_neigh(rose_neigh);
+ rose_kill_by_neigh(rose_neigh);
+}
+
/*
* A level 2 link has timed out, therefore it appears to be a poor link,
* then don't use that neighbour until it is reset. Blow away all through
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 028/109] ax25: send unacknowledged I frames again when a link is re-established
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (26 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 027/109] rose: clear the circuits of a neighbour that restarts the link Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 029/109] rose: bring up the link of the preferred route passed over by a connect request Greg Kroah-Hartman
` (87 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
When a connection goes back through state 1 from state 3 or 4 (invalid
N(R), FRMR, illegal frame), the UA that re-establishes it resets V(S),
V(A) and V(R) but leaves the I frames that were waiting for their
acknowledgment in the ack queue. From then on every acknowledgment
frees the frame at the head of the queue, an old one, instead of the
one it is for, so the queue keeps that many frames too many for ever:
- they stay charged to the socket that sent them, whose send memory is
never given back;
- they are sent again by the next go-back-N, long after the event, and
the peer gets duplicates;
- they were not sent again when the link came back, so the layer above
lost them.
Seen on a live ROSE node: an AX.25 connection in state 3 with
V(S) == V(A), empty write queue and 18 frames in its ack queue, and the
ROSE sockets using it showing a send queue that never drains.
Requeue those frames when the UA is received, as is already done when
the peer resets the link with a SABM in state 3 or 4. Same change in
the DAMA slave state machine.
Like that existing path, this can send again a frame that the peer had
received just before the reset: AX.25 has no way to know. The layer
above sees a duplicate instead of a silent loss. ROSE drops an exact
duplicate, but resets the circuit when the replayed frame carries an
N(R) that is no longer valid. On the nodes below those resets came
with link re-establishments caused by a late N(R), which "ax25: send a
private copy of each I frame" removes: with both patches the node that
is instrumented saw no re-establishment and no reset in 49 hours,
against five resets in the 47 hours before.
Tested in QEMU (6.12.94 with lockdep and KASAN), two nodes over a
mkiss serial link, one ROSE circuit, 40 messages 150 ms apart, the
link forced through state 1 three times by an injected FRMR. Before:
6 runs of 6 deliver 40/40 and leave one frame in the ack queue, the
ROSE socket keeps 960 bytes of send memory. After: 6 of 6 deliver
40/40 and the send memory goes back to zero. No lockdep, KASAN or
refcount report.
On four production nodes since 3 October 2026, six since 6 October. A
relay node that had 18 to 20 such frames on one link is sampled every
hour: 124 samples and 1301 link states in five days, none with a frame
left in an ack queue.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
Assisted-by: Claude:claude-opus-5-5
(cherry picked from commit 7b22827cb729de19ccba284ebaff3eeee21d4f11)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/ax25/ax25_ds_in.c | 2 ++
net/ax25/ax25_std_in.c | 8 ++++++++
2 files changed, 10 insertions(+)
diff --git a/net/ax25/ax25_ds_in.c b/net/ax25/ax25_ds_in.c
index c62f8fb06189e..b78a577760c6f 100644
--- a/net/ax25/ax25_ds_in.c
+++ b/net/ax25/ax25_ds_in.c
@@ -58,6 +58,8 @@ static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int framet
ax25->vr = 0;
ax25->state = AX25_STATE_3;
ax25->n2count = 0;
+ /* I frames left unacknowledged by a link reset: send them again */
+ ax25_requeue_frames(ax25);
if (ax25->sk != NULL) {
bh_lock_sock(ax25->sk);
ax25->sk->sk_state = TCP_ESTABLISHED;
diff --git a/net/ax25/ax25_std_in.c b/net/ax25/ax25_std_in.c
index f280683f83fcf..d0269ece4fc58 100644
--- a/net/ax25/ax25_std_in.c
+++ b/net/ax25/ax25_std_in.c
@@ -66,6 +66,14 @@ static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frame
ax25->vr = 0;
ax25->state = AX25_STATE_3;
ax25->n2count = 0;
+ /*
+ * The link may have been re-established from state
+ * 3 or 4 with I frames still waiting for their
+ * acknowledgment. V(S) and V(A) start again from
+ * zero: send those frames again, as is done when
+ * the peer resets the link with a SABM.
+ */
+ ax25_requeue_frames(ax25);
if (ax25->sk != NULL) {
bh_lock_sock(ax25->sk);
ax25->sk->sk_state = TCP_ESTABLISHED;
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 029/109] rose: bring up the link of the preferred route passed over by a connect request
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (27 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 028/109] ax25: send unacknowledged I frames again when a link is re-established Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 030/109] ax25: dont free an incoming SABM skb left on the listeners queue Greg Kroah-Hartman
` (86 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
For a connect request rose_get_neigh() walks the routes that match the
address, from the most specific to the least specific, and takes the
first neighbour whose link is restarted. A neighbour whose link is down
is only used when no matching route at all has a restarted neighbour.
A node that has both an exact route to a neighbouring node and a wide
route through a relay therefore never uses the exact route once its
link is down: the relay is restarted, it is always found first, and
since no frame is ever sent over the direct link nothing brings it up
again. The calls keep going through the relay, which may have no route
other than back to the calling node and then clears them. Seen on a node
whose White Pages daemon calls an adjacent node: every call goes to the
relay and comes back as CLEAR REQUEST "not obtainable", for as long as
the node runs.
Keep sending the call to the restarted neighbour, so that a call never
waits for a link that may not answer, but when a neighbour of a more
specific route, or an earlier neighbour of the same route, was passed
over because its link is down, start the restart procedure on that link.
If the other end answers, the next call finds the link restarted and
uses it. A link that has just failed is left alone for
link_fail_timeout, and nothing is done while a restart is under way.
Frames in transit are handled as before.
Tested in QEMU with a lockdep and KASAN kernel, three nodes A - B - C
where B has an exact route to C over their direct link and a wide route
through A, and the link B - A is restarted while B - C is not. Before,
every call from B to C goes to A and is cleared there, and the direct
link stays down. After, the first call still goes to A, the direct link
is restarted within 20 ms and the following calls go through it. With C
switched off no call waits: they all go to A at once, the restart is
attempted once, and after the AX.25 connection gives up the link is left
alone for link_fail_timeout. A call from A to C in transit through B
gets the same answer before and after. No lockdep, KASAN or refcount
report.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit cdf5a2eba2bc4f79dabb013a3a935f54aedb85e9)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
include/net/rose.h | 1 +
net/rose/rose_link.c | 14 ++++++++++++++
net/rose/rose_route.c | 14 ++++++++++++++
3 files changed, 29 insertions(+)
diff --git a/include/net/rose.h b/include/net/rose.h
index 2820b7e4697e6..2fa0331e36f09 100644
--- a/include/net/rose.h
+++ b/include/net/rose.h
@@ -240,6 +240,7 @@ void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *,
void rose_transmit_clear_request(struct rose_neigh *, unsigned int,
unsigned char, unsigned char);
void rose_link_restart(struct rose_neigh *neigh);
+void rose_link_bring_up(struct rose_neigh *neigh);
void rose_transmit_link(struct sk_buff *, struct rose_neigh *);
/* rose_loopback.c */
diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c
index f2d9e5ffa4ea6..1fe48d58726f5 100644
--- a/net/rose/rose_link.c
+++ b/net/rose/rose_link.c
@@ -287,6 +287,20 @@ void rose_link_restart(struct rose_neigh *neigh)
rose_start_t0timer(neigh);
}
+/*
+ * Start the restart procedure on a link that is down without waiting for
+ * a frame to send over it. Nothing is done while a restart is under way.
+ */
+void rose_link_bring_up(struct rose_neigh *neigh)
+{
+ if (neigh->loopback || neigh->restarted || rose_t0timer_running(neigh))
+ return;
+
+ rose_transmit_restart_request(neigh);
+ neigh->dce_mode = 0;
+ rose_start_t0timer(neigh);
+}
+
void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh)
{
unsigned char *dptr;
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c
index 68a4f2385277b..6a1de0055e28b 100644
--- a/net/rose/rose_route.c
+++ b/net/rose/rose_route.c
@@ -741,6 +741,7 @@ struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause,
unsigned char *diagnostic, int route_frame)
{
struct rose_neigh *res = NULL;
+ struct rose_neigh *down = NULL;
struct rose_node *node;
int failed = 0;
int i;
@@ -752,8 +753,21 @@ struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause,
if (node->neighbour[i]->restarted) {
res = node->neighbour[i];
rose_neigh_hold(node->neighbour[i]);
+ /*
+ * A neighbour of a more specific route
+ * was passed over because its link is
+ * down: this call does not wait for it,
+ * but have the link brought up for the
+ * next ones.
+ */
+ if (down)
+ rose_link_bring_up(down);
goto out;
}
+ /* The first one is the preferred one. */
+ if (!route_frame && !down &&
+ !rose_ftimer_running(node->neighbour[i]))
+ down = node->neighbour[i];
}
}
}
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 030/109] ax25: dont free an incoming SABM skb left on the listeners queue
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (28 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 029/109] rose: bring up the link of the preferred route passed over by a connect request Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 031/109] block: avoid potential deadlock on zone revalidation failure Greg Kroah-Hartman
` (85 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Bernard Pidoux, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <bernard.f6bvp@gmail.com>
When a SABM comes in for a listening socket, ax25_rcv() creates the new
socket, puts the skb on the listener's receive queue and only then
allocates the digipeater path. If that allocation fails, the skb is
freed with kfree_skb() while it is still linked on the queue, and the
accept backlog count taken by sk_acceptq_added() is never given back.
The next accept() or the close of the listener then walks a queue that
contains a freed skb.
Allocate the path before the skb is queued, so that the error path only
has to free an skb nobody else knows about and to destroy the new
socket. The later allocation remains for a connection that has no
listening socket.
Only reachable when the atomic allocation fails for a call that comes
through digipeaters. Regression tested in QEMU (6.12.94 with lockdep
and KASAN): 14 incoming connections to a listening socket, each
accepted, served and closed, before and after; no report.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
(cherry picked from commit 2d67e8e9f3d627327114fb946c3a28ef72becb2d)
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/ax25/ax25_in.c | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c
index c81bc8c4563b5..1bcb5e321467d 100644
--- a/net/ax25/ax25_in.c
+++ b/net/ax25/ax25_in.c
@@ -383,6 +383,24 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
}
ax25 = sk_to_ax25(make);
+
+ /*
+ * Get the digipeater path before the skb goes on the
+ * listener's queue: once queued it belongs to that queue
+ * and cannot simply be freed if the allocation fails.
+ */
+ if (dp.ndigi && !ax25->digipeat) {
+ ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC);
+ if (!ax25->digipeat) {
+ kfree_skb(skb);
+ ax25_destroy_socket(ax25);
+ bh_unlock_sock(sk);
+ sock_put(sk);
+
+ return 0;
+ }
+ }
+
skb_set_owner_r(skb, make);
skb_queue_head(&sk->sk_receive_queue, skb);
@@ -406,14 +424,13 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
ax25->dest_addr = src;
/*
- * Sort out any digipeated paths.
+ * Sort out any digipeated paths. A socket made from a listener
+ * already has its path allocated, see above.
*/
if (dp.ndigi && !ax25->digipeat &&
(ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
kfree_skb(skb);
ax25_destroy_socket(ax25);
- if (sk)
- sock_put(sk);
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 031/109] block: avoid potential deadlock on zone revalidation failure
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (29 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 030/109] ax25: dont free an incoming SABM skb left on the listeners queue Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 032/109] mm/damon/core: error damos_commit_quota_goal() for zero target_value Greg Kroah-Hartman
` (84 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, stable, Damien Le Moal,
Christoph Hellwig, Hannes Reinecke, Jens Axboe, Stanley Jhu,
Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Damien Le Moal <dlemoal@kernel.org>
commit 3dd63dba8f9cb6990a40af7ed66ee0159f475819 upstream.
If revalidating the zones of a zoned block device with
blk_revalidate_disk_zones() fails during a SCSI disk rescan, the following
lockdep splat is thrown:
[ 347.251859] [ T11230] sda: failed to revalidate zones
[ 347.261380] [ T11230] ======================================================
[ 347.263882] [ T11230] WARNING: possible circular locking dependency detected
[ 347.266353] [ T11230] 7.1.0+ #1194 Not tainted
[ 347.268052] [ T11230] ------------------------------------------------------
[ 347.270537] [ T11230] tcsh/11230 is trying to acquire lock:
[ 347.272555] [ T11230] ffffffff8f91d400 (wq_pool_mutex){+.+.}-{4:4}, at: destroy_workqueue+0x15d/0x8d0
[ 347.275914] [ T11230]
but task is already holding lock:
[ 347.278646] [ T11230] ffff88812fa1bcc0 (&q->q_usage_counter(io)#5){++++}-{0:0}, at: blk_mq_freeze_queue_nomemsave+0x16/0x30
[ 347.282503] [ T11230]
which lock already depends on the new lock.
[ 347.286239] [ T11230]
the existing dependency chain (in reverse order) is:
[ 347.289408] [ T11230]
-> #2 (&q->q_usage_counter(io)#5){++++}-{0:0}:
[ 347.292437] [ T11230] blk_alloc_queue+0x5ca/0x750
[ 347.294379] [ T11230] blk_mq_alloc_queue+0x14c/0x240
[ 347.296375] [ T11230] scsi_alloc_sdev+0x871/0xd10 [scsi_mod]
[ 347.298619] [ T11230] scsi_probe_and_add_lun+0x600/0xc50 [scsi_mod]
[ 347.301056] [ T11230] __scsi_scan_target+0x187/0x3b0 [scsi_mod]
[ 347.303385] [ T11230] scsi_scan_channel+0xf2/0x180 [scsi_mod]
[ 347.305651] [ T11230] scsi_scan_host_selected+0x20b/0x2d0 [scsi_mod]
[ 347.308119] [ T11230] do_scan_async+0x42/0x420 [scsi_mod]
[ 347.310276] [ T11230] async_run_entry_fn+0x94/0x5a0
[ 347.312284] [ T11230] process_one_work+0x8da/0x1690
[ 347.314287] [ T11230] worker_thread+0x5fe/0x1010
[ 347.316216] [ T11230] kthread+0x358/0x450
[ 347.317675] [ T11230] ret_from_fork+0x5b9/0x8e0
[ 347.319181] [ T11230] ret_from_fork_asm+0x11/0x20
[ 347.320778] [ T11230]
-> #1 (fs_reclaim){+.+.}-{0:0}:
[ 347.322890] [ T11230] fs_reclaim_acquire+0xd5/0x120
[ 347.324464] [ T11230] __kmalloc_cache_node_noprof+0x39/0x620
[ 347.326223] [ T11230] init_rescuer+0x19b/0x560
[ 347.327697] [ T11230] workqueue_init+0x33b/0x6a0
[ 347.329224] [ T11230] kernel_init_freeable+0x2eb/0x600
[ 347.330881] [ T11230] kernel_init+0x1c/0x140
[ 347.332334] [ T11230] ret_from_fork+0x5b9/0x8e0
[ 347.333847] [ T11230] ret_from_fork_asm+0x11/0x20
[ 347.335360] [ T11230]
-> #0 (wq_pool_mutex){+.+.}-{4:4}:
[ 347.337510] [ T11230] __lock_acquire+0xdea/0x2260
[ 347.339030] [ T11230] lock_acquire+0x187/0x2f0
[ 347.340495] [ T11230] __mutex_lock+0x1ab/0x2600
[ 347.341464] [ T11230] destroy_workqueue+0x15d/0x8d0
[ 347.342485] [ T11230] disk_free_zone_resources+0xd5/0x560
[ 347.343577] [ T11230] blk_revalidate_disk_zones+0x620/0xac7
[ 347.344723] [ T11230] sd_zbc_revalidate_zones+0x1dd/0x790 [sd_mod]
[ 347.345938] [ T11230] sd_revalidate_disk+0xc66/0x8e60 [sd_mod]
[ 347.347112] [ T11230] scsi_rescan_device+0x1f9/0x310 [scsi_mod]
[ 347.348318] [ T11230] store_rescan_field+0x19/0x20 [scsi_mod]
[ 347.349507] [ T11230] kernfs_fop_write_iter+0x3d2/0x5e0
[ 347.350565] [ T11230] vfs_write+0x469/0x1000
[ 347.351484] [ T11230] ksys_write+0x116/0x250
[ 347.352403] [ T11230] do_syscall_64+0xf0/0x6e0
[ 347.353361] [ T11230] entry_SYSCALL_64_after_hwframe+0x4b/0x53
[ 347.354533] [ T11230]
other info that might help us debug this:
[ 347.356432] [ T11230] Chain exists of:
wq_pool_mutex --> fs_reclaim --> &q->q_usage_counter(io)#5
[ 347.358919] [ T11230] Possible unsafe locking scenario:
[ 347.360307] [ T11230] CPU0 CPU1
[ 347.361327] [ T11230] ---- ----
[ 347.362340] [ T11230] lock(&q->q_usage_counter(io)#5);
[ 347.363344] [ T11230] lock(fs_reclaim);
[ 347.364526] [ T11230] lock(&q->q_usage_counter(io)#5);
[ 347.365968] [ T11230] lock(wq_pool_mutex);
[ 347.366811] [ T11230]
*** DEADLOCK ***
This happens because SCSI disk rescan is executed from a work context
and a failure of blk_revalidate_disk_zones() causes a call to
disk_free_zone_resources() which will free the disk zone write plug
workqueue.
Avoid this by delaying the destruction of the disk zone write plug
workqueue to disk_release(). Do this by introducing the function
disk_release_zone_resources() and using this new function from
disk_release(). This new function destroys the zone write plugs workqueue
and calls disk_free_zone_resources(), thus allowing to remove the call to
destroy_workqueue() from disk_free_zone_resources().
disk_alloc_zone_resources() is modified to not create the disk zone
write plug work queue if it already exists.
Fixes: a8f59e5a5dea ("block: use a per disk workqueue for zone write plugging")
Cc: stable@vger.kernek.org
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Link: https://patch.msgid.link/20260701082155.1369996-1-dlemoal@kernel.org
Signed-off-by: Jens Axboe <axboe@kernel.dk>
[ stanleyjhu: 6.18.y has no zone write plug kthread worker and does not
use WQ_PERCPU, so only the workqueue part applies:
disk_alloc_zone_resources() reuses an existing workqueue,
disk_free_zone_resources() drains it instead of destroying it, and
the new disk_release_zone_resources() destroys it from
disk_release(). ]
Signed-off-by: Stanley Jhu <stanleyjhu@google.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
block/blk-zoned.c | 37 +++++++++++++++++++++++++++----------
block/blk.h | 4 ++--
block/genhd.c | 2 +-
3 files changed, 30 insertions(+), 13 deletions(-)
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 5c16cf96b97c1..a7d3d2e002da6 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -1413,11 +1413,20 @@ static int disk_alloc_zone_resources(struct gendisk *disk,
if (!disk->zone_wplugs_pool)
goto free_hash;
- disk->zone_wplugs_wq =
- alloc_workqueue("%s_zwplugs", WQ_MEM_RECLAIM | WQ_HIGHPRI,
- pool_size, disk->disk_name);
- if (!disk->zone_wplugs_wq)
- goto destroy_pool;
+ /*
+ * We may already have a zone write plug workqueue as this function may
+ * be called after disk_free_zone_resources(), which does not destroy
+ * the workqueue (the zone write plugs workqueue is destroyed at
+ * disk_release() time).
+ */
+ if (!disk->zone_wplugs_wq) {
+ disk->zone_wplugs_wq =
+ alloc_workqueue("%s_zwplugs",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI,
+ pool_size, disk->disk_name);
+ if (!disk->zone_wplugs_wq)
+ goto destroy_pool;
+ }
return 0;
@@ -1474,15 +1483,13 @@ static unsigned int disk_set_conv_zones_bitmap(struct gendisk *disk,
return nr_conv_zones;
}
-void disk_free_zone_resources(struct gendisk *disk)
+static void disk_free_zone_resources(struct gendisk *disk)
{
if (!disk->zone_wplugs_pool)
return;
- if (disk->zone_wplugs_wq) {
- destroy_workqueue(disk->zone_wplugs_wq);
- disk->zone_wplugs_wq = NULL;
- }
+ if (disk->zone_wplugs_wq)
+ drain_workqueue(disk->zone_wplugs_wq);
disk_destroy_zone_wplugs_hash_table(disk);
@@ -1501,6 +1508,16 @@ void disk_free_zone_resources(struct gendisk *disk)
disk->nr_zones = 0;
}
+void disk_release_zone_resources(struct gendisk *disk)
+{
+ if (disk->zone_wplugs_wq) {
+ destroy_workqueue(disk->zone_wplugs_wq);
+ disk->zone_wplugs_wq = NULL;
+ }
+
+ disk_free_zone_resources(disk);
+}
+
static inline bool disk_need_zone_resources(struct gendisk *disk)
{
/*
diff --git a/block/blk.h b/block/blk.h
index 2810b4d086e6a..179662b868998 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -499,7 +499,7 @@ static inline void ioc_clear_queue(struct request_queue *q)
#ifdef CONFIG_BLK_DEV_ZONED
void disk_init_zone_resources(struct gendisk *disk);
-void disk_free_zone_resources(struct gendisk *disk);
+void disk_release_zone_resources(struct gendisk *disk);
static inline bool bio_zone_write_plugging(struct bio *bio)
{
return bio_flagged(bio, BIO_ZONE_WRITE_PLUGGING);
@@ -552,7 +552,7 @@ int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,
static inline void disk_init_zone_resources(struct gendisk *disk)
{
}
-static inline void disk_free_zone_resources(struct gendisk *disk)
+static inline void disk_release_zone_resources(struct gendisk *disk)
{
}
static inline bool bio_zone_write_plugging(struct bio *bio)
diff --git a/block/genhd.c b/block/genhd.c
index bbcfbd1a363c5..6ba0e22ce9dff 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -1308,7 +1308,7 @@ static void disk_release(struct device *dev)
disk_release_events(disk);
kfree(disk->random);
- disk_free_zone_resources(disk);
+ disk_release_zone_resources(disk);
xa_destroy(&disk->part_tbl);
kobject_put(&disk->queue_kobj);
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 032/109] mm/damon/core: error damos_commit_quota_goal() for zero target_value
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (30 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 031/109] block: avoid potential deadlock on zone revalidation failure Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 033/109] net_sched: codel: fix stale state for empty flows in fq_codel Greg Kroah-Hartman
` (83 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, SJ Park, Andrew Morton, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <sj@kernel.org>
[ Upstream commit 610c4438fb66e06d9e693c0ad3b2c1be54c0d61f ]
Patch series "mm/damon: move zero damos quota target_value handling to the
core layer".
Having zero DAMOS quota target value can cause division by zero. DAMON
API callers are checking the target value parameters to avoid that. It is
easy to make mistakes in some of the multiple API callers. Move that to
the core layer.
Patch 1 adds the corner case handling into the core layer DAMON parameters
validation logic. Patches 2 and 3 remove no more needed DAMON API callers
side handling of the corner case in DAMON_LRU_SORT and DMON_SAMPLIE_MTIER,
respectively.
This patch (of 3):
If a DAMOS scheme has a damos_quota_goal of zero target_value,
damos_quota_goal() could trigger division-by-zero error. Hence each DAMON
API callers should do the zero target_value validation. It is easy to
make mistakes. Actually such bugs in DAMON_LRU_SORT and
DAMON_SAMPLE_MTIER were found and fixed [1].
It is better to handle the corner case only once in the core layer,
instead of multiple places in all DAMON API callers. One straightforward
option is using an alternative denominator for the corner case in the
damos_quota_goal(). However, the zero target_value is meaningless. In
this case, the quota goal is always evaluated as achieved or
over-achieved. The quota will only keep being reduced.
Simply avoid using zero target_value by adding a check in the core layer
DAMOS quota goal parameters validation/commit path,
damos_commit_quota_goal(). Update it to return an error in the case.
Also update its caller to propagate the error.
Notes for stable kernel porting. The upstream commit doesn't have
Fixes: and therefore not ported to stable kernels. It turned out it
fixes commit 8b549a4fd3c5 ("mm/damon/sysfs-schemes: commit damos quota
goals user input to DAMOS") which was merged into v6.8. The user impact
is a race condition that can result in divide-by-zero. Hence backport.
Reproducing the issue requires sysfs write permission, certain DAMON ABI
usages that are supposed to be uncommon, and a rare race timing, though.
The issue was discovered [2] by Sashiko as a part [3] of its bug report.
Also, the upstream commit on this stable kernel can fix the issue for
parameters commit paths but DAMON start path. That's because DAMON
start path does not invoke damon_commit_quota_goals(). To fix the path
with minimum change, add a new small validation of quota goal target
value before damon_start() call.
[2] https://sashiko.dev/#/bug/linux-7f814c9a-986f-4103-a55a-ef9537dcf62b
[3] https://lore.kernel.org/20261021005234.94107-1-sj@kernel.org
Link: https://lore.kernel.org/20260903010722.94244-1-sj@kernel.org
Link: https://lore.kernel.org/20260903010722.94244-2-sj@kernel.org
Link: https://lore.kernel.org/20260803134034.15217-1-sj@kernel.org [1]
Signed-off-by: SJ Park <sj@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: SJ Park <sj@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
mm/damon/core.c | 22 ++++++++++++++++------
mm/damon/sysfs.c | 19 +++++++++++++++++++
2 files changed, 35 insertions(+), 6 deletions(-)
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 4e6e3d7d0a5c0..8fa788b0d709c 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -807,15 +807,18 @@ static void damos_commit_quota_goal_union(
}
}
-static void damos_commit_quota_goal(
+static int damos_commit_quota_goal(
struct damos_quota_goal *dst, struct damos_quota_goal *src)
{
+ if (!src->target_value)
+ return -EINVAL;
dst->metric = src->metric;
dst->target_value = src->target_value;
if (dst->metric == DAMOS_QUOTA_USER_INPUT)
dst->current_value = src->current_value;
/* keep last_psi_total as is, since it will be updated in next cycle */
damos_commit_quota_goal_union(dst, src);
+ return 0;
}
/**
@@ -833,14 +836,17 @@ static void damos_commit_quota_goal(
int damos_commit_quota_goals(struct damos_quota *dst, struct damos_quota *src)
{
struct damos_quota_goal *dst_goal, *next, *src_goal, *new_goal;
- int i = 0, j = 0;
+ int i = 0, j = 0, err;
damos_for_each_quota_goal_safe(dst_goal, next, dst) {
src_goal = damos_nth_quota_goal(i++, src);
- if (src_goal)
- damos_commit_quota_goal(dst_goal, src_goal);
- else
+ if (src_goal) {
+ err = damos_commit_quota_goal(dst_goal, src_goal);
+ if (err)
+ return err;
+ } else {
damos_destroy_quota_goal(dst_goal);
+ }
}
damos_for_each_quota_goal_safe(src_goal, next, src) {
if (j++ < i)
@@ -849,7 +855,11 @@ int damos_commit_quota_goals(struct damos_quota *dst, struct damos_quota *src)
src_goal->metric, src_goal->target_value);
if (!new_goal)
return -ENOMEM;
- damos_commit_quota_goal(new_goal, src_goal);
+ err = damos_commit_quota_goal(new_goal, src_goal);
+ if (err) {
+ damos_free_quota_goal(new_goal);
+ return err;
+ }
damos_add_quota_goal(dst, new_goal);
}
return 0;
diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
index 7f52470be36e3..3a79d8c89b5bb 100644
--- a/mm/damon/sysfs.c
+++ b/mm/damon/sysfs.c
@@ -1592,6 +1592,20 @@ static int damon_sysfs_repeat_call_fn(void *data)
return 0;
}
+static bool damon_sysfs_valid_start_ctx(struct damon_ctx *ctx)
+{
+ struct damos *s;
+ struct damos_quota_goal *g;
+
+ damon_for_each_scheme(s, ctx) {
+ damos_for_each_quota_goal(g, (&s->quota)) {
+ if (!g->target_value)
+ return false;
+ }
+ }
+ return true;
+}
+
static int damon_sysfs_turn_damon_on(struct damon_sysfs_kdamond *kdamond)
{
struct damon_ctx *ctx;
@@ -1618,6 +1632,11 @@ static int damon_sysfs_turn_damon_on(struct damon_sysfs_kdamond *kdamond)
kfree(repeat_call_control);
return PTR_ERR(ctx);
}
+ if (!damon_sysfs_valid_start_ctx(ctx)) {
+ kfree(repeat_call_control);
+ damon_destroy_ctx(ctx);
+ return -EINVAL;
+ }
err = damon_start(&ctx, 1, false);
if (err) {
kfree(repeat_call_control);
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 033/109] net_sched: codel: fix stale state for empty flows in fq_codel
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (31 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 032/109] mm/damon/core: error damos_commit_quota_goal() for zero target_value Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 034/109] Bluetooth: L2CAP: refuse __l2cap_chan_add if chan already has conn Greg Kroah-Hartman
` (82 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Jesper Dangaard Brouer,
Jonas Köppeler, Chris Arges,
Toke Høiland-Jørgensen, Eric Dumazet, Jakub Kicinski,
Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jonas Köppeler <j.koeppeler@tu-berlin.de>
[ Upstream commit 815980fe6dbb01ad4007e8b260a45617f598b76d ]
When codel_dequeue() finds an empty queue, it resets vars->dropping
but does not reset vars->first_above_time. The reference CoDel
algorithm (Nichols & Jacobson, ACM Queue 2012) resets both:
dodeque_result codel_queue_t::dodeque(time_t now) {
...
if (r.p == NULL) {
first_above_time = 0; // <-- Linux omits this
}
...
}
Note that codel_should_drop() does reset first_above_time when called
with a NULL skb, but codel_dequeue() returns early before ever calling
codel_should_drop() in the empty-queue case. The post-drop code paths
do reach codel_should_drop(NULL) and correctly reset the timer, so a
dropped packet breaks the cycle -- but the next delivered packet
re-arms first_above_time and the cycle repeats.
For sparse flows such as ICMP ping (one packet every 200ms-1s), the
first packet arms first_above_time, the flow goes empty, and the
second packet arrives after the interval has elapsed and gets dropped.
The pattern repeats, producing sustained loss on flows that are not
actually congested.
Test: veth pair, fq_codel, BQL disabled, 30000 iptables rules in the
consumer namespace (NAPI-64 cycle ~14ms, well above fq_codel's 5ms
target), ping at 5 pps under UDP flood:
Before fix: 26% ping packet loss
After fix: 0% ping packet loss
Fix by resetting first_above_time to zero in the empty-queue path
of codel_dequeue(), matching the reference algorithm.
Fixes: 76e3cc126bb2 ("codel: Controlled Delay AQM")
Fixes: d068ca2ae2e6 ("codel: split into multiple files")
Co-developed-by: Jesper Dangaard Brouer <hawk@kernel.org>
Signed-off-by: Jesper Dangaard Brouer <hawk@kernel.org>
Signed-off-by: Jonas Köppeler <j.koeppeler@tu-berlin.de>
Reported-by: Chris Arges <carges@cloudflare.com>
Tested-by: Jonas Köppeler <j.koeppeler@tu-berlin.de>
Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com>
Link: https://lore.kernel.org/all/20260318134826.1281205-7-hawk@kernel.org/
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20260323174920.253526-1-hawk@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
include/net/codel_impl.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/net/codel_impl.h b/include/net/codel_impl.h
index 78a27ac730700..b2c359c6dd1b8 100644
--- a/include/net/codel_impl.h
+++ b/include/net/codel_impl.h
@@ -158,6 +158,7 @@ static struct sk_buff *codel_dequeue(void *ctx,
bool drop;
if (!skb) {
+ vars->first_above_time = 0;
vars->dropping = false;
return skb;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 034/109] Bluetooth: L2CAP: refuse __l2cap_chan_add if chan already has conn
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (32 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 033/109] net_sched: codel: fix stale state for empty flows in fq_codel Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 035/109] tracing: Hold trace_types_lock when updating modules in persistent instances Greg Kroah-Hartman
` (81 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Pauli Virtanen,
Luiz Augusto von Dentz, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <pav@iki.fi>
[ Upstream commit 86b55991d3cd690e73c5ef8d20b75ae4905b357a ]
l2cap_chan may be linked to l2cap_conn at most once. This is assumed in
several places, eg l2cap_chan_del cleanup.
There is a TOCTOU race where the invariant is violated:
[Task 1] [Task 2]
l2cap_chan_connect l2cap_sock_bind
l2cap_chan_lock lock_sock
l2cap_state_change if (sk->sk_state != BT_OPEN)
chan->state = BT_CONNECT
l2cap_sock_state_change_cb chan->state = BT_BOUND
sk->sk_state = BT_BOUND
lock_sock <------------------ release_sock
sk->sk_state = BT_CONNECT
l2cap_sock_connect() does not check sk->sk_state, so since chan->state
is now BT_BOUND, subsequent connect() ends up with second
__l2cap_chan_add.
Explicitly document and check the invariant in __l2cap_chan_add with
WARN_ON_ONCE. The only callsite where it could be hit is
l2cap_chan_connect, so add pre-check there to avoid relying on
chan->state. chan->state read/write is not properly guarded currently so
there can be other TOCTOUC problems.
Add l2cap_lock_chan in l2cap_sock_bind() to guard chan->state write.
Fixes: b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref")
Assisted-by: deepseek-4-flash # finding the race condition
Signed-off-by: Pauli Virtanen <pav@iki.fi>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/bluetooth/l2cap_core.c | 7 ++++++-
net/bluetooth/l2cap_sock.c | 2 ++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 7d4115603d08e..462667a6e6ac2 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -625,6 +625,10 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
__le16_to_cpu(chan->psm), chan->dcid);
+ /* Caller must ensure l2cap_chan is linked to l2cap_conn only once */
+ if (WARN_ON_ONCE(chan->conn || test_bit(FLAG_DEL, &chan->flags)))
+ return;
+
conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
chan->conn = l2cap_conn_get(conn);
@@ -7410,7 +7414,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
}
}
- if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
+ if ((cid && __l2cap_get_chan_by_dcid(conn, cid)) || chan->conn ||
+ test_bit(FLAG_DEL, &chan->flags)) {
hci_conn_drop(hcon);
err = -EBUSY;
goto chan_unlock;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 9078a672e2dd3..e78866e02bfc2 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -111,6 +111,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr_unsized *addr, i
return -EINVAL;
}
+ l2cap_chan_lock(chan);
lock_sock(sk);
if (sk->sk_state != BT_OPEN) {
@@ -176,6 +177,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr_unsized *addr, i
done:
release_sock(sk);
+ l2cap_chan_unlock(chan);
return err;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 117+ messages in thread
* [PATCH 6.18 035/109] tracing: Hold trace_types_lock when updating modules in persistent instances
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (33 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 034/109] Bluetooth: L2CAP: refuse __l2cap_chan_add if chan already has conn Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 036/109] tracing: Hold trace_types_lock when resetting last boot data from readers Greg Kroah-Hartman
` (80 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, David Carlier, Steven Rostedt
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Carlier <devnexen@gmail.com>
commit 66877a312ae3c2c3d849e82e882f642ffd4ec3e2 upstream.
trace_module_record() walks ftrace_trace_arrays from the module notifier
without trace_types_lock. A concurrent rmdir of an instance removes it
from the list and frees it under that lock, so a module load or unload
can walk into a freed trace_array.
Cc: stable@vger.kernel.org
Fixes: 5f3719f697c3 ("tracing: Update modules to persistent instances when loaded")
Link: https://patch.msgid.link/20261016061638.214847-1-devnexen@gmail.com
Signed-off-by: David Carlier <devnexen@gmail.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
kernel/trace/trace.c | 2 ++
1 file changed, 2 insertions(+)
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -10693,6 +10693,8 @@ static void trace_module_record(struct m
struct trace_array *tr;
unsigned long flags;
+ guard(mutex)(&trace_types_lock);
+
list_for_each_entry(tr, &ftrace_trace_arrays, list) {
flags = tr->flags & (TRACE_ARRAY_FL_BOOT | TRACE_ARRAY_FL_LAST_BOOT);
/* Update any persistent trace array that has already been started */
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 036/109] tracing: Hold trace_types_lock when resetting last boot data from readers
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (34 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 035/109] tracing: Hold trace_types_lock when updating modules in persistent instances Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 037/109] tracing: Reject incomplete event filter expressions Greg Kroah-Hartman
` (79 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, David Carlier, Steven Rostedt
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Carlier <devnexen@gmail.com>
commit 5a6bd62f09147b49b31fd19d4eba122cb5f941a1 upstream.
update_last_data_if_empty() is called from tracing_open() with O_TRUNC,
tracing_read_pipe() and tracing_buffers_read() without trace_types_lock.
Two readers can both pass the LAST_BOOT test and free module_delta
twice, and a module notifier can update module_delta while it is being
freed.
Take trace_types_lock in those paths and keep the callers that already
hold it on an unlocked variant.
Cc: stable@vger.kernel.org
Fixes: 32dc0042528d ("tracing: Reset last-boot buffers when reading out all cpu buffers")
Link: https://patch.msgid.link/20261016061638.214847-2-devnexen@gmail.com
Signed-off-by: David Carlier <devnexen@gmail.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
kernel/trace/trace.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4847,6 +4847,7 @@ static int tracing_single_release_tr(str
return single_release(inode, file);
}
+static bool __update_last_data_if_empty(struct trace_array *tr);
static bool update_last_data_if_empty(struct trace_array *tr);
static int tracing_open(struct inode *inode, struct file *file)
@@ -5916,7 +5917,7 @@ tracing_set_trace_read(struct file *filp
int tracer_init(struct tracer *t, struct trace_array *tr)
{
tracing_reset_online_cpus(&tr->array_buffer);
- update_last_data_if_empty(tr);
+ __update_last_data_if_empty(tr);
return t->init(tr);
}
@@ -6654,8 +6655,10 @@ static int tracing_wait_pipe(struct file
return 1;
}
-static bool update_last_data_if_empty(struct trace_array *tr)
+static bool __update_last_data_if_empty(struct trace_array *tr)
{
+ lockdep_assert_held(&trace_types_lock);
+
if (!(tr->flags & TRACE_ARRAY_FL_LAST_BOOT))
return false;
@@ -6670,6 +6673,16 @@ static bool update_last_data_if_empty(st
return true;
}
+static bool update_last_data_if_empty(struct trace_array *tr)
+{
+ /* Avoid the lock on the read path unless this is a last boot buffer */
+ if (!(tr->flags & TRACE_ARRAY_FL_LAST_BOOT))
+ return false;
+
+ guard(mutex)(&trace_types_lock);
+ return __update_last_data_if_empty(tr);
+}
+
/*
* Consumer reader.
*/
@@ -7599,7 +7612,7 @@ int tracing_set_clock(struct trace_array
ring_buffer_set_clock(tr->max_buffer.buffer, trace_clocks[i].func);
tracing_reset_online_cpus(&tr->max_buffer);
#endif
- update_last_data_if_empty(tr);
+ __update_last_data_if_empty(tr);
if (tr->scratch && !(tr->flags & TRACE_ARRAY_FL_LAST_BOOT)) {
struct trace_scratch *tscratch = tr->scratch;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 037/109] tracing: Reject incomplete event filter expressions
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (35 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 036/109] tracing: Hold trace_types_lock when resetting last boot data from readers Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 038/109] tracing: Save the event file instead of the compatible histogram Greg Kroah-Hartman
` (78 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Masami Hiramatsu (Google),
JongHyuk Kim(MalHyuk), Kyle Zeng, Steven Rostedt
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <kylebot@openai.com>
commit 1fdc4164e488916402badddbb1f51e9380dabb91 upstream.
A non-empty expression normally finishes in predicate_parse()'s inner
loop, which resolves the pending logical operators before jumping to
finalization. If the input ends after an operator, possibly followed by
unary '!' tokens, the outer loop instead falls through with unresolved
operator state.
For example, "common_pid == 1 || common_pid == 2 &&" leaves
prog[0].target equal to -1. The second pass then reads prog[-1] before
the forward-jump check can reject it. Depending on adjacent slab
contents, it can also copy an out-of-bounds word into a branch target
that passes the forward-only check. A self-targeted perf tracepoint
counting event can reach this path through PERF_EVENT_IOC_SET_FILTER.
Reject the outer-loop fallthrough when a predicate has already been
parsed. This ensures that non-empty expressions only reach finalization
after the inner loop has resolved their operators. Keep the existing
no-filter error for input that has no predicates. Add a regression test
for trailing logical operators and unary negations.
Fixes: 80765597bc587 ("tracing: Rewrite filter logic to be simpler and faster")
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20261023174620.91969-1-kylebot@openai.com
Assisted-by: Codex:gpt-6-astra
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Tested-by: JongHyuk Kim(MalHyuk) <malhyuk97@gmail.com>
Signed-off-by: Kyle Zeng <kylebot@openai.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
kernel/trace/trace_events_filter.c | 6 ++
tools/testing/selftests/ftrace/test.d/filter/event-filter-incomplete.tc | 24 ++++++++++
2 files changed, 30 insertions(+)
create mode 100644 tools/testing/selftests/ftrace/test.d/filter/event-filter-incomplete.tc
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -595,6 +595,12 @@ predicate_parse(const char *str, int nr_
top--; /* #12 */
}
}
+ /* Only the inner loop can complete a non-empty expression. */
+ if (N) {
+ ret = -EINVAL;
+ parse_error(pe, FILT_ERR_INVALID_FILTER, ptr - str);
+ goto out_free;
+ }
out:
if (top != op_stack) {
/* Too many '(' */
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/filter/event-filter-incomplete.tc
@@ -0,0 +1,24 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: event filter - reject incomplete logical expressions
+# requires: events/sched/sched_switch/filter
+# flags: instance
+
+filter=events/sched/sched_switch/filter
+
+for base in \
+ 'common_pid == 1' \
+ 'common_pid == 1 || common_pid == 2' \
+ 'common_pid == 1 && common_pid == 2' \
+ '!(common_pid == 1 || common_pid == 2)'
+do
+ printf '%s' "$base" > "$filter"
+ for tail in '&&' '||' '&& !' '|| !' '&& !! ' '|| !! '; do
+ if printf '%s' "$base $tail" > "$filter" 2>/dev/null; then
+ echo "Accepted incomplete filter: $base $tail"
+ exit_fail
+ fi
+ done
+done
+
+echo 0 > "$filter"
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 038/109] tracing: Save the event file instead of the compatible histogram
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (36 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 037/109] tracing: Reject incomplete event filter expressions Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 039/109] tracing: Unregister field variable histograms when the command fails Greg Kroah-Hartman
` (77 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Sashiko, Donggeun Yoo, Tom Zanussi,
Steven Rostedt
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
commit d5c42664fc094899af9aae5300b27fe8fdb658ef upstream.
create_field_var_hist() creates a histogram on the matched event to
supply a field variable, and records it on the target so it can be torn
down later:
hist_data = find_compatible_hist(target_hist_data, file);
...
/* Save the compatible histogram information */
var_hist->hist_data = hist_data;
hist_data is not the histogram it creates. It is one that was already
there, whose keys the new one copies. The saved pointer has a single
user, unregister_field_var_hists(), which runs when the target is
removed:
file = hist_data->field_var_hists[i]->hist_data->event_file;
find_compatible_hist() matches on keys alone, so it can pick one with no
variables of its own. check_var_refs() then returns false and the user
can remove it while the target still points at it:
BUG: KASAN: slab-use-after-free in event_hist_trigger_free+0x2b2/0x320
Read of size 8 at addr ffff8880093b90e0 by task init/1
Freed by task 1:
kfree+0x154/0x420
event_hist_trigger_free+0x1d5/0x320
event_hist_trigger_parse+0x35f3/0x69e0
trigger_process_regex+0x1a6/0x250
Save the event file instead. It is all the dereference ever wanted, and
it does not go away when the user removes a trigger.
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://lore.kernel.org/all/20260914110753.221E61F000FF@smtp.kernel.org/
Link: https://patch.msgid.link/20260914234754.3676905-1-donggeunyoo.kernel@gmail.com
Fixes: 02205a6752f2 ("tracing: Add support for 'field variables'")
Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
Assisted-by: Claude:claude-fable-5
Acked-by: Tom Zanussi <zanussi@kernel.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
kernel/trace/trace_events_hist.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -544,7 +544,7 @@ struct field_var {
};
struct field_var_hist {
- struct hist_trigger_data *hist_data;
+ struct trace_event_file *file;
char *cmd;
};
@@ -3098,8 +3098,8 @@ create_field_var_hist(struct hist_trigge
return ERR_PTR(-ENOMEM);
}
- /* Save the compatible histogram information */
- var_hist->hist_data = hist_data;
+ /* Needed to remove the histogram when the target goes away */
+ var_hist->file = file;
/* Create the new histogram with our variable */
ret = event_hist_trigger_parse(&trigger_hist_cmd, file,
@@ -6327,7 +6327,7 @@ static void unregister_field_var_hists(s
int ret;
for (i = 0; i < hist_data->n_field_var_hists; i++) {
- file = hist_data->field_var_hists[i]->hist_data->event_file;
+ file = hist_data->field_var_hists[i]->file;
cmd = hist_data->field_var_hists[i]->cmd;
ret = event_hist_trigger_parse(&trigger_hist_cmd, file,
"!hist", "hist", cmd);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 039/109] tracing: Unregister field variable histograms when the command fails
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (37 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 038/109] tracing: Save the event file instead of the compatible histogram Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 040/109] tracing: Fix 64-bit and signed histogram fields on 32-bit kernels Greg Kroah-Hartman
` (76 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, sashiko-bot, Donggeun Yoo,
Steven Rostedt
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
commit 91c14d6c13096091c56304120aaa26e479130b8b upstream.
An action that names a field on another event needs that field as a
variable, so create_field_var_hist() registers a second hist trigger on
that event to provide it, and records it in
target_hist_data->field_var_hists[].
If a later part of the same command fails, create_actions() returns an
error and event_hist_trigger_parse() jumps to out_free. The write fails
with -EINVAL and sched_switch gets no trigger, but:
# echo 'hist:keys=pid:ts0=common_timestamp.usecs' >> \
events/sched/sched_waking/trigger
# echo 'my_synth u64 lat; int a; int b' >> synthetic_events
# echo 'hist:keys=next_pid:wakeup_lat=common_timestamp.usecs-$ts0:\
onmatch(sched.sched_waking).my_synth($wakeup_lat,prio,nosuchfld)'\
>> events/sched/sched_switch/trigger
# cat events/sched/sched_waking/trigger
hist:keys=pid:vals=hitcount:ts0=common_timestamp.usecs:sort=hitcount:size=2048:clock=global [active]
hist:keys=pid:vals=hitcount:synthetic_prio=prio:sort=hitcount:size=2048 [active]
The second histogram is the one created for 'prio'. It is still active and
still counting.
out_free reaches destroy_field_var_hists(), which frees the
field_var_hists[] entries, but never unregister_field_var_hists(), which is
what removes the triggers and runs only from event_hist_trigger_free(). The
entry is freed and the trigger is left with nothing tracking it.
Unregister the field variable histograms in out_free as well, and record
each one as soon as its trigger is registered rather than after the lookup
that follows it, so no exit can leave a registered trigger untracked. The
entry then belongs to field_var_hists[] and is released by
destroy_field_var_hists(), so freeing it on that exit would be a double
free.
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260914104036.1768529-1-donggeunyoo.kernel@gmail.com
Fixes: 02205a6752f2 ("tracing: Add support for 'field variables'")
Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/all/20260913204302.DB2551F000FF@smtp.kernel.org/
Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
Assisted-by: Claude:claude-fable-5
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
kernel/trace/trace_events_hist.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -3114,20 +3114,18 @@ create_field_var_hist(struct hist_trigge
kfree(cmd);
+ n = target_hist_data->n_field_var_hists;
+ target_hist_data->field_var_hists[n] = var_hist;
+ target_hist_data->n_field_var_hists++;
+
/* If we can't find the variable, something went wrong */
event_var = find_synthetic_field_var(target_hist_data, subsys_name,
event_name, field_name);
if (IS_ERR_OR_NULL(event_var)) {
- kfree(var_hist->cmd);
- kfree(var_hist);
hist_err(tr, HIST_ERR_SYNTH_VAR_NOT_FOUND, errpos(field_name));
return ERR_PTR(-EINVAL);
}
- n = target_hist_data->n_field_var_hists;
- target_hist_data->field_var_hists[n] = var_hist;
- target_hist_data->n_field_var_hists++;
-
return event_var;
}
@@ -6943,6 +6941,8 @@ static int event_hist_trigger_parse(stru
trigger_data_free(trigger_data);
+ unregister_field_var_hists(hist_data);
+
destroy_hist_data(hist_data);
goto out;
}
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 040/109] tracing: Fix 64-bit and signed histogram fields on 32-bit kernels
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (38 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 039/109] tracing: Unregister field variable histograms when the command fails Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 041/109] tracing/histograms: Verify event_call identity when updating synth_event refcount Greg Kroah-Hartman
` (75 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, sashiko-bot, Donggeun Yoo,
Steven Rostedt
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
commit 443d23176812803d0fa182b8d05ef066a7b8271f upstream.
DEFINE_HIST_FIELD_FN() reads a trace event field and widens it to the u64
the histogram uses as a key or a value:
type *addr = (type *)(event + hist_field->field->offset);
return (u64)(unsigned long)*addr;
unsigned long is 32 bits on a 32-bit kernel, so the intermediate cast
discards the upper half of a 64-bit field, and widens a signed field by
zero extension from 32 bits rather than sign extension from its own
width. Five of the eight instantiations are wrong there: s64, u64, s32,
s16 and s8.
The cast came in with commit 79e577cbce4c ("tracing: Support string type
key properly"), which also added hist_field_dynstring() and
hist_field_pstring(). Those convert a char * to u64 and do warn without
an unsigned long in between on 32-bit. DEFINE_HIST_FIELD_FN() converts an
integer and never a pointer, so it gains nothing from the cast and only
loses information.
Drop the intermediate cast and let the usual arithmetic conversions widen
*addr according to its own type. unsigned long is 64 bits on LP64, so the
two forms are identical there and 64-bit kernels are unaffected.
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260917015532.103081-1-donggeunyoo.kernel@gmail.com
Fixes: 79e577cbce4c ("tracing: Support string type key properly")
Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/all/20260914054614.82A9B1F000FF@smtp.kernel.org/
Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
kernel/trace/trace_events_hist.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -461,7 +461,7 @@ static u64 hist_field_unary_minus(struct
{ \
type *addr = (type *)(event + hist_field->field->offset); \
\
- return (u64)(unsigned long)*addr; \
+ return (u64)*addr; \
}
DEFINE_HIST_FIELD_FN(s64);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 041/109] tracing/histograms: Verify event_call identity when updating synth_event refcount
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (39 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 040/109] tracing: Fix 64-bit and signed histogram fields on 32-bit kernels Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 042/109] net: gso: limit recursive IP-in-IP segmentation Greg Kroah-Hartman
` (74 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Lee Jones, Steven Rostedt
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lee Jones <lee@kernel.org>
commit 3403d9bb27ebc3572bd048620746b4414e073bf9 upstream.
In event_hist_trigger_parse() and hist_unreg_all(), the histogram
registration and unregistration paths look up find_synth_event() using
trace_event_name(file->event_call) and adjust se->ref if a match is
found.
Because find_synth_event() only compares the event basename, registering
or removing a histogram on a non-synthetic trace event whose basename
matches a synthetic event erroneously modifies the synthetic event's
reference count. If a histogram is registered on the non-synthetic
event before the synthetic event is created and removed afterwards,
se->ref is decremented without a matching increment, allowing the
synthetic event to be freed while an active histogram action still holds
a pointer to it.
Introduce find_synth_event_call() to verify that &se->call matches
file->event_call before adjusting se->ref.
Fixes: 4b147936fa50 ("tracing: Add support for 'synthetic' events")
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20261019120730.2342739-1-lee@kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
kernel/trace/trace_events_hist.c | 11 +++--------
kernel/trace/trace_events_synth.c | 10 ++++++++++
kernel/trace/trace_synth.h | 1 +
3 files changed, 14 insertions(+), 8 deletions(-)
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -6753,7 +6753,6 @@ static void hist_unreg_all(struct trace_
struct event_trigger_data *test, *n;
struct hist_trigger_data *hist_data;
struct synth_event *se;
- const char *se_name;
lockdep_assert_held(&event_mutex);
@@ -6766,8 +6765,7 @@ static void hist_unreg_all(struct trace_
list_del_rcu(&test->list);
trace_event_trigger_enable_disable(file, 0);
- se_name = trace_event_name(file->event_call);
- se = find_synth_event(se_name);
+ se = find_synth_event_call(file->event_call);
if (se)
se->ref--;
@@ -6791,7 +6789,6 @@ static int event_hist_trigger_parse(stru
struct hist_trigger_data *hist_data;
char *param, *filter, *p, *start;
struct synth_event *se;
- const char *se_name;
bool remove;
int ret = 0;
@@ -6885,8 +6882,7 @@ static int event_hist_trigger_parse(stru
}
event_trigger_unregister(cmd_ops, file, glob+1, trigger_data);
- se_name = trace_event_name(file->event_call);
- se = find_synth_event(se_name);
+ se = find_synth_event_call(file->event_call);
if (se)
se->ref--;
ret = 0;
@@ -6921,8 +6917,7 @@ static int event_hist_trigger_parse(stru
if (ret)
goto out_unreg;
- se_name = trace_event_name(file->event_call);
- se = find_synth_event(se_name);
+ se = find_synth_event_call(file->event_call);
if (se)
se->ref++;
out:
--- a/kernel/trace/trace_events_synth.c
+++ b/kernel/trace/trace_events_synth.c
@@ -848,6 +848,16 @@ struct synth_event *find_synth_event(con
return NULL;
}
+struct synth_event *find_synth_event_call(struct trace_event_call *call)
+{
+ struct synth_event *se;
+ const char *name;
+
+ name = trace_event_name(call);
+ se = find_synth_event(name);
+ return se && &se->call == call ? se : NULL;
+}
+
static struct trace_event_fields synth_event_fields_array[] = {
{ .type = TRACE_FUNCTION_TYPE,
.define_fields = synth_event_define_fields },
--- a/kernel/trace/trace_synth.h
+++ b/kernel/trace/trace_synth.h
@@ -37,5 +37,6 @@ struct synth_event {
};
extern struct synth_event *find_synth_event(const char *name);
+extern struct synth_event *find_synth_event_call(struct trace_event_call *call);
#endif /* __TRACE_SYNTH_H */
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 042/109] net: gso: limit recursive IP-in-IP segmentation
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (40 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 041/109] tracing/histograms: Verify event_call identity when updating synth_event refcount Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 043/109] io-wq: order the exit bit against worker creation task work Greg Kroah-Hartman
` (73 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Vega, Luxing Yin, Zihan Xi,
Willem de Bruijn, Paolo Abeni, Ting-Han Hou
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zihan Xi <zihanx@nebusec.ai>
commit 006c8b834aed8b02bd8fb9fb05e9e7ead37d1763 upstream.
IP-in-IP GSO can re-enter inet_gso_segment() or ipv6_gso_segment()
for each nested IP header. encap_level tracks header bytes, not callback
depth, so a deep chain can exhaust the kernel stack. Making
inet_gso_segment() stackable introduced unbounded IPv4 nesting; IPIP
GSO/TSO later made the path reachable. The IPv6 stackable path was
introduced separately and uses the same guard.
Count IPv4 and IPv6 GSO handler entries in skb_gso_cb, initialized once
per top-level GSO operation and preserved across GRE/UDP context changes.
Use the existing IP_TUNNEL_RECURSION_LIMIT for both handlers. The first
five entries pass, and the sixth returns -EINVAL before dispatching
another GSO callback.
Fixes: 3347c9602955 ("ipv4: gso: make inet_gso_segment() stackable")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Closes: https://lore.kernel.org/all/cover.1790157745.git.zihanx@nebusec.ai/
Assisted-by: LLM
Co-developed-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Zihan Xi <zihanx@nebusec.ai>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20260924051521.32568-2-zihanx@nebusec.ai
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
[ Ting-Han: ipv6_gso_segment() still calls ipv6_hopopt_jumbo_remove()
right after skb_reset_network_header() in this version; keep the new
recursion check directly after skb_reset_network_header() as upstream
does, before the jumbo removal ]
Signed-off-by: Ting-Han Hou <ue081723@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
include/net/gso.h | 8 ++++++++
net/core/gso.c | 1 +
net/ipv4/af_inet.c | 3 +++
net/ipv6/ip6_offload.c | 4 ++++
4 files changed, 16 insertions(+)
--- a/include/net/gso.h
+++ b/include/net/gso.h
@@ -19,10 +19,18 @@ struct skb_gso_cb {
int encap_level;
__wsum csum;
__u16 csum_start;
+ /* Number of IPv4/IPv6 GSO handler entries for this packet. */
+ u8 recursion_counter;
};
#define SKB_GSO_CB_OFFSET 32
#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)((skb)->cb + SKB_GSO_CB_OFFSET))
+static inline bool gso_recursion_inc_test(struct sk_buff *skb,
+ unsigned int limit)
+{
+ return ++SKB_GSO_CB(skb)->recursion_counter > limit;
+}
+
static inline int skb_tnl_header_len(const struct sk_buff *inner_skb)
{
return (skb_mac_header(inner_skb) - inner_skb->head) -
--- a/net/core/gso.c
+++ b/net/core/gso.c
@@ -117,6 +117,7 @@ struct sk_buff *__skb_gso_segment(struct
SKB_GSO_CB(skb)->mac_offset = skb_headroom(skb);
SKB_GSO_CB(skb)->encap_level = 0;
+ SKB_GSO_CB(skb)->recursion_counter = 0;
skb_reset_mac_header(skb);
skb_reset_mac_len(skb);
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1364,6 +1364,9 @@ struct sk_buff *inet_gso_segment(struct
int id;
skb_reset_network_header(skb);
+ if (unlikely(gso_recursion_inc_test(skb,
+ IP_TUNNEL_RECURSION_LIMIT)))
+ goto out;
nhoff = skb_network_header(skb) - skb_mac_header(skb);
if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))
goto out;
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -17,6 +17,7 @@
#include <net/udp.h>
#include <net/gro.h>
#include <net/gso.h>
+#include <net/ip_tunnels.h>
#include "ip6_offload.h"
@@ -120,6 +121,9 @@ static struct sk_buff *ipv6_gso_segment(
bool gso_partial;
skb_reset_network_header(skb);
+ if (unlikely(gso_recursion_inc_test(skb,
+ IP_TUNNEL_RECURSION_LIMIT)))
+ goto out;
err = ipv6_hopopt_jumbo_remove(skb);
if (err)
return ERR_PTR(err);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 043/109] io-wq: order the exit bit against worker creation task work
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (41 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 042/109] net: gso: limit recursive IP-in-IP segmentation Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 044/109] io_uring/waitid: own the request before clearing ->head Greg Kroah-Hartman
` (72 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Jens Axboe, Oleg Nesterov,
Christian Brauner (Amutable)
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <brauner@kernel.org>
Commit 0e5663856b69219ef2f02e04bac9f5c5be19718a upstream.
io_wq_exit_workers() cancels task work for queued worker creation with
task_work_cancel_match(). That has a plain load of task->task_works.
io_queue_worker_create() adds the new entry and tests IO_WQ_BIT_EXIT to
cancel it if the workqueue is already on its way out.
That must be ordered. task_work_add() has a full barrier via cmpxchg().
But io_wq_exit_start() sets the bit with set_bit() which doesn't have
any memory ordering.
So afaict, on weakly ordered architectures the exiting task may load
task_works before the store of the bit is visible. The other side tests
the bit before the store is visible as well. So the entry remains queued
with worker_refs and the exiting task waits on worker_done indefinitely.
If the task still has rings then io_uring_del_tctx_node() provides the
barrier via test_and_set_bit() in io_wq_set_exit_on_idle(). When it
has closed all rings though that barrier is gone.
Add the barrier after the set_bit().
Fixes: 71a85387546e ("io-wq: check for wq exit after adding new worker task_work")
Cc: stable@vger.kernel.org
Reviewed-by: Jens Axboe <axboe@kernel.dk>
Acked-by: Oleg Nesterov <oleg@redhat.com>
Link: https://patch.msgid.link/20260921-work-coredump-fixes-v3-4-8e4adb1619e6@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io-wq.c | 2 ++
1 file changed, 2 insertions(+)
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -1320,6 +1320,8 @@ static bool io_task_work_match(struct ca
void io_wq_exit_start(struct io_wq *wq)
{
set_bit(IO_WQ_BIT_EXIT, &wq->state);
+ /* Pairs with task_work_add() in io_queue_worker_create(). */
+ smp_mb__after_atomic();
}
static void io_wq_cancel_tw_create(struct io_wq *wq)
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 044/109] io_uring/waitid: own the request before clearing ->head
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (42 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 043/109] io-wq: order the exit bit against worker creation task work Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 045/109] io_uring/cmd: dont leave list poison in ->hash_node Greg Kroah-Hartman
` (71 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit c254eec4364b9f4b4cbdce43be3fb4df5a651a70 upstream.
io_waitid_remove_wq() only takes the wait queue lock if ->head is set.
io_waitid_wait() clears ->head first thing, before it tries to become the
owner of the request by bumping ->refs. If somebody else owns the request
at that point, nothing keeps it from completing the request while the
wake up handler is still going to touch it:
CPU0 (io_waitid_wait) CPU1 (io_waitid, holds a ref)
list_del_init(&wait->entry)
smp_store_release(&iw->head, NULL)
__do_wait() finds another child
io_waitid_remove_wq()
// ->head == NULL, no locking
io_waitid_finish(), IOU_COMPLETE
// request freed and reused
atomic_fetch_inc(&iw->refs)
The same goes for __io_waitid_cancel() if it claims the request in that
window. ->refs is part of the per opcode data of the request, so this
increments whatever the next user of the request keeps there. If that
happens to be zero, the handler concludes that it owns the request, and
queues io_waitid_cb() for it. With the window held open by a delay, and
the request reused for a receive that is waiting for data:
BUG: KASAN: slab-use-after-free in __wake_up_common+0x1ca/0x1f0
__wake_up_sync_key+0x36/0x50
sock_def_readable+0xf1/0x420
unix_stream_sendmsg+0x60b/0x1110
Allocated by task 87:
io_arm_apoll+0x1ed/0x940
Freed by task 87:
__io_submit_flush_completions+0x118d/0x1a10
tctx_task_work_run+0x327/0x950
Only clear ->head once the handler owns the request. If it doesn't, the
owner finds ->head set and has to wait for the lock, which is held by the
caller of the handler. Deleting the already deleted entry again is
harmless.
Fixes: ab673c1bcaf2 ("io_uring/waitid: use io_waitid_remove_wq() consistently")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-1-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/waitid.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
--- a/io_uring/waitid.c
+++ b/io_uring/waitid.c
@@ -265,12 +265,18 @@ static int io_waitid_wait(struct wait_qu
return 0;
list_del_init(&wait->entry);
- smp_store_release(&iw->head, NULL);
- /* cancel is in progress */
+ /*
+ * Cancel or issue is in progress. Leave ->head alone, so that whoever
+ * owns the request has to take the wait queue lock, which we hold,
+ * before it can complete and free it.
+ */
if (atomic_fetch_inc(&iw->refs) & IO_WAITID_REF_MASK)
return 1;
+ /* pairs with the smp_load_acquire() in io_waitid_remove_wq() */
+ smp_store_release(&iw->head, NULL);
+
req->io_task_work.func = io_waitid_cb;
__io_req_task_work_add(req, IOU_F_TWQ_IN_WAKE);
return 1;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 045/109] io_uring/cmd: dont leave list poison in ->hash_node
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (43 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 044/109] io_uring/waitid: own the request before clearing ->head Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 046/109] io_uring/wait: dont spin on a stuck overflow list Greg Kroah-Hartman
` (70 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 4c83e39f4d1944d7146fd06094f2c8053238d55c upstream.
io_uring_cmd_del_cancelable() uses hlist_del() to take a command off
->cancelable_uring_cmd, which leaves LIST_POISON1/2 behind in
->hash_node. Requests are recycled through the request cache without
being reinitialised, and not every user of ->hash_node sets it up before
it is used:
If a vectored futex wait is woken while futex_wait_multiple_setup() is
still queueing the futexes, io_futexv_wait() doesn't add the request to
->futex_list, and leaves the completion to the task_work queued by the
wake up. io_futexv_complete() ends in __io_futex_complete(), whose
hlist_del_init() is fine for a request that is unhashed, which is how all
other users of the node leave it. If the request was a cancelable
uring_cmd in its previous life, e.g. a ublk FETCH_REQ, it writes through
LIST_POISON2:
Oops: general protection fault, probably for non-canonical address
KASAN: maybe wild-memory-access in range [0xdead000000000120-...]
RIP: 0010:io_futexv_complete+0x219/0x420
Call Trace:
tctx_task_work_run+0x804/0x950
tctx_task_work+0x71/0xa0
task_work_run+0x118/0x200
Leave the node unhashed.
Fixes: 93b8cc60c37b ("io_uring: cancelable uring_cmd")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-2-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/uring_cmd.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/io_uring/uring_cmd.c
+++ b/io_uring/uring_cmd.c
@@ -87,7 +87,7 @@ static void io_uring_cmd_del_cancelable(
cmd->flags &= ~IORING_URING_CMD_CANCELABLE;
io_ring_submit_lock(ctx, issue_flags);
- hlist_del(&req->hash_node);
+ hlist_del_init(&req->hash_node);
io_ring_submit_unlock(ctx, issue_flags);
}
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 046/109] io_uring/wait: dont spin on a stuck overflow list
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (44 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 045/109] io_uring/cmd: dont leave list poison in ->hash_node Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 047/109] io_uring/io-wq: dont lose the signal that ends work Greg Kroah-Hartman
` (69 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe,
Claude Fable 5.1
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 34f5a664c34d3cb62f6bec7f4156754594a7d25b upstream.
As long as IO_CHECK_CQ_OVERFLOW_BIT is set, io_cqring_wait_schedule()
returns right away, before it gets to look at signals or arms the timeout,
for io_cqring_wait() to flush the overflow list. If that doesn't empty the
list, and the CQ ring doesn't hold the number of events the caller wants,
the loop starts over, with the same result. The task spins in the kernel,
and can't be killed.
The assumption is that this can't happen: if CQEs can't be flushed, the
ring is full, and min_events is capped to the size of the ring. But:
- With IORING_SETUP_CQE_MIXED a 32b CQE needs two contiguous entries. It
is stuck on the list if a single one is free, and then the ring holds
less than cq_entries events. A single threaded application that asks
for as many events as the ring has entries never gets out of
io_uring_enter().
- Whether the ring is full is up to the CQ head, which the application
can write to. While one thread waits for events on an empty ring,
another one can move the head back so that the ring looks full and
have a CQE overflow. The waiter can't be stopped any more, not even by
SIGKILL.
Only the application can make room in the CQ ring. Return to it if the
list could not be flushed.
[ 6.18: io_cqring_wait() lives in io_uring.c, there is no wait.c yet ]
Fixes: 3fcf19d592d5 ("io_uring: parse check_cq out of wq waiting")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-3-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io_uring.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -2749,9 +2749,18 @@ static int io_cqring_wait(struct io_ring
check_cq = READ_ONCE(ctx->check_cq);
if (unlikely(check_cq)) {
- /* let the caller flush overflows, retry */
- if (check_cq & BIT(IO_CHECK_CQ_OVERFLOW_BIT))
+ if (check_cq & BIT(IO_CHECK_CQ_OVERFLOW_BIT)) {
io_cqring_do_overflow_flush(ctx);
+ /*
+ * No room for what has overflown. It takes the
+ * application to change that, so return to it.
+ */
+ if (test_bit(IO_CHECK_CQ_OVERFLOW_BIT,
+ &ctx->check_cq)) {
+ ret = 0;
+ break;
+ }
+ }
if (check_cq & BIT(IO_CHECK_CQ_DROPPED_BIT)) {
ret = -EBADR;
break;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 047/109] io_uring/io-wq: dont lose the signal that ends work
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (45 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 046/109] io_uring/wait: dont spin on a stuck overflow list Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 048/109] io_uring/io-wq: dont hide a link from cancellations Greg Kroah-Hartman
` (68 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 63c103279a95e82280092f2f4232e91e8117a252 upstream.
When a thread exits, only its tracked requests are cancelled. Then its
io-wq is torn down. io_wq_exit_workers() signals every worker, to get it
out of an interruptible sleep, and waits for all of them to exit. Before
they do, they go through the work that is left. Unbounded work is
cancelled. Bounded work is run, as it is expected to end by itself.
Not all of it does. An open has no file yet, so it counts as bounded, and
the open of a FIFO waits for the other end. That is what the signal is for.
But the workers are signalled once, and a worker that hasn't started the
work by then loses the signal:
io_wq_exit_workers() io_worker_handle_work()
io_wq_worker_wake()
set TIF_NOTIFY_SIGNAL
io_assign_current_work()
io_run_task_work()
clear TIF_NOTIFY_SIGNAL
io_wq_submit_work()
fifo_open()
wait_for_partner()
wait_for_completion_timeout()
The thread never gets out of do_exit():
IORING_OP_OPENAT of a FIFO without a writer, IOSQE_ASYNC
exit of the thread that has submitted it
hangs in more than 9 of 10 attempts (4 CPUs, KVM), until the process is
killed.
Set TIF_NOTIFY_SIGNAL for bounded work that is started with IO_WQ_BIT_EXIT
set. The bit is set before the workers are signalled, and it is tested
after the flag has been cleared, with clear_notify_signal() providing the
barrier. So either the flag stays, or the bit is seen.
Work that is started while the io-wq exits is treated like work that is
being run at that time: it isn't interrupted unless it waits
interruptibly.
Fixes: afcc4015d1bf ("io-wq: provide an io_wq_put_and_exit() helper")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-4-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io-wq.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -648,9 +648,15 @@ static void io_worker_handle_work(struct
next_hashed = wq_next_work(work);
- if (do_kill &&
- (work_flags & IO_WQ_WORK_UNBOUND))
+ /*
+ * io_wq_exit_workers() signals us once, so that we
+ * don't sleep for ever. If that was before this work
+ * became the current one, the signal is gone.
+ */
+ if (do_kill && (work_flags & IO_WQ_WORK_UNBOUND))
atomic_or(IO_WQ_WORK_CANCEL, &work->flags);
+ else if (do_kill)
+ set_notify_signal(current);
io_wq_submit_work(work);
io_assign_current_work(worker, NULL);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 048/109] io_uring/io-wq: dont hide a link from cancellations
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (46 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 047/109] io_uring/io-wq: dont lose the signal that ends work Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 049/109] io_uring: dont flood a cancelling task with task_work Greg Kroah-Hartman
` (67 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe,
Claude Fable 5.1
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 436d48a2ed13112d2d27085325838e5bce6b4507 upstream.
A task that exits cancels its tracked requests and waits until they are
gone. io_uring_cancel_generic() looks everywhere a request can be, and
goes to sleep if it found nothing to cancel. It is woken up each time
one of the requests of the task is completed by task work or freed.
When io-wq is done with a request that has a link, there is a time in which
the next request of the link can't be found:
io_worker_handle_work()
io_assign_current_work(worker, NULL)
io_wq_free_work(work)
io_req_find_next() // takes the link off the request
io_free_req() // task work for the task
io_assign_current_work(worker, linked)
io_run_task_work()
cond_resched()
worker->cur_work = linked
It is not pending, it is not the current work of a worker, and it is not
yet on the list that it is about to be parked on. If the pass that the
exiting task makes because of io_free_req() falls into that time, it finds
nothing and the task goes to sleep. The worker then runs the link, which
IO_WQ_BIT_EXIT doesn't prevent for bounded work. If that parks the request
nothing is completed or freed, and nobody wakes up the task. If it is the
process that exits it can't be killed any more either.
NOP, IOSQE_ASYNC | IOSQE_IO_LINK
FUTEX_WAIT, FUTEX2_PRIVATE
exit of the thread, or of the process, up to 63us later
With four of these per round 5 to 8 of 10,000 rounds hang (4 CPUs, KVM):
io_uring_cancel_generic
do_exit
Commit f8b632e89a10 ("io_uring: tighten task exit cancellations") is about
the same gap, and makes the task look again for whatever happens before
io_free_req().
Free the request once the link is the current work of the worker, or
pending. Whoever is woken up by that can find the link, until it is parked
where cancellations look as well. To do so split io_wq_free_work() in two.
[ 6.18: io_wq_free_work() has no REQ_F_CQE_SKIP link check yet (5530a5102e3c), kept its link logic in the split ]
Fixes: 768134d4f481 ("io_uring: don't do flush cancel under inflight_lock")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-5-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io-wq.c | 18 +++++++++++++++---
io_uring/io_uring.c | 29 +++++++++++++++++++++--------
io_uring/io_uring.h | 3 ++-
3 files changed, 38 insertions(+), 12 deletions(-)
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -640,11 +640,12 @@ static void io_worker_handle_work(struct
/* handle a whole dependent link */
do {
bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state);
- struct io_wq_work *next_hashed, *linked;
+ struct io_wq_work *next_hashed, *linked, *done = work;
unsigned int work_flags = atomic_read(&work->flags);
unsigned int hash = __io_wq_is_hashed(work_flags)
? __io_get_work_hash(work_flags)
: -1U;
+ bool last;
next_hashed = wq_next_work(work);
@@ -660,7 +661,7 @@ static void io_worker_handle_work(struct
io_wq_submit_work(work);
io_assign_current_work(worker, NULL);
- linked = io_wq_free_work(work);
+ last = io_wq_put_work(work, &linked);
work = next_hashed;
if (!work && linked && !io_wq_is_hashed(linked)) {
work = linked;
@@ -669,6 +670,13 @@ static void io_worker_handle_work(struct
io_assign_current_work(worker, work);
if (linked)
io_wq_enqueue(wq, linked);
+ /*
+ * Not before the link is current or pending work again.
+ * This may be the last time that a task is woken up
+ * which exits and looks for requests to cancel.
+ */
+ if (last)
+ io_wq_free_work(done);
if (hash != -1U && !next_hashed) {
/* serialize hash clear with wake_up() */
@@ -1000,9 +1008,13 @@ void io_wq_set_exit_on_idle(struct io_wq
static void io_run_cancel(struct io_wq_work *work, struct io_wq *wq)
{
do {
+ struct io_wq_work *linked;
+
atomic_or(IO_WQ_WORK_CANCEL, &work->flags);
io_wq_submit_work(work);
- work = io_wq_free_work(work);
+ if (io_wq_put_work(work, &linked))
+ io_wq_free_work(work);
+ work = linked;
} while (work);
}
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -1886,17 +1886,30 @@ int io_poll_issue(struct io_kiocb *req,
return ret;
}
-struct io_wq_work *io_wq_free_work(struct io_wq_work *work)
+/*
+ * io-wq is done with @work. Returns true if it held the last reference, and
+ * hands out the next request of the link then. The caller has to make sure
+ * that cancellations can find the latter before it calls io_wq_free_work().
+ */
+bool io_wq_put_work(struct io_wq_work *work, struct io_wq_work **linked)
{
struct io_kiocb *req = container_of(work, struct io_kiocb, work);
struct io_kiocb *nxt = NULL;
- if (req_ref_put_and_test_atomic(req)) {
- if (req->flags & IO_REQ_LINK_FLAGS)
- nxt = io_req_find_next(req);
- io_free_req(req);
- }
- return nxt ? &nxt->work : NULL;
+ *linked = NULL;
+ if (!req_ref_put_and_test_atomic(req))
+ return false;
+
+ if (req->flags & IO_REQ_LINK_FLAGS)
+ nxt = io_req_find_next(req);
+ if (nxt)
+ *linked = &nxt->work;
+ return true;
+}
+
+void io_wq_free_work(struct io_wq_work *work)
+{
+ io_free_req(container_of(work, struct io_kiocb, work));
}
void io_wq_submit_work(struct io_wq_work *work)
@@ -1907,7 +1920,7 @@ void io_wq_submit_work(struct io_wq_work
bool needs_poll = false;
int ret = 0, err = -ECANCELED;
- /* one will be dropped by io_wq_free_work() after returning to io-wq */
+ /* one will be dropped by io_wq_put_work() after returning to io-wq */
if (!(req->flags & REQ_F_REFCOUNT))
__io_req_set_refcount(req, 2);
else
--- a/io_uring/io_uring.h
+++ b/io_uring/io_uring.h
@@ -185,7 +185,8 @@ int io_do_iopoll(struct io_ring_ctx *ctx
__cold void io_iopoll_try_reap_events(struct io_ring_ctx *ctx);
void __io_submit_flush_completions(struct io_ring_ctx *ctx);
-struct io_wq_work *io_wq_free_work(struct io_wq_work *work);
+bool io_wq_put_work(struct io_wq_work *work, struct io_wq_work **linked);
+void io_wq_free_work(struct io_wq_work *work);
void io_wq_submit_work(struct io_wq_work *work);
void io_free_req(struct io_kiocb *req);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 049/109] io_uring: dont flood a cancelling task with task_work
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (47 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 048/109] io_uring/io-wq: dont hide a link from cancellations Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 050/109] io_uring/io-wq: dont go to sleep on pending work Greg Kroah-Hartman
` (66 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 22e643e61b9b6299b42ff7d88bab48bccada8a5d upstream.
io_ring_exit_work() asks each task which has used the ring to remove its
tctx node, by task_work, and repeats that until the list is empty.
io_tctx_exit_cb() doesn't touch the node while the task is in
io_uring_cancel_generic(), which removes all the nodes at its end. So the
work item queues the callback again as soon as it has completed, for
nothing.
That is more than a waste of time. task_work_run() only returns when it
finds the list empty. If the rings are released from inside the
cancellation, because requests of other rings held the last references to
their files, up to 64 work items keep sending callbacks to the one task
which is running them. It never finds the list empty, never gets back to
the cancellation, the nodes stay, and so it goes on: a task with 60 pairs
of rings which poll each other ran 280000 callbacks in 5 seconds, 20
seconds after exit_group(). It ends when all the senders happen to be late
at the same time. Until then the task keeps a CPU and the work items busy.
An exiting task can't be killed meanwhile. execve() cancels the requests in
the same way, so a live task can get stuck there as well, for minutes. If
it takes more than five, the work items run into the
WARN_ON_ONCE(time_after(jiffies, timeout)) of io_ring_exit_work(). No
privilege is needed for any of this.
If the callback has met a task in cancellation, wait a little before it is
sent again. Nobody has to wake the work item up for that: either the node
is gone by then, or it is the turn of the next round.
Fixes: d56d938b4bef ("io_uring: do ctx initiated file note removal")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-6-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io_uring.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -3003,6 +3003,7 @@ struct io_tctx_exit {
struct callback_head task_work;
struct completion completion;
struct io_ring_ctx *ctx;
+ bool in_cancel;
};
static __cold void io_tctx_exit_cb(struct callback_head *cb)
@@ -3017,7 +3018,8 @@ static __cold void io_tctx_exit_cb(struc
* tctx can be NULL if the queueing of this task_work raced with
* work cancelation off the exec path.
*/
- if (tctx && !atomic_read(&tctx->in_cancel))
+ work->in_cancel = tctx && atomic_read(&tctx->in_cancel);
+ if (tctx && !work->in_cancel)
io_uring_del_tctx_node((unsigned long)work->ctx);
complete(&work->completion);
}
@@ -3110,6 +3112,14 @@ static __cold void io_ring_exit_work(str
* wait is marked as interruptible.
*/
wait_for_completion_interruptible(&exit.completion);
+ /*
+ * The task removes the node when it is done with its
+ * cancellation. Give it the time for that: it doesn't get back
+ * to it as long as it finds new task_work each time it has run
+ * the old one, and we may be one of many who send it.
+ */
+ if (exit.in_cancel)
+ schedule_timeout_idle(HZ / 20);
mutex_lock(&ctx->uring_lock);
}
mutex_unlock(&ctx->uring_lock);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 050/109] io_uring/io-wq: dont go to sleep on pending work
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (48 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 049/109] io_uring: dont flood a cancelling task with task_work Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 051/109] io_uring/io-wq: dont strand work when a worker exits Greg Kroah-Hartman
` (65 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit f19c38feb96802ee53a99649d740d2bf630bcce7 upstream.
io_wq_enqueue() wakes a worker off the free list. If there is none it
creates one, unless ->nr_running says that a worker is running. That
relies on whoever takes ->nr_running to zero to check for pending work,
which io_wq_dec_running() no longer does for a worker without
->cur_work, on the grounds that an idle worker is either woken right
away or someone else has created a worker. Neither has to be the case:
worker A (idle) worker B (idle) io_wq_enqueue()
woken for work 1
woken (e.g. timeout) takes work 1
work list is empty
queues work 2
B is first on the free
list: wake_up_process(B)
leaves the free list
work 1 blocks
nr_running 2 -> 1
schedule_timeout()
nr_running 1 -> 0
->cur_work == NULL
Work 2 isn't looked at until work 1 is done or A's idle timeout fires,
five seconds later. ->cur_work is also NULL while a worker blocks on
->uring_lock in io_wq_put_work(), with the same result if it was the
only one running.
Let the idle worker leave ->nr_running by itself before it takes its last
look at the work list. Both that and queueing work are done under
acct->lock, so either the worker sees the work, or the enqueue side sees
that it can't count on this worker. This doesn't bring back the request
for a new worker which the check was meant to avoid, and as idle workers
don't get to io_wq_dec_running() any more, the check can go.
Fixes: 8343cae362e1 ("io_uring/io-wq: ignore non-busy worker going to sleep")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-10-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io-wq.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -465,11 +465,9 @@ static void io_wq_dec_running(struct io_
if (!atomic_dec_and_test(&acct->nr_running))
return;
- if (!worker->cur_work)
- return;
if (!io_acct_run_queue(acct))
return;
- if (io_wq_hash_defer(worker->cur_work, acct)) {
+ if (worker->cur_work && io_wq_hash_defer(worker->cur_work, acct)) {
raw_spin_unlock(&acct->lock);
return;
}
@@ -741,6 +739,19 @@ static int io_wq_worker(void *data)
raw_spin_unlock(&acct->workers_lock);
if (io_run_task_work())
continue;
+ /*
+ * io_wq_enqueue() leaves new work to us for as long as we are
+ * accounted as running. Get out of ->nr_running before taking
+ * the last look at the work list, rather than when scheduling
+ * out. No need to ask for another worker then, we can do it.
+ */
+ clear_bit(IO_WORKER_F_RUNNING, &worker->flags);
+ atomic_dec(&acct->nr_running);
+ if (io_acct_run_queue(acct)) {
+ io_wq_worker_running(current);
+ io_worker_handle_work(acct, worker);
+ continue;
+ }
ret = schedule_timeout(WORKER_IDLE_TIMEOUT);
if (signal_pending(current)) {
struct ksignal ksig;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 051/109] io_uring/io-wq: dont strand work when a worker exits
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (49 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 050/109] io_uring/io-wq: dont go to sleep on pending work Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 052/109] io_uring/io-wq: dont exit with a creation pending Greg Kroah-Hartman
` (64 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit ad41f325be1271b22d99353819c90f0d5e45acc8 upstream.
A worker exits if its idle timeout fired and it isn't the last one, if
its affinity was changed, or if the wq is marked exit-on-idle. It decides
so after it found the work list empty, and until io_worker_exit() is
through it stays on the free list and in ->nr_running:
worker io_wq_enqueue()
io_acct_run_queue() == false
queues work
io_acct_activate_free_worker()
wake_up_process(worker)
// found one, nothing to create
acct->nr_workers--
break
io_worker_exit()
Nobody is going to look at the work. If other workers exist, it has to
wait until one of them finishes what it is blocked on or hits its idle
timeout. If not, which is possible for the affinity and the exit-on-idle
case, it sits there until something else is queued. For exit-on-idle it
takes a task that closes its last ring and sets up a new one while the
workers are on their way out.
Make the worker take itself off the free list when it gives up its
->nr_workers slot, then leave ->nr_running, and only then take the final
look at the work list. That look and queueing work are both done under
acct->lock, so either the worker sees the work and stays, or the enqueue
side sees that the worker is gone and wakes or creates another one. If it
wants to stay and finds that its slot has been taken, a worker has been
created in its place, which is going to look at the work list, and it can
leave after all.
Fixes: 38aa434ab933 ("io_uring/io-wq: add exit-on-idle state")
Fixes: 05c5f4ee4da7 ("io-wq: get rid of FIXED worker flag")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-11-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io-wq.c | 36 +++++++++++++++++++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -693,6 +693,35 @@ static void io_worker_handle_work(struct
} while (1);
}
+/*
+ * The worker has decided to exit, and has given up its ->nr_workers slot and
+ * its place on the free list. io_wq_enqueue() may have picked it from that
+ * list, or seen it running, after it last looked at the work list. Leave
+ * ->nr_running as well and check again. Returns false if the worker has to
+ * stay.
+ */
+static bool io_worker_retire(struct io_wq_acct *acct, struct io_worker *worker)
+{
+ /* turns io_wq_dec_running() and the scheduler hooks into no-ops */
+ clear_bit(IO_WORKER_F_UP, &worker->flags);
+ atomic_dec(&acct->nr_running);
+ if (!io_acct_run_queue(acct))
+ return true;
+ raw_spin_unlock(&acct->lock);
+
+ raw_spin_lock(&acct->workers_lock);
+ if (acct->nr_workers >= acct->max_workers) {
+ /* a worker was created in our place, the work is its business */
+ raw_spin_unlock(&acct->workers_lock);
+ return true;
+ }
+ acct->nr_workers++;
+ raw_spin_unlock(&acct->workers_lock);
+ atomic_inc(&acct->nr_running);
+ set_bit(IO_WORKER_F_UP, &worker->flags);
+ return false;
+}
+
static int io_wq_worker(void *data)
{
struct io_worker *worker = data;
@@ -730,9 +759,14 @@ static int io_wq_worker(void *data)
if ((last_timeout && (exit_mask || acct->nr_workers > 1)) ||
test_bit(IO_WQ_BIT_EXIT_ON_IDLE, &wq->state)) {
acct->nr_workers--;
+ if (test_and_clear_bit(IO_WORKER_F_FREE, &worker->flags))
+ hlist_nulls_del_init_rcu(&worker->nulls_node);
raw_spin_unlock(&acct->workers_lock);
__set_current_state(TASK_RUNNING);
- break;
+ if (io_worker_retire(acct, worker))
+ break;
+ last_timeout = false;
+ continue;
}
last_timeout = false;
__io_worker_idle(acct, worker);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 052/109] io_uring/io-wq: dont exit with a creation pending
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (50 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 051/109] io_uring/io-wq: dont strand work when a worker exits Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 053/109] io_uring/io-wq: let cancel find picked up hashed work Greg Kroah-Hartman
` (63 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit e231d2e8f80d3649ecd3851b7f2abdfbe606c04b upstream.
A worker that is the last one to go to sleep while work is pending asks
for another worker. io_wq_dec_running() bumps ->nr_running on behalf of
the new worker and queues create_worker_cb() as task_work for the owner
of the io-wq. Until that has run, everybody else takes it that a worker
is running which is going to look after the work list: io_wq_enqueue()
doesn't create one, and other workers that go to sleep don't find
themselves to be the last.
If the worker that made the request exits, io_worker_exit() cancels the
task_work, and io_worker_cancel_cb() drops ->nr_running without a look at
the work list:
worker A worker B io_wq_enqueue()
blocks, work is pending
nr_running++
queues create_worker_cb()
wakes, handles the work
idle timeout, decides to exit
picks up work 1
queues work 2
B is on the free list
still: wakes B
work 1 blocks
nr_running 2 -> 1
io_worker_exit()
io_worker_cancel_cb()
nr_running 1 -> 0
Work 2 has to wait for work 1, although there is room for more workers.
It takes an owner that doesn't get around to run its task_work for the
time of the idle timeout, less if the worker exits because its affinity
was changed or the io-wq is marked exit-on-idle.
Let the worker stay around until its request has been dealt with. Only
the worker itself sets ->create_state, so it can't be set behind its
back once it has decided to leave.
Fixes: 1d5f5ea7cb7d ("io-wq: remove worker to owner tw dependency")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-12-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io-wq.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -755,9 +755,13 @@ static int io_wq_worker(void *data)
* idle-exit, drop the worker as well. This is used to avoid
* keeping io-wq workers around for tasks that no longer have
* any active io_uring instances.
+ * Not as long as a request of ours for another worker is
+ * pending, though. It is accounted as a running worker, which
+ * others rely on, and io_worker_exit() would cancel it.
*/
- if ((last_timeout && (exit_mask || acct->nr_workers > 1)) ||
- test_bit(IO_WQ_BIT_EXIT_ON_IDLE, &wq->state)) {
+ if (((last_timeout && (exit_mask || acct->nr_workers > 1)) ||
+ test_bit(IO_WQ_BIT_EXIT_ON_IDLE, &wq->state)) &&
+ !test_bit(0, &worker->create_state)) {
acct->nr_workers--;
if (test_and_clear_bit(IO_WORKER_F_FREE, &worker->flags))
hlist_nulls_del_init_rcu(&worker->nulls_node);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 053/109] io_uring/io-wq: let cancel find picked up hashed work
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (51 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 052/109] io_uring/io-wq: dont exit with a creation pending Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 054/109] io_uring/poll: dont lose a cancel as io-wq arms poll Greg Kroah-Hartman
` (62 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit e9efeb4fc97b0879512a3c3b995ed448f2487e12 upstream.
io_get_next_work() takes all the work items that share a hash off the
work list in one go, and the worker runs them one after the other. Only
the one it is working on is visible as ->cur_work. The others are neither
on the list nor current, so io_wq_cancel_cb() can't find them.
IORING_OP_ASYNC_CANCEL and IORING_REGISTER_SYNC_CANCEL return -ENOENT for
a request that has not even been started, e.g. a buffered write queued
behind another write to the same file that is blocked, and the request is
carried out. The cancellation done for a task or ring that goes away gets
there in the end, as it keeps trying, but it has to wait for each item to
become current.
Keep a pointer to the remainder of the chain next to ->cur_work, and have
io_wq_worker_cancel() look at it. The items on it belong to the worker,
which needs ->lock to move on to the next one. Matching work is flagged
with IO_WQ_WORK_CANCEL and fails with -ECANCELED when its turn comes. The
worker isn't signalled for it, that would interrupt the unrelated work it
is busy with. As the request isn't gone yet, it is reported like running
work, with -EALREADY.
Fixes: 86f3cd1b589a ("io-wq: handle hashed writes in chains")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-13-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io-wq.c | 35 +++++++++++++++++++++++++++++------
1 file changed, 29 insertions(+), 6 deletions(-)
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -54,6 +54,8 @@ struct io_worker {
struct io_wq_acct *acct;
struct io_wq_work *cur_work;
+ /* rest of the hashed chain ->cur_work was taken off the list with */
+ struct io_wq_work *next_work;
raw_spinlock_t lock;
struct completion ref_done;
@@ -579,6 +581,17 @@ static struct io_wq_work *io_get_next_wo
return NULL;
}
+static void __io_assign_current_work(struct io_worker *worker,
+ struct io_wq_work *work)
+{
+ raw_spin_lock(&worker->lock);
+ worker->cur_work = work;
+ /* stays valid while in between two items of a chain */
+ if (work)
+ worker->next_work = wq_next_work(work);
+ raw_spin_unlock(&worker->lock);
+}
+
static void io_assign_current_work(struct io_worker *worker,
struct io_wq_work *work)
{
@@ -587,9 +600,7 @@ static void io_assign_current_work(struc
cond_resched();
}
- raw_spin_lock(&worker->lock);
- worker->cur_work = work;
- raw_spin_unlock(&worker->lock);
+ __io_assign_current_work(worker, work);
}
/*
@@ -620,9 +631,7 @@ static void io_worker_handle_work(struct
* work list, but isn't yet discoverable as the
* current work item for this worker.
*/
- raw_spin_lock(&worker->lock);
- worker->cur_work = work;
- raw_spin_unlock(&worker->lock);
+ __io_assign_current_work(worker, work);
}
raw_spin_unlock(&acct->lock);
@@ -1171,6 +1180,7 @@ static bool __io_wq_worker_cancel(struct
static bool io_wq_worker_cancel(struct io_worker *worker, void *data)
{
struct io_cb_cancel_data *match = data;
+ struct io_wq_work *work;
/*
* Hold the lock to avoid ->cur_work going out of scope, caller
@@ -1179,6 +1189,19 @@ static bool io_wq_worker_cancel(struct i
raw_spin_lock(&worker->lock);
if (__io_wq_worker_cancel(worker, match, worker->cur_work))
match->nr_running++;
+ /*
+ * Hashed work is taken off the work list a chain at a time, what the
+ * worker hasn't started yet can only be found here. It doesn't move on
+ * without the lock. Just mark the work, it fails once it is its turn.
+ */
+ for (work = worker->next_work; work; work = wq_next_work(work)) {
+ if (match->nr_running && !match->cancel_all)
+ break;
+ if (match->fn(work, match->data)) {
+ atomic_or(IO_WQ_WORK_CANCEL, &work->flags);
+ match->nr_running++;
+ }
+ }
raw_spin_unlock(&worker->lock);
return match->nr_running && !match->cancel_all;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 054/109] io_uring/poll: dont lose a cancel as io-wq arms poll
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (52 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 053/109] io_uring/io-wq: let cancel find picked up hashed work Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 055/109] io_uring: dont lose a cancel as io-wq parks a request Greg Kroah-Hartman
` (61 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 6d2d4c2530e95ff6ddeb98195d49d99dbca14369 upstream.
A request on a pollable file that was forced to io-wq is issued without
blocking there. If that ends in -EAGAIN, io_wq_submit_work() arms poll
for it and returns. It looks at IO_WQ_WORK_CANCEL once, before it issues
the request.
Somebody who wants to cancel the request in between finds it running:
io-wq worker io_try_cancel()
io_wq_submit_work()
IO_WQ_WORK_CANCEL isn't set
io_issue_sqe() = -EAGAIN
io_async_cancel_one()
sets IO_WQ_WORK_CANCEL
signals the worker
= -EALREADY
io_poll_cancel() = -ENOENT
...
= -ENOENT
io_arm_poll_handler() = IO_APOLL_OK
return
Nobody looks at the flag again, and the signal doesn't matter to a
worker that doesn't sleep. The request is armed as if nothing had
happened:
- a linked timeout completes with -ENOENT, and the request it was
meant to limit waits for as long as it takes.
- IORING_OP_ASYNC_CANCEL returns -EALREADY, and the request stays.
- IORING_REGISTER_SYNC_CANCEL waits for the request to complete if it
gets -EALREADY. Since commit 5623eb1ed035 ("io_uring/tctx: add
separate lock for list of tctx's in ctx") it doesn't sleep but spins,
so its next attempt finds the armed poll.
The same goes for a multishot poll, recv or accept that is armed from
io-wq.
With a read of an empty pipe, IOSQE_ASYNC, and a linked timeout of 5ms
or a cancel in the same submission, in a guest that is slow enough (TCG,
KASAN), the request is lost in 21 of 24 rounds of 40 or 100 attempts.
While it arms poll from io-wq, __io_arm_poll_handler() owns the request.
Look at the flag once the request is in the hash table. The table is
searched and the request is added with ->uring_lock held, and the flag is
set before the search. So either the search finds the request, or it
came first and the flag is seen. With the request marked as cancelled, the
task work that is queued, right away or because ownership can't be
released, fails it with -ECANCELED.
What the canceller is told doesn't change. In this window a linked timeout
still completes with -ENOENT rather than -ETIME, a cancel request with
-EALREADY, and one with IORING_ASYNC_CANCEL_ALL or _ANY with a count that
doesn't include the request. But the request is cancelled, so what is new
is a request that completes with -ECANCELED after such a result.
This doesn't help in two cases:
- IORING_ASYNC_CANCEL_ALL and _ANY, if the work is flagged after the
test added here, while io-wq still has it as the current work of the
worker. io_cancel_cb() stamps it with the sequence of the cancel, so the
search of the hash table on behalf of the same cancel takes it for
done and skips it.
- requests that are parked elsewhere by io-wq, like IORING_OP_FUTEX_WAIT
and IORING_OP_WAITID. They have the same problem.
Fixes: 90fa02883f06 ("io_uring: implement async hybrid mode for pollable requests")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-14-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/poll.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
--- a/io_uring/poll.c
+++ b/io_uring/poll.c
@@ -614,6 +614,18 @@ static int __io_arm_poll_handler(struct
io_poll_add_hash(req, issue_flags);
+ /*
+ * Whoever tried to cancel the request while io-wq was running it has
+ * flagged the work item. If it searched the hash table before the
+ * request got there it found nothing, and it won't come back. Both
+ * the search and the insertion are done with ->uring_lock held, and
+ * the flag is set before the search. If the search came first,
+ * the flag is visible by now.
+ */
+ if (ipt->owning &&
+ (atomic_read(&req->work.flags) & IO_WQ_WORK_CANCEL))
+ io_poll_mark_cancelled(req);
+
if (mask && (poll->events & EPOLLET) &&
io_poll_can_finish_inline(req, ipt)) {
__io_poll_execute(req, mask, 0);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 055/109] io_uring: dont lose a cancel as io-wq parks a request
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (53 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 054/109] io_uring/poll: dont lose a cancel as io-wq arms poll Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 056/109] io_uring/cancel: dont forget that the work was running Greg Kroah-Hartman
` (60 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 5cb049d2203d7a81a1c1a737c8df4575e71078e3 upstream.
IORING_OP_FUTEX_WAIT, IORING_OP_FUTEX_WAITV and IORING_OP_WAITID don't
block. They put the request on a list of the ring, where a wake up or a
cancellation finds it, and return IOU_ISSUE_SKIP_COMPLETE. They do so as
well if they are run by io-wq, e.g. because of IOSQE_ASYNC.
io_wq_submit_work() looks at IO_WQ_WORK_CANCEL once, before it issues the
request. Somebody who wants to cancel the request after that, and before
it is on the list, finds it running:
io-wq worker io_try_cancel()
io_wq_submit_work()
IO_WQ_WORK_CANCEL isn't set
io_async_cancel_one()
sets IO_WQ_WORK_CANCEL
signals the worker
= -EALREADY
...
io_futex_cancel() = -ENOENT
...
io_futex_wait()
adds the request to ->futex_list
= IOU_ISSUE_SKIP_COMPLETE
Nobody looks at the flag again. IORING_OP_ASYNC_CANCEL returns -EALREADY
and the request stays.
It is the problem that "io_uring/poll: don't lose a cancel as io-wq arms
poll" deals with for poll.
Look at the flag once the request is on the list, and cancel it if it is
set. The lists are searched and the requests are added with ->uring_lock
held, and the flag is set before the search. So either the search finds
the request, or it came first and the flag is seen.
What the canceller is told doesn't change, see the other patch, also for
IORING_ASYNC_CANCEL_ALL and _ANY.
Fixes: 194bb58c6090 ("io_uring: add support for futex wake and wait")
Fixes: 8f350194d5cf ("io_uring: add support for vectored futex waits")
Fixes: f31ecf671ddc ("io_uring: add IORING_OP_WAITID support")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-15-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/futex.c | 4 ++++
io_uring/io_uring.h | 17 +++++++++++++++++
io_uring/waitid.c | 2 ++
3 files changed, 23 insertions(+)
--- a/io_uring/futex.c
+++ b/io_uring/futex.c
@@ -278,6 +278,8 @@ int io_futexv_wait(struct io_kiocb *req,
*/
__set_current_state(TASK_RUNNING);
hlist_add_head(&req->hash_node, &ctx->futex_list);
+ if (io_wq_cancel_seen(req, issue_flags))
+ __io_futex_cancel(req);
} else {
iof->futexv_unqueued = 1;
if (woken != -1)
@@ -318,6 +320,8 @@ int io_futex_wait(struct io_kiocb *req,
&ifd->q, NULL, NULL);
if (!ret) {
hlist_add_head(&req->hash_node, &ctx->futex_list);
+ if (io_wq_cancel_seen(req, issue_flags))
+ __io_futex_cancel(req);
io_ring_submit_unlock(ctx, issue_flags);
return IOU_ISSUE_SKIP_COMPLETE;
--- a/io_uring/io_uring.h
+++ b/io_uring/io_uring.h
@@ -396,6 +396,23 @@ static inline void io_ring_submit_lock(s
lockdep_assert_held(&ctx->uring_lock);
}
+/*
+ * For a handler that is run by io-wq and parks the request on a list where
+ * cancellations find it, rather than completing it. Whoever tried to cancel
+ * the request before that found it running. It has flagged the work item,
+ * has searched the list in vain, and won't come back. To be called with
+ * ->uring_lock held, which the search needs as well, once the request is on
+ * the list: if the search came first, the flag is visible.
+ */
+static inline bool io_wq_cancel_seen(struct io_kiocb *req,
+ unsigned int issue_flags)
+{
+ lockdep_assert_held(&req->ctx->uring_lock);
+
+ return (issue_flags & IO_URING_F_IOWQ) &&
+ (atomic_read(&req->work.flags) & IO_WQ_WORK_CANCEL);
+}
+
static inline void io_commit_cqring(struct io_ring_ctx *ctx)
{
/* order cqe stores with ring update */
--- a/io_uring/waitid.c
+++ b/io_uring/waitid.c
@@ -349,6 +349,8 @@ int io_waitid(struct io_kiocb *req, unsi
* a waitqueue callback, or if someone cancels it.
*/
if (!io_waitid_drop_issue_ref(req)) {
+ if (io_wq_cancel_seen(req, issue_flags))
+ __io_waitid_cancel(req, true);
io_ring_submit_unlock(ctx, issue_flags);
return IOU_ISSUE_SKIP_COMPLETE;
}
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 056/109] io_uring/cancel: dont forget that the work was running
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (54 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 055/109] io_uring: dont lose a cancel as io-wq parks a request Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 057/109] io_uring/cancel: recognize "no timeout" on 32-bit Greg Kroah-Hartman
` (59 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 8aa0e5db4845fb5ac11abe40d7b1cac0656c3ee3 upstream.
If the request to be cancelled is being run by io-wq,
io_async_cancel_one() flags the work, signals the worker and returns
-EALREADY. Since commit ccbf726171b7 ("io_uring: perform poll removal even
if async work removal is successful") io_try_cancel() goes on regardless,
as poll may be armed for the request. If it isn't, and nothing else turns
up, the result is the -ENOENT of the last search.
__io_async_cancel() makes up for that. On -ENOENT it tries the io-wq of
every task that uses the ring, and that of the caller is one of them. So
the work is found a second time, and the result is -EALREADY after all.
Unless the worker is done with it by then. If the work is flagged before
io_wq_submit_work() looks at IO_WQ_WORK_CANCEL, all the worker does is to
queue the request to be failed by task work. Then it isn't anywhere to be
found until the task work has run, and
- IORING_REGISTER_SYNC_CANCEL returns -ENOENT,
- IORING_OP_ASYNC_CANCEL completes with -ENOENT,
for a request that they have just cancelled, and that completes with
-ECANCELED.
With a read of an empty pipe, IOSQE_ASYNC, and a sync cancel right behind
it, 1 to 3 of 1,000,000 calls return -ENOENT, and 50 to 110 with
IORING_SETUP_DEFER_TASKRUN (4 CPUs, KVM). Nothing is written to the pipe,
and all of these reads end with -ECANCELED.
Remember that the work was found. IORING_OP_ASYNC_CANCEL completes with
-EALREADY, as it does if the worker is a bit slower. For
IORING_REGISTER_SYNC_CANCEL it means to try again, which ends with 0.
The result of a linked timeout, which calls io_try_cancel() by itself,
and the number that is returned for IORING_ASYNC_CANCEL_ALL and _ANY don't
change.
Fixes: ccbf726171b7 ("io_uring: perform poll removal even if async work removal is successful")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-16-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/cancel.c | 13 ++++++++++++-
io_uring/cancel.h | 2 ++
2 files changed, 14 insertions(+), 1 deletion(-)
--- a/io_uring/cancel.c
+++ b/io_uring/cancel.c
@@ -118,6 +118,8 @@ int io_try_cancel(struct io_uring_task *
*/
if (!ret)
return 0;
+ if (ret == -EALREADY)
+ cd->running = true;
ret = io_poll_cancel(ctx, cd, issue_flags);
if (ret != -ENOENT)
@@ -174,6 +176,7 @@ static int __io_async_cancel(struct io_c
struct io_tctx_node *node;
int ret, nr = 0;
+ cd->running = false;
do {
ret = io_try_cancel(tctx, cd, issue_flags);
if (ret == -ENOENT)
@@ -195,7 +198,15 @@ static int __io_async_cancel(struct io_c
}
}
io_ring_submit_unlock(ctx, issue_flags);
- return all ? nr : ret;
+ if (all)
+ return nr;
+ /*
+ * The worker may be done with the work that was found running, and
+ * flagged, above. That isn't the same as not having found it.
+ */
+ if (ret == -ENOENT && cd->running)
+ ret = -EALREADY;
+ return ret;
}
int io_async_cancel(struct io_kiocb *req, unsigned int issue_flags)
--- a/io_uring/cancel.h
+++ b/io_uring/cancel.h
@@ -11,6 +11,8 @@ struct io_cancel_data {
u8 opcode;
u32 flags;
int seq;
+ /* io_try_cancel() has found it being run by io-wq, and flagged it */
+ bool running;
};
int io_async_cancel_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 057/109] io_uring/cancel: recognize "no timeout" on 32-bit
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (55 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 056/109] io_uring/cancel: dont forget that the work was running Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 058/109] io_uring/cancel: let CANCEL_ALL find parked requests Greg Kroah-Hartman
` (58 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 3774e4d4c8533da2caae95556c4d635c7c4f45b8 upstream.
IORING_REGISTER_SYNC_CANCEL waits without a limit if both fields of
io_uring_sync_cancel_reg::timeout are -1. io_sync_cancel() compares them
with -1UL. They are of type long long. With a long of 32 bits -1UL is
4294967295, which is what it stays when it is converted for the
comparison, and the fields are never equal to it.
So { -1, -1 } is taken for a timeout. ktime_set() takes the nanoseconds as
an unsigned long, so what becomes of it is -1s + 4294967295ns = 3.29s.
If the request is being run by io-wq and can't be interrupted, the call
returns -ETIME after that time, rather than waiting for the request to be
gone. On i386, with a buffered read from a null_blk device that takes 4s
to complete a request, IOSQE_ASYNC, that is what each call does.
Fixes: 78a861b94959 ("io_uring: add sync cancelation API through io_uring_register()")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-17-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/cancel.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/io_uring/cancel.c
+++ b/io_uring/cancel.c
@@ -310,7 +310,7 @@ int io_sync_cancel(struct io_ring_ctx *c
if (ret != -EALREADY)
goto out;
- if (sc.timeout.tv_sec != -1UL || sc.timeout.tv_nsec != -1UL) {
+ if (sc.timeout.tv_sec != -1 || sc.timeout.tv_nsec != -1) {
struct timespec64 ts = {
.tv_sec = sc.timeout.tv_sec,
.tv_nsec = sc.timeout.tv_nsec
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 058/109] io_uring/cancel: let CANCEL_ALL find parked requests
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (56 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 057/109] io_uring/cancel: recognize "no timeout" on 32-bit Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 059/109] io_uring: wake ring pollers for local task_work Greg Kroah-Hartman
` (57 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit fe6ca13c98b1223f6005f32913ebd32e4df64b77 upstream.
A cancellation with IORING_ASYNC_CANCEL_ALL or _ANY goes on until it finds
nothing more. A request may stay where it was found for a while after it
has been cancelled, so each one that matches is stamped with the sequence
number of the cancellation, and is skipped from then on.
io_cancel_cb() does so for io-wq work as well. There is no need to:
pending work is taken off the list, and the workers are looked at once per
call. And it does harm. A request that is run by io-wq may be parked
while it still is the current work of the worker: poll gets armed for it,
or it is put on ->futex_list or ->waitid_list.
io-wq worker io_try_cancel()
io_wq_submit_work()
io_arm_poll_handler()
io_poll_add_hash()
io_async_cancel_one()
io_cancel_cb()
stamps the request
sets IO_WQ_WORK_CANCEL
= -EALREADY
io_poll_cancel()
skips the request
= -ENOENT
io_assign_current_work(NULL)
The flag comes too late for the worker to see it, and the search that
would have found the request takes it for done. The cancellation returns,
and the request stays.
With a multishot poll, IOSQE_ASYNC, and IORING_OP_ASYNC_CANCEL with
IORING_ASYNC_CANCEL_ALL up to 63us later, that happens to 4 of 1,000
requests (4 CPUs, KVM).
Take the stamp off again if the work matches. If the request is found
on one of the lists it gets a new one, and io_cancel_cb() doesn't match it
a second time.
__io_async_cancel() tries the io-wq of the caller twice. So far the stamp
has kept it from counting work that is running there the second time.
Skip it instead, so that the number that is returned stays the same.
Fixes: 8e29da69fead ("io_uring: add support for IORING_ASYNC_CANCEL_ALL")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-18-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/cancel.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
--- a/io_uring/cancel.c
+++ b/io_uring/cancel.c
@@ -73,7 +73,16 @@ static bool io_cancel_cb(struct io_wq_wo
struct io_kiocb *req = container_of(work, struct io_kiocb, work);
struct io_cancel_data *cd = data;
- return io_cancel_req_match(req, cd);
+ if (!io_cancel_req_match(req, cd))
+ return false;
+ /*
+ * Pending work is gone once it has matched. Work that is running may
+ * park the request where the same cancellation looks next. Don't make
+ * it skip the request as one that it has dealt with.
+ */
+ if (cd->flags & (IORING_ASYNC_CANCEL_ALL | IORING_ASYNC_CANCEL_ANY))
+ req->cancel_seq_set = false;
+ return true;
}
static int io_async_cancel_one(struct io_uring_task *tctx,
@@ -190,6 +199,9 @@ static int __io_async_cancel(struct io_c
io_ring_submit_lock(ctx, issue_flags);
ret = -ENOENT;
list_for_each_entry(node, &ctx->tctx_list, ctx_node) {
+ /* has been done above, don't count what is running there */
+ if (all && node->task->io_uring == tctx)
+ continue;
ret = io_async_cancel_one(node->task->io_uring, cd);
if (ret != -ENOENT) {
if (!all)
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 059/109] io_uring: wake ring pollers for local task_work
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (57 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 058/109] io_uring/cancel: let CANCEL_ALL find parked requests Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 060/109] io_uring/wait: look at all CQ waiters on wake up Greg Kroah-Hartman
` (56 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe,
Claude Fable 5.1
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit b88902d8745a701d24275d012f0830e3628e2a22 upstream.
With IORING_SETUP_DEFER_TASKRUN completions are queued as local work, and
only become CQEs when the submitter enters the kernel to wait for events.
For an application that finds out via poll() or epoll on the ring fd when
it is time to do that, io_uring_poll() reports EPOLLIN if local work is
pending.
That only helps if the work is there at the time of the call. When
DEFER_TASKRUN was added, io_req_local_work_add() did io_cqring_wake(),
which took care of tasks sleeping in poll() as well. Then polling got a
wait queue of its own, ->poll_wq, and the wake up in
io_req_local_work_add() was replaced by waking the submitter directly.
Since then ->poll_wq is only woken when CQEs are committed, which for
such a ring is done by the very task that is waiting to be told to:
submitter completion
poll(ring_fd)
io_uring_poll() // nothing yet
schedule()
io_req_local_work_add()
// ->cq_wait_nr: nobody waits
// sleeps on
Wake ->poll_wq when the first entry is put on the work list. Like for the
eventfd, that must not be done right away if the work is queued from a
wake up handler, as the chain of wake ups could get back to the wait
queue whose lock is held. Use an RCU callback in that case, with a
reference to the ring.
[ 6.18: io_req_local_work_add() lives in io_uring.c, and ->poll_activated is a bitfield rather than an int_flags bit ]
Fixes: 130bd686d9be ("io_uring: waitqueue-less cq waiting")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-19-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
include/linux/io_uring_types.h | 3 +++
io_uring/io_uring.c | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 37 insertions(+)
--- a/include/linux/io_uring_types.h
+++ b/include/linux/io_uring_types.h
@@ -445,6 +445,9 @@ struct io_ring_ctx {
u32 iowq_limits[2];
struct callback_head poll_wq_task_work;
+ /* deferred ->poll_wq wake up for local task_work */
+ struct rcu_head poll_wq_rcu;
+ atomic_t poll_wq_rcu_pending;
struct list_head defer_list;
unsigned nr_drained;
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -1219,6 +1219,37 @@ static void io_ctx_mark_taskrun(struct i
}
}
+static void io_poll_wq_wake_rcu(struct rcu_head *rcu)
+{
+ struct io_ring_ctx *ctx = container_of(rcu, struct io_ring_ctx,
+ poll_wq_rcu);
+
+ atomic_set(&ctx->poll_wq_rcu_pending, 0);
+ io_poll_wq_wake(ctx);
+ percpu_ref_put(&ctx->refs);
+}
+
+/*
+ * io_uring_poll() reports pending local work as EPOLLIN, as it takes a trip to
+ * the kernel to turn it into CQEs. Let those know who are asleep already.
+ */
+static void io_local_work_wake_poll(struct io_ring_ctx *ctx, unsigned int flags)
+{
+ if (!(flags & IOU_F_TWQ_IN_WAKE)) {
+ io_poll_wq_wake(ctx);
+ return;
+ }
+ /*
+ * Called from a wake up handler with some wait queue lock held, this
+ * one could lead back to it. Do it from a clean context.
+ */
+ if (!wq_has_sleeper(&ctx->poll_wq) ||
+ atomic_xchg(&ctx->poll_wq_rcu_pending, 1))
+ return;
+ percpu_ref_get(&ctx->refs);
+ call_rcu_hurry(&ctx->poll_wq_rcu, io_poll_wq_wake_rcu);
+}
+
static void io_req_local_work_add(struct io_kiocb *req, unsigned flags)
{
struct io_ring_ctx *ctx = req->ctx;
@@ -1276,6 +1307,9 @@ static void io_req_local_work_add(struct
io_ctx_mark_taskrun(ctx);
if (ctx->has_evfd)
io_eventfd_signal(ctx, false, flags & IOU_F_TWQ_IN_WAKE);
+ /* whoever sets the flag wakes ->poll_wq afterwards */
+ if (ctx->poll_activated)
+ io_local_work_wake_poll(ctx, flags);
}
nr_wait = atomic_read(&ctx->cq_wait_nr);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 060/109] io_uring/wait: look at all CQ waiters on wake up
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (58 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 059/109] io_uring: wake ring pollers for local task_work Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 061/109] io_uring/poll: drop stale double poll entry on rearm Greg Kroah-Hartman
` (55 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe,
Claude Fable 5.1
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 356f92f2412686f822383b784fa102924407457a upstream.
io_wake_function() returns -1 if the waiter it is called for doesn't have
enough events yet, which makes __wake_up_common() stop walking the wait
queue. That assumes that the waiters behind it can't be satisfied either.
Each waiter has its own target though, computed from the min_complete it
passed in:
thread A: io_uring_enter(GETEVENTS, min_complete = 10) // queued first
thread B: io_uring_enter(GETEVENTS, min_complete = 1)
one CQE is posted: A isn't woken, and B isn't even considered
B keeps sleeping with the event it asked for sitting in the CQ ring, until
A's demand is met or a timeout or signal ends the wait.
Return 0 to carry on with the next waiter. Only waiters whose condition
is met are woken, like before.
[ 6.18: io_wake_function() lives in io_uring.c, there is no wait.c yet ]
Fixes: bda521624e75 ("io_uring: make CQ ring wakeups be more efficient")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-20-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io_uring.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -2525,7 +2525,8 @@ static int io_wake_function(struct wait_
*/
if (io_should_wake(iowq) || io_has_work(iowq->ctx))
return autoremove_wake_function(curr, mode, wake_flags, key);
- return -1;
+ /* keep going, a waiter further down may be content with less */
+ return 0;
}
int io_run_task_work_sig(struct io_ring_ctx *ctx)
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 061/109] io_uring/poll: drop stale double poll entry on rearm
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (59 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 060/109] io_uring/wait: look at all CQ waiters on wake up Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 062/109] io_uring/futex: allocate private hash before waiting Greg Kroah-Hartman
` (54 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit f2dbe87968a3aef74a11c025776d293b80964d3f upstream.
A file that uses two wait queues for poll, e.g. a tty, gets a second
io_poll entry, which IORING_OP_POLL_ADD keeps in ->async_data until the
request is freed. io_poll_add() can run more than once for a request
though: IORING_OP_POLL_REMOVE with one of the update flags disarms and
rearms it, and io_poll_task_func() reissues it if a wake up turns out to
be spurious. The second poll_wait() of the new round then finds
*poll_ptr set, with ->head cleared by the disarm, and __io_queue_proc()
fails what it takes for a third wait queue with -EINVAL.
For an update that means that the poll request is completed with
-ECANCELED, while the update itself reports success. For a spurious wake
up, e.g. tcsetattr() on the polled tty, the poll request fails with
-EINVAL.
Free the old entry and clear the poll flags before arming, which is what
io_arm_apoll() does for internal polling. All entries have been removed
from their wait queues at that point.
Fixes: b69de288e913 ("io_uring: allow events and user_data update of running poll requests")
Fixes: 6e5aedb9324a ("io_uring/poll: attempt request issue after racy poll wakeup")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-21-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/poll.c | 9 +++++++++
1 file changed, 9 insertions(+)
--- a/io_uring/poll.c
+++ b/io_uring/poll.c
@@ -915,6 +915,15 @@ int io_poll_add(struct io_kiocb *req, un
ipt.pt._qproc = io_poll_queue_proc;
+ /*
+ * A poll update or a spurious wake up arms the request again. Forget
+ * the entries of the previous round like io_arm_apoll() does, a stale
+ * second entry would be taken for a third wait queue.
+ */
+ if (req_has_async_data(req))
+ io_req_async_data_free(req);
+ req->flags &= ~(REQ_F_SINGLE_POLL | REQ_F_DOUBLE_POLL);
+
ret = __io_arm_poll_handler(req, poll, &ipt, poll->events, issue_flags);
if (ret > 0) {
io_req_set_res(req, ipt.result_mask, 0);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 062/109] io_uring/futex: allocate private hash before waiting
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (60 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 061/109] io_uring/poll: drop stale double poll entry on rearm Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 063/109] io_uring: drop the CQE cache if a refill fails Greg Kroah-Hartman
` (53 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe,
Claude Fable 5.1
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 3eff68bce25584cded3fb6ec033ff9a1c9913d04 upstream.
The futex code relies on there being no private futex waiters by the time
the first private hash of a mm is installed: __futex_pivot_hash() moves
the waiters from the previous private hash to the new one, but it does not
look for them in the global hash which is used as long as there is no
private hash. That is why every clone(CLONE_VM) allocates it up front: a
task which is alone in its mm cannot block in futex_wait() and create a
thread or invoke prctl(PR_FUTEX_HASH) at the same time.
IORING_OP_FUTEX_WAIT and IORING_OP_FUTEX_WAITV do not block the submitter
though:
io_uring_enter()
io_futex_wait()
futex_wait_setup() // no private hash: queued in the global hash
pthread_create()
futex_hash_allocate_default()
__futex_pivot_hash() // first private hash, nothing is moved
futex(FUTEX_WAKE_PRIVATE)
futex_hash() // private hash: no waiter
The wakeup is lost and the request only completes when it is cancelled.
prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_SET_SLOTS, n) instead of
pthread_create() has the same effect.
Allocate the default private hash when a wait request on a private futex
is prepared, which happens in the context of the submitter. This is a RCU
protected pointer check once the hash exists. A failure to allocate it
fails the request with -ENOMEM.
[ 6.18: the futex vector is a local, not ifd->futexv, so free that on error ]
Fixes: 80367ad01d93 ("futex: Add basic infrastructure for local task local hash")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-22-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/futex.c | 28 ++++++++++++++++++++++++----
1 file changed, 24 insertions(+), 4 deletions(-)
--- a/io_uring/futex.c
+++ b/io_uring/futex.c
@@ -149,6 +149,24 @@ int io_futex_prep(struct io_kiocb *req,
return 0;
}
+/*
+ * A wait on a private futex does not block the submitter. Even a single
+ * threaded task can go on and have the mm private hash installed by clone()
+ * or prctl(), which does not pick up waiters from the global hash. Make
+ * sure the private hash exists before the first waiter is queued.
+ */
+static int io_futex_prep_private(struct io_kiocb *req)
+{
+ int ret = futex_hash_allocate_default();
+
+ if (unlikely(ret))
+ return ret;
+
+ /* inflight tracking only needed for mm private hash */
+ io_req_track_inflight(req);
+ return 0;
+}
+
int io_futex_wait_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
{
struct io_futex *iof = io_kiocb_to_cmd(req, struct io_futex);
@@ -158,9 +176,8 @@ int io_futex_wait_prep(struct io_kiocb *
if (unlikely(ret))
return ret;
- /* inflight tracking only needed for mm private hash */
if (!(iof->futex_flags & FLAGS_SHARED))
- io_req_track_inflight(req);
+ return io_futex_prep_private(req);
return 0;
}
@@ -207,10 +224,13 @@ int io_futexv_prep(struct io_kiocb *req,
return ret;
}
- /* inflight tracking only needed for mm private hash */
for (i = 0; i < iof->futex_nr; i++) {
if (!(futexv[i].w.flags & FLAGS_SHARED)) {
- io_req_track_inflight(req);
+ ret = io_futex_prep_private(req);
+ if (ret) {
+ kfree(futexv);
+ return ret;
+ }
break;
}
}
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 063/109] io_uring: drop the CQE cache if a refill fails
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (61 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 062/109] io_uring/futex: allocate private hash before waiting Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 064/109] io_uring/register: dont wrap 32b CQEs when resizing Greg Kroah-Hartman
` (52 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Leonid Ovchinnikov, Josef Bacik,
Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 78f9c9cffef02a290b6e97672aaa9c75df0d21da upstream.
->cqe_cached and ->cqe_sentinel delimit the CQ entries which can be
handed out without looking at the ring. io_get_cqe_overflow() only calls
io_cqe_cache_refill() if that range is too small. Without
IORING_SETUP_CQE_MIXED too small means empty. With it, a 32b CQE needs
two entries, and one may be left. If the refill then fails, the entry
stays in the cache, and the next 16b CQE gets it without any checks.
That CQE overtakes the 32b one, which went to the overflow list. The test
for IO_CHECK_CQ_OVERFLOW_BIT, which is there to prevent that, is part of
the refill:
CQ ring with 4 entries, head == 0
16b CQE, 32b CQE tail == 3, one entry cached
32b CQE no room, the CQE overflows
16b CQE written to entry 3, ahead of the 32b CQE
Forget about the rest of the cache when it is refilled.
Fixes: e26dca67fde1 ("io_uring: add support for IORING_SETUP_CQE_MIXED")
Reported-by: Leonid Ovchinnikov <teiny.work@gmail.com>
Closes: https://lore.kernel.org/io-uring/CAECZYD5OMD9vMTzjteFMMP40Dn+SWKj9SFj=43U6uO3XMnxJbw@mail.gmail.com/
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-23-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io_uring.c | 7 +++++++
1 file changed, 7 insertions(+)
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -781,6 +781,13 @@ bool io_cqe_cache_refill(struct io_ring_
return false;
/*
+ * A single entry may be left if a 32b CQE is asked for. It must not be
+ * handed out later on if this fails, as the CQE is going to overflow.
+ */
+ ctx->cqe_cached = NULL;
+ ctx->cqe_sentinel = NULL;
+
+ /*
* Post dummy CQE if a 32b CQE is needed and there's only room for a
* 16b CQE before the ring wraps.
*/
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 064/109] io_uring/register: dont wrap 32b CQEs when resizing
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (62 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 063/109] io_uring: drop the CQE cache if a refill fails Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 065/109] io_uring/register: handle index wrap " Greg Kroah-Hartman
` (51 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 0edf1930b8ed3c398d3cbdbafd8f72dbd1a3d9e4 upstream.
On an IORING_SETUP_CQE_MIXED ring, a 32b CQE uses two contiguous CQ
entries and is never allowed to wrap the end of the ring. When posting,
io_cqe_cache_refill() inserts a dummy IORING_CQE_F_SKIP entry if only
one entry is left before the ring wraps, and the application relies on
that to read a 32b CQE as one contiguous 32 byte slot.
io_register_resize_rings() copies pending CQEs to the new ring one 16b
entry at a time, placing entry i at i & (new_entries - 1). When the CQ
ring is shrunk, a pending 32b CQE that was contiguous in the old ring
can land with its first half on the last entry of the new ring and its
second half on entry 0. For example, with 8 old entries, 4 new entries
and a 32b CQE at index 3, the halves end up at entries 3 and 0. The
application then reads the extra data of that CQE from past the end of
the CQ array, and the CQE content it sees is wrong.
This was found by code inspection and has not been reproduced.
Fix by failing the resize with -EOVERFLOW if a pending 32b CQE would
wrap the new ring, the same way the resize fails when the pending CQEs
don't fit.
Fixes: e26dca67fde1 ("io_uring: add support for IORING_SETUP_CQE_MIXED")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-25-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/register.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
--- a/io_uring/register.c
+++ b/io_uring/register.c
@@ -406,6 +406,7 @@ static int io_register_resize_rings(stru
size_t size, sq_array_offset;
unsigned i, tail, old_head;
struct io_uring_params p;
+ bool cqe32_tail = false;
int ret;
/* limited to DEFER_TASKRUN for now */
@@ -558,6 +559,21 @@ overflow:
src_mask = (ctx->cq_entries << 1) - 1;
dst_mask = (p.cq_entries << 1) - 1;
}
+ /*
+ * A 32b CQE on a CQE_MIXED ring takes two contiguous entries
+ * and must not wrap. A smaller ring may place it on the last
+ * entry, fail the resize if that happens.
+ */
+ if (ctx->flags & IORING_SETUP_CQE_MIXED) {
+ if (cqe32_tail) {
+ cqe32_tail = false;
+ } else if (READ_ONCE(o.rings->cqes[index & src_mask].flags) &
+ IORING_CQE_F_32) {
+ if ((index & dst_mask) == dst_mask)
+ goto overflow;
+ cqe32_tail = true;
+ }
+ }
memcpy(&n.rings->cqes[index & dst_mask], &o.rings->cqes[index & src_mask], cq_size);
}
WRITE_ONCE(n.rings->cq.head, old_head);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 065/109] io_uring/register: handle index wrap when resizing
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (63 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 064/109] io_uring/register: dont wrap 32b CQEs when resizing Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 066/109] io_uring/fdinfo: fix CQE dump of CQE32 rings Greg Kroah-Hartman
` (50 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit e8dee8d5498c710a22a84076d8ecb661627032b4 upstream.
The ring heads and tails are free running 32-bit counters.
io_register_resize_rings() takes that into account when it checks whether
the pending entries fit into the new rings, but then copies them with
for (i = old_head; i < tail; i++)
If the tail has wrapped and the head hasn't, that copies nothing. The new
rings get the old head and tail, so the application finds whatever is in
the freshly allocated memory, zeroes, in place of the CQEs it hasn't
reaped yet, or the kernel in place of the SQEs that have not been
submitted yet. A ring that completes a million requests per second gets
there after little more than an hour.
Loop until the tail is reached, the number of iterations has been bounded
by the size of the new ring at that point.
Fixes: 79cfe9e59c2a ("io_uring/register: add IORING_REGISTER_RESIZE_RINGS")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-26-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/register.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/io_uring/register.c
+++ b/io_uring/register.c
@@ -515,7 +515,7 @@ static int io_register_resize_rings(stru
old_head = READ_ONCE(o.rings->sq.head);
if (tail - old_head > p.sq_entries)
goto overflow;
- for (i = old_head; i < tail; i++) {
+ for (i = old_head; i != tail; i++) {
unsigned index, dst_mask, src_mask;
size_t sq_size;
@@ -545,7 +545,7 @@ overflow:
ret = -EOVERFLOW;
goto out;
}
- for (i = old_head; i < tail; i++) {
+ for (i = old_head; i != tail; i++) {
unsigned index, dst_mask, src_mask;
size_t cq_size;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 066/109] io_uring/fdinfo: fix CQE dump of CQE32 rings
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (64 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 065/109] io_uring/register: handle index wrap " Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 067/109] io_uring/timeout: dont skip over reached sequences Greg Kroah-Hartman
` (49 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe,
Claude Fable 5.1
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit e4507a62c247081fffe299622c04c6f7c3d98e92 upstream.
Since the CQ ring dump was changed to deal with IORING_SETUP_CQE_MIXED, a
32b CQE is taken to occupy two entries of the ring. That is how it works
for mixed rings. With IORING_SETUP_CQE32 each entry has twice the size:
head and tail count 32b CQEs, and entry n is found at cqes[n << 1].
For such rings the dump reads cqes[n], and skips every other entry. It
shows half of the pending CQEs at best, and what it shows is taken from
the wrong place unless the head happens to be at the start of the ring.
The extra data of the last entry isn't shown at all, as the check that
keeps the dump of a mixed ring from reading beyond the array is applied to
these rings as well.
[ 6.18: without the last-slot check of f26f49600c9a, which is not in 6.18 ]
Fixes: 82ceb7fcc5ff ("io_uring/fdinfo: handle mixed sized CQEs")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-28-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/fdinfo.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
--- a/io_uring/fdinfo.c
+++ b/io_uring/fdinfo.c
@@ -125,11 +125,17 @@ static void __io_uring_show_fdinfo(struc
cq_entries = min(cq_tail - cq_head, ctx->cq_entries);
for (i = 0; i < cq_entries; i++) {
struct io_uring_cqe *cqe;
- bool cqe32 = false;
+ bool cqe32 = false, two_entries = false;
- cqe = &r->cqes[(cq_head & cq_mask)];
- if (cqe->flags & IORING_CQE_F_32 || ctx->flags & IORING_SETUP_CQE32)
+ if (ctx->flags & IORING_SETUP_CQE32) {
+ /* all entries have twice the size */
+ cqe = &r->cqes[(cq_head & cq_mask) << 1];
cqe32 = true;
+ } else {
+ cqe = &r->cqes[(cq_head & cq_mask)];
+ two_entries = cqe->flags & IORING_CQE_F_32;
+ cqe32 = two_entries;
+ }
seq_printf(m, "%5u: user_data:%llu, res:%d, flag:%x",
cq_head & cq_mask, cqe->user_data, cqe->res,
cqe->flags);
@@ -138,7 +144,7 @@ static void __io_uring_show_fdinfo(struc
cqe->big_cqe[0], cqe->big_cqe[1]);
seq_printf(m, "\n");
cq_head++;
- if (cqe32) {
+ if (two_entries) {
cq_head++;
i++;
}
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 067/109] io_uring/timeout: dont skip over reached sequences
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (65 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 066/109] io_uring/fdinfo: fix CQE dump of CQE32 rings Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 068/109] io_uring/timeout: keep repeat count on multishot update Greg Kroah-Hartman
` (48 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit c6c0b3eba88c3b13ca11ea112404982e86d58f7a upstream.
io_flush_timeouts() copes with the wrap of the CQ sequence by looking at
the distance from ->cq_last_tm_flush. That relies on the target of each
pending timeout being at or ahead of the last flush.
io_timeout() breaks that. It sets ->cq_last_tm_flush to the current
sequence, on the assumption that nothing has been posted since the last
flush. That isn't true. A multishot request posts its CQEs right when it
is issued or retried, but they are committed, and the timeouts flushed,
at the end of the submission or task_work run:
X: IORING_OP_TIMEOUT, off == 1 target_seq == N + 1
io_submit_sqes()
multishot recv, posts two CQEs sequence == N + 2
Y: IORING_OP_TIMEOUT, off == 100 ->cq_last_tm_flush = N + 2
io_flush_timeouts()
X: events_needed = N + 1 - (N + 2) // 0xffffffff
events_got = 0 // not yet
X isn't completed although the completions it was waiting for have
happened, only its timer can end it now. And as the list is sorted and
the walk ends at the first timeout that isn't due, it gets in the way of
the ones behind it. The insertion sort has the same problem, it measures
the distance of the pending targets from the current sequence.
->cq_last_tm_flush only needs to be brought up to date if no timeout is
waiting for a sequence, as io_flush_timeouts() isn't called before the
first one is set up. Otherwise it is current enough. Timeouts with a
sequence come first in the list, so looking at the first entry will do.
Use the last flush as the base for the insertion sort as well.
Fixes: f010505b78a4 ("io_uring: flush timeouts that should already have expired")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-29-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/timeout.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
--- a/io_uring/timeout.c
+++ b/io_uring/timeout.c
@@ -601,8 +601,9 @@ int io_timeout(struct io_kiocb *req, uns
struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout);
struct io_ring_ctx *ctx = req->ctx;
struct io_timeout_data *data = req->async_data;
+ struct io_timeout *first;
struct list_head *entry;
- u32 tail, off = timeout->off;
+ u32 tail, base, off = timeout->off;
raw_spin_lock_irq(&ctx->timeout_lock);
@@ -619,11 +620,17 @@ int io_timeout(struct io_kiocb *req, uns
tail = data_race(ctx->cached_cq_tail) - atomic_read(&ctx->cq_timeouts);
timeout->target_seq = tail + off;
- /* Update the last seq here in case io_flush_timeouts() hasn't.
- * This is safe because ->completion_lock is held, and submissions
- * and completions are never mixed in the same ->completion_lock section.
+ /*
+ * Update the last seq here in case io_flush_timeouts() hasn't, it only
+ * gets called once sequences are in use. Leave it alone if there are
+ * timeouts waiting for one. CQEs may have been posted since the last
+ * flush, and a target that has been reached must not fall behind it.
*/
- ctx->cq_last_tm_flush = tail;
+ first = list_first_entry_or_null(&ctx->timeout_list, struct io_timeout,
+ list);
+ if (!first || io_is_timeout_noseq(cmd_to_io_kiocb(first)))
+ ctx->cq_last_tm_flush = tail;
+ base = ctx->cq_last_tm_flush;
/*
* Insertion sort, ensuring the first entry in the list is always
@@ -635,8 +642,8 @@ int io_timeout(struct io_kiocb *req, uns
if (io_is_timeout_noseq(nxt))
continue;
- /* nxt.seq is behind @tail, otherwise would've been completed */
- if (off >= nextt->target_seq - tail)
+ /* no target is behind the last flush, see io_flush_timeouts() */
+ if (timeout->target_seq - base >= nextt->target_seq - base)
break;
}
add:
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 068/109] io_uring/timeout: keep repeat count on multishot update
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (66 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 067/109] io_uring/timeout: dont skip over reached sequences Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 069/109] io_uring/wait: no early -ETIME if min_wait finds work Greg Kroah-Hartman
` (47 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 3dc2a32cd356ee382aa4a8c7d844c53b2c0ad83b upstream.
IORING_TIMEOUT_UPDATE clears ->off of the timeout it updates, to turn a
timeout that waits for a number of completions into a pure one. For a
multishot timeout ->off isn't a sequence though, it tells whether the
number of repeats is limited: io_timeout_finish() only looks at
->repeats if ->off is set.
A multishot timeout that was set up to fire N times hence fires forever
once its interval has been updated. Leave ->off alone for those, they are
never treated as sequence timeouts anyway.
Fixes: ea97f6c8558e ("io_uring: add support for multishot timeouts")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-30-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/timeout.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
--- a/io_uring/timeout.c
+++ b/io_uring/timeout.c
@@ -433,8 +433,10 @@ static int io_timeout_update(struct io_r
if (IS_ERR(req))
return PTR_ERR(req);
- timeout->off = 0; /* noseq */
data = req->async_data;
+ /* for a multishot request this isn't a sequence, keep the repeats */
+ if (!(data->flags & IORING_TIMEOUT_MULTISHOT))
+ timeout->off = 0; /* noseq */
data->ts = *ts;
list_add_tail(&timeout->list, &ctx->timeout_list);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 069/109] io_uring/wait: no early -ETIME if min_wait finds work
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (67 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 068/109] io_uring/timeout: keep repeat count on multishot update Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 070/109] io_uring/napi: make stale entries time out after 60s Greg Kroah-Hartman
` (46 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe,
Claude Fable 5.1
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 83cce67298ab1f4d2e9205a49f56975475cdb154 upstream.
When the min wait time is over and there is work to run, which for
DEFER_TASKRUN includes task_work that was queued lazily and didn't wake
the waiter, io_cqring_min_timer_wakeup() ends the wait via
io_cqring_timer_wakeup(). That flags it as timed out. io_cqring_wait()
runs the work, leaves the loop, and returns 0 if the CQ ring holds
something.
Running task_work doesn't have to result in a CQE, though. A poll wake up
can turn out to be spurious, for instance if somebody else has read the
data by the time the request is retried. The application then gets -ETIME
once the min wait time has passed, no matter how far away the timeout it
asked for is.
Wake the task without declaring a timeout. From now on a single CQE
satisfies the wait, like in the case where the timer is switched over to
the normal timeout. If running the work doesn't post one, the loop in
io_cqring_wait() goes back to sleep with the normal timeout.
[ 6.18: io_cqring_min_timer_wakeup() lives in io_uring.c, there is no wait.c yet ]
Fixes: 1100c4a2656d ("io_uring: add support for batch wait timeout")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-32-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io_uring.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -2583,9 +2583,16 @@ static enum hrtimer_restart io_cqring_mi
if (iowq->timeout == KTIME_MAX ||
ktime_compare(iowq->min_timeout, iowq->timeout) >= 0)
goto out_wake;
- /* work we may need to run, wake function will see if we need to wake */
- if (io_has_work(ctx))
- goto out_wake;
+ /*
+ * Work we need to run. It isn't known if that is going to post a CQE,
+ * so this isn't a timeout. Carry on with the normal one if it doesn't.
+ */
+ if (io_has_work(ctx)) {
+ iowq->min_timeout = 0;
+ iowq->cq_tail = iowq->cq_min_tail + 1;
+ wake_up_process(iowq->wq.private);
+ return HRTIMER_NORESTART;
+ }
/* got events since we started waiting, min timeout is done */
scoped_guard(rcu) {
struct io_rings *rings = io_get_rings(ctx);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 070/109] io_uring/napi: make stale entries time out after 60s
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (68 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 069/109] io_uring/wait: no early -ETIME if min_wait finds work Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 071/109] io_uring/napi: dont busy poll past an expired timeout Greg Kroah-Hartman
` (45 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 9c151648780d89e5446e5f89f3fa2e416c3bb911 upstream.
With dynamic tracking a NAPI id that hasn't been seen for a minute is
meant to be dropped from the list the ring busy polls. The timeout is
added to jiffies, but it is defined as 60 * SEC_CONVERSION, and
SEC_CONVERSION isn't the number of jiffies in a second. It is the
internal factor of __timespec64_to_jiffies(), HZ scaled up by
SEC_JIFFIE_SC bits. For HZ=1000 that is 1000 << 22, which puts the
timeout at about eight years. On 32-bit the multiplication overflows.
So entries are never removed, and every id that a socket of the ring
ever had keeps being polled on each wait.
Fixes: 8d0c12a80cde ("io-uring: add napi busy poll support")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-33-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/napi.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/io_uring/napi.c
+++ b/io_uring/napi.c
@@ -6,7 +6,7 @@
#ifdef CONFIG_NET_RX_BUSY_POLL
/* Timeout for cleanout of stale entries. */
-#define NAPI_TIMEOUT (60 * SEC_CONVERSION)
+#define NAPI_TIMEOUT (60 * HZ)
struct io_napi_entry {
unsigned int napi_id;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 071/109] io_uring/napi: dont busy poll past an expired timeout
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (69 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 070/109] io_uring/napi: make stale entries time out after 60s Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 072/109] io_uring/zcrx: fix buffer offsets in areas above 4GB Greg Kroah-Hartman
` (44 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 21ed976c12f6b77d7ea028a4fe9a08e69a56179f upstream.
__io_napi_busy_loop() caps the busy poll time to what is left until the
timeout of the wait. The difference is negative if the timeout has passed
already, which is what an application gets if it uses
IORING_ENTER_ABS_TIMER with a time that isn't in the future any more. The
min_t(u64, ...) turns that into a huge value, so the cap has no effect
and the wait, which should return right away, spins for the full busy
poll time first.
Fixes: d5cce407e4f5 ("io_uring/napi: postpone napi timeout adjustment")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-34-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/napi.c | 2 ++
1 file changed, 2 insertions(+)
--- a/io_uring/napi.c
+++ b/io_uring/napi.c
@@ -365,6 +365,8 @@ void __io_napi_busy_loop(struct io_ring_
if (iowq->timeout != KTIME_MAX) {
ktime_t dt = ktime_sub(iowq->timeout, io_get_time(ctx));
+ /* an absolute timeout may have passed already */
+ dt = max_t(ktime_t, dt, 0);
iowq->napi_busy_poll_dt = min_t(u64, iowq->napi_busy_poll_dt, dt);
}
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 072/109] io_uring/zcrx: fix buffer offsets in areas above 4GB
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (70 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 071/109] io_uring/napi: dont busy poll past an expired timeout Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 073/109] io_uring/kbuf: handle index wrap in last buffer check Greg Kroah-Hartman
` (43 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit ab4536bf2e5617b04f2d9396f06393c7bbfb071d upstream.
io_zcrx_queue_cqe() computes the offset of the buffer in its area from
net_iov_idx(), which is an unsigned int, so the shift is done in 32 bits
and only the result is widened to the u64 that goes into the CQE. An area
can be up to 1TB in size. For every niov beyond the first 4GB the CQE
points the application at a buffer 4GB, or a multiple of that, below the
one which has the data. It reads the wrong data, and then returns a
buffer it doesn't own via the refill queue while the real one is never
given back.
Fixes: 11ed914bbf94 ("io_uring/zcrx: add io_recvzc request")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-35-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/zcrx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/io_uring/zcrx.c
+++ b/io_uring/zcrx.c
@@ -962,7 +962,7 @@ static bool io_zcrx_queue_cqe(struct io_
cqe->flags |= IORING_CQE_F_32;
area = io_zcrx_iov_to_area(niov);
- offset = off + (net_iov_idx(niov) << ifq->niov_shift);
+ offset = off + ((u64)net_iov_idx(niov) << ifq->niov_shift);
rcqe = (struct io_uring_zcrx_cqe *)(cqe + 1);
rcqe->off = offset + ((u64)area->area_id << IORING_ZCRX_AREA_SHIFT);
rcqe->__pad = 0;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 073/109] io_uring/kbuf: handle index wrap in last buffer check
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (71 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 072/109] io_uring/zcrx: fix buffer offsets in areas above 4GB Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 074/109] io_uring/sqpoll: let the thread sleep on disabled rings Greg Kroah-Hartman
` (42 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 6e773b5161506a4d0e739eb3aa4667d28f20219c upstream.
io_ring_buffer_select() flags the request with REQ_F_BL_EMPTY if it takes
the last buffer of the ring. Head and tail are 16-bit counters, but
"head + 1" is computed as an int. If head is 65535 and tail has wrapped
to 0, that compares 65536 to 0 and the flag isn't set.
A bundle receive relies on the flag to tell that there is no point in
trying to get more data. Without it, it goes for another round, finds no
buffer, and the request, which has received data, is marked as failed on
account of the -ENOBUFS. That severs a link, and ends a multishot.
Fixes: c3f9109dbc9e ("io_uring/kbuf: flag request if buffer pool is empty after buffer pick")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-36-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/kbuf.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/io_uring/kbuf.c
+++ b/io_uring/kbuf.c
@@ -203,7 +203,7 @@ static struct io_br_sel io_ring_buffer_s
if (unlikely(tail == head))
return sel;
- if (head + 1 == tail)
+ if ((__u16)(head + 1) == tail)
req->flags |= REQ_F_BL_EMPTY;
buf = io_ring_head_to_buf(br, head, bl->mask);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 074/109] io_uring/sqpoll: let the thread sleep on disabled rings
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (72 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 073/109] io_uring/kbuf: handle index wrap in last buffer check Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 075/109] io_uring/openclose: keep the lock in io_pipe_fixed() Greg Kroah-Hartman
` (41 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit d492dcbb3b81c1c2d0aabef41fa1307093f3a556 upstream.
__io_sq_thread() doesn't submit anything as long as the ring has
IORING_SETUP_R_DISABLED set. The check that keeps the SQPOLL thread from
going to sleep while the SQ ring holds entries doesn't know about that.
If an application fills in SQEs first and enables the ring later, the
thread finds that there is work each time it is about to sleep, goes
around, doesn't submit it, and so on. It uses up a CPU until the ring
gets enabled.
Entries in the SQ ring of a disabled ring are no reason to stay awake.
io_register_enable_rings() clears the flag and then wakes the thread, and
the flag is looked at after prepare_to_wait(), so the thread can't miss
that.
Fixes: 0298ef969a11 ("io_uring: clean R_DISABLED startup mess")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-37-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/sqpoll.c | 7 +++++++
1 file changed, 7 insertions(+)
--- a/io_uring/sqpoll.c
+++ b/io_uring/sqpoll.c
@@ -390,6 +390,13 @@ static int io_sq_thread(void *data)
*/
smp_mb__after_atomic();
+ /*
+ * Nothing gets submitted before the ring is
+ * enabled, which comes with a wake up.
+ */
+ if (READ_ONCE(ctx->flags) & IORING_SETUP_R_DISABLED)
+ continue;
+
if (io_sqring_entries(ctx)) {
needs_sched = false;
break;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 075/109] io_uring/openclose: keep the lock in io_pipe_fixed()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (73 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 074/109] io_uring/sqpoll: let the thread sleep on disabled rings Greg Kroah-Hartman
@ 2026-10-28 17:00 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 076/109] io_uring: dont pass restart error codes to userspace Greg Kroah-Hartman
` (40 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:00 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit a3d7ee11289f657aa9af826d8f0844221292a27a upstream.
io_pipe_fixed() installs both ends of the pipe in the fixed file table,
drops ->uring_lock, and copies the slot numbers to the application. If
that faults, it takes the lock again and removes whatever is in these
slots.
If the request is run by io-wq the lock really is dropped in between, and
the slots are visible to everybody else. By the time the error path gets
to them, they may have been closed and reused, by IORING_OP_CLOSE and a
direct open or accept, or by IORING_REGISTER_FILES_UPDATE. Then a file is
removed from the table that has nothing to do with the failed request.
Do the copy with the lock held. Faulting in user memory under
->uring_lock is fine, the registration paths do it all the time.
Fixes: 53db8a71ecb4 ("io_uring: add support for IORING_OP_PIPE")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-38-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/openclose.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
--- a/io_uring/openclose.c
+++ b/io_uring/openclose.c
@@ -366,13 +366,16 @@ static int io_pipe_fixed(struct io_kiocb
fds[1] = alloc_slot ? ret : slot - 1;
files[1] = NULL;
- io_ring_submit_unlock(ctx, issue_flags);
-
- if (!copy_to_user(p->fds, fds, sizeof(fds)))
+ /*
+ * Hang on to the lock. If this fails, the slots have to be cleared, and
+ * they must still hold what was put there.
+ */
+ if (!copy_to_user(p->fds, fds, sizeof(fds))) {
+ io_ring_submit_unlock(ctx, issue_flags);
return 0;
+ }
ret = -EFAULT;
- io_ring_submit_lock(ctx, issue_flags);
err:
if (fds[0] != -1)
io_fixed_fd_remove(ctx, fds[0]);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 076/109] io_uring: dont pass restart error codes to userspace
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (74 preceding siblings ...)
2026-10-28 17:00 ` [PATCH 6.18 075/109] io_uring/openclose: keep the lock in io_pipe_fixed() Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 077/109] io_uring/net: dont retry recv on uncommitted buffers Greg Kroah-Hartman
` (39 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit e1a64bc64897f4c2fe460922c345f2ed8f889b5f upstream.
-ERESTARTSYS and friends are instructions for the system call exit code,
which either restarts the call or turns them into -EINTR. They are not
supposed to be seen by applications. A request isn't a system call,
nobody looks at what it returns before that is put into a CQE.
The read/write and the networking opcodes convert these codes to -EINTR
by hand. The others don't. An io-wq worker that is blocked in the open of
a FIFO, a tty or some other device, or in a splice from an empty pipe,
and that gets interrupted because the request is cancelled, completes it
with -512:
IORING_OP_OPENAT, IOSQE_ASYNC // FIFO without a writer
IORING_OP_ASYNC_CANCEL // -EALREADY, worker is signalled
CQE of the open: res == -512
There are more candidates, like whatever a file system or driver returns
from ->flush(), ->fsync(), ->fallocate() or ->uring_cmd(), or breaking a
lease or delegation.
Take care of it in io_issue_sqe(), which all of them go through. The
helper is moved from rw.c as is. The cost is a compare and a branch that
is not taken per request.
Fixes: 15b71abe7b52 ("io_uring: add support for IORING_OP_OPENAT")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-39-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io_uring.c | 5 +++++
io_uring/io_uring.h | 18 ++++++++++++++++++
io_uring/rw.c | 18 ------------------
3 files changed, 23 insertions(+), 18 deletions(-)
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -1890,6 +1890,9 @@ static int io_issue_sqe(struct io_kiocb
ret = __io_issue_sqe(req, issue_flags, def);
if (ret == IOU_COMPLETE) {
+ /* not all handlers take care of the internal restart codes */
+ if (unlikely(req->cqe.res <= -ERESTARTSYS))
+ req->cqe.res = io_fixup_restart_res(req->cqe.res);
if (issue_flags & IO_URING_F_COMPLETE_DEFER)
io_req_complete_defer(req);
else
@@ -1905,6 +1908,8 @@ static int io_issue_sqe(struct io_kiocb
if ((req->ctx->flags & IORING_SETUP_IOPOLL) && def->iopoll_queue)
io_iopoll_req_issued(req, issue_flags);
}
+ if (unlikely(ret <= -ERESTARTSYS))
+ ret = io_fixup_restart_res(ret);
return ret;
}
--- a/io_uring/io_uring.h
+++ b/io_uring/io_uring.h
@@ -334,6 +334,24 @@ static inline void io_req_set_res32(stru
req->big_cqe.extra2 = extra2;
}
+static inline ssize_t io_fixup_restart_res(ssize_t ret)
+{
+ switch (ret) {
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
+ case -ERESTARTNOHAND:
+ case -ERESTART_RESTARTBLOCK:
+ /*
+ * We can't just restart the syscall, since previously
+ * submitted sqes may already be in progress. Just fail
+ * this IO with EINTR.
+ */
+ return -EINTR;
+ default:
+ return ret;
+ }
+}
+
static inline void *io_uring_alloc_async_data(struct io_alloc_cache *cache,
struct io_kiocb *req)
{
--- a/io_uring/rw.c
+++ b/io_uring/rw.c
@@ -625,24 +625,6 @@ static void io_complete_rw_iopoll(struct
smp_store_release(&req->iopoll_completed, 1);
}
-static inline ssize_t io_fixup_restart_res(ssize_t ret)
-{
- switch (ret) {
- case -ERESTARTSYS:
- case -ERESTARTNOINTR:
- case -ERESTARTNOHAND:
- case -ERESTART_RESTARTBLOCK:
- /*
- * We can't just restart the syscall, since previously
- * submitted sqes may already be in progress. Just fail
- * this IO with EINTR.
- */
- return -EINTR;
- default:
- return ret;
- }
-}
-
static inline void io_rw_done(struct io_kiocb *req, ssize_t ret)
{
struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 077/109] io_uring/net: dont retry recv on uncommitted buffers
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (75 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 076/109] io_uring: dont pass restart error codes to userspace Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 078/109] io_uring/timeout: no ABS update of multishot timeouts Greg Kroah-Hartman
` (38 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 8444de681a179e41a4eb92d5bbe770a0677074a6 upstream.
Commit 41b70df5b38b ("io_uring/net: commit partial buffers on retry")
commits ring provided buffers when a receive is going to be retried after
a partial transfer, so that another request using the same buffer group
can't be handed the memory the first one keeps receiving into. It commits
the number of bytes received so far. That does the job for a single
buffer which is consumed as a whole, but the request hangs on to all it
has mapped:
- For a buffer ring with IOU_PBUF_RING_INC only these bytes are consumed.
The rest of the buffer stays at the head of the ring.
- For a bundle only the buffers which hold these bytes are consumed, the
others that were peeked stay in the ring.
In both cases the next receive with that buffer group gets memory that
the retried request is still going to write to, and whoever comes last
overwrites the data of the other. What the retry fills isn't committed at
all, as the buffer list isn't known any more at that point. So the kernel
hands it out again later on, while the CQE tells the application that it
was used, and for an incremental buffer that it is done with.
The remainder can't be committed in advance: if the request ends up
receiving less, there is no way to tell the application that more was
consumed than the CQE accounts for. Complete the request with what it has
got instead of retrying it. MSG_WAITALL is no longer honoured for
receives into incremental buffers or multi buffer bundles, they can
return a short count, with the request marked as failed like for other
short MSG_WAITALL transfers.
Fixes: 41b70df5b38b ("io_uring/net: commit partial buffers on retry")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-40-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/net.c | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
--- a/io_uring/net.c
+++ b/io_uring/net.c
@@ -498,6 +498,24 @@ static int io_bundle_nbufs(struct io_asy
return nbufs;
}
+/*
+ * A receive that is retried after a partial transfer keeps writing to what it
+ * has mapped. io_net_kbuf_recyle() only commits what was filled so far, which
+ * takes a buffer out of circulation if it's a single one that is consumed as a
+ * whole. The tail of an incrementally consumed buffer, or the yet unused
+ * buffers of a bundle, would be up for grabs. They can't be committed in
+ * advance either, the CQE has no way to tell if they end up not being used.
+ */
+static bool io_net_kbuf_can_retry(struct io_kiocb *req,
+ struct io_buffer_list *bl,
+ struct io_async_msghdr *kmsg)
+{
+ /* not ring provided, or committed at selection time */
+ if (!(req->flags & REQ_F_BUFFERS_COMMIT))
+ return true;
+ return !(bl->flags & IOBL_INC) && iter_is_ubuf(&kmsg->msg.msg_iter);
+}
+
static int io_net_kbuf_recyle(struct io_kiocb *req, struct io_buffer_list *bl,
struct io_async_msghdr *kmsg, int len)
{
@@ -1089,7 +1107,8 @@ retry_multishot:
io_kbuf_recycle(req, sel.buf_list, issue_flags);
return IOU_RETRY;
}
- if (ret > 0 && io_net_retry(sock, flags)) {
+ if (ret > 0 && io_net_retry(sock, flags) &&
+ io_net_kbuf_can_retry(req, sel.buf_list, kmsg)) {
sr->done_io += ret;
return io_net_kbuf_recyle(req, sel.buf_list, kmsg, ret);
}
@@ -1238,7 +1257,8 @@ retry_multishot:
io_kbuf_recycle(req, sel.buf_list, issue_flags);
return IOU_RETRY;
}
- if (ret > 0 && io_net_retry(sock, flags)) {
+ if (ret > 0 && io_net_retry(sock, flags) &&
+ io_net_kbuf_can_retry(req, sel.buf_list, kmsg)) {
sr->len -= ret;
sr->buf += ret;
sr->done_io += ret;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 078/109] io_uring/timeout: no ABS update of multishot timeouts
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (76 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 077/109] io_uring/net: dont retry recv on uncommitted buffers Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 079/109] io_uring/timeout: dont end a linked timeout with 1 Greg Kroah-Hartman
` (37 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit b088ae34d4da730dc647261b893a6eb622fe5e1f upstream.
IORING_TIMEOUT_MULTISHOT can't be combined with IORING_TIMEOUT_ABS, prep
rejects that, as the request is armed again with the same value each time
it fires. IORING_TIMEOUT_UPDATE doesn't check and takes an absolute time
for a multishot timeout. The first expiry is as asked for, but
io_timeout_complete() then arms the timer with ->mode of the original
request, i.e. it uses the absolute time as the interval. With
CLOCK_MONOTONIC the next expiry is an uptime away, with CLOCK_REALTIME
more than fifty years.
Fail such an update with -EINVAL, and do so before the timeout is
touched.
Fixes: ea97f6c8558e ("io_uring: add support for multishot timeouts")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-41-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/timeout.c | 38 ++++++++++++++++++++++++++------------
1 file changed, 26 insertions(+), 12 deletions(-)
--- a/io_uring/timeout.c
+++ b/io_uring/timeout.c
@@ -278,22 +278,29 @@ static enum hrtimer_restart io_timeout_f
return HRTIMER_NORESTART;
}
-static struct io_kiocb *io_timeout_extract(struct io_ring_ctx *ctx,
- struct io_cancel_data *cd)
+static struct io_kiocb *io_timeout_find(struct io_ring_ctx *ctx,
+ struct io_cancel_data *cd)
__must_hold(&ctx->timeout_lock)
{
struct io_timeout *timeout;
- struct io_timeout_data *io;
- struct io_kiocb *req = NULL;
list_for_each_entry(timeout, &ctx->timeout_list, list) {
- struct io_kiocb *tmp = cmd_to_io_kiocb(timeout);
+ struct io_kiocb *req = cmd_to_io_kiocb(timeout);
- if (io_cancel_req_match(tmp, cd)) {
- req = tmp;
- break;
- }
+ if (io_cancel_req_match(req, cd))
+ return req;
}
+ return NULL;
+}
+
+static struct io_kiocb *io_timeout_extract(struct io_ring_ctx *ctx,
+ struct io_cancel_data *cd)
+ __must_hold(&ctx->timeout_lock)
+{
+ struct io_kiocb *req = io_timeout_find(ctx, cd);
+ struct io_timeout *timeout;
+ struct io_timeout_data *io;
+
if (!req)
return ERR_PTR(-ENOENT);
@@ -426,14 +433,21 @@ static int io_timeout_update(struct io_r
__must_hold(&ctx->timeout_lock)
{
struct io_cancel_data cd = { .ctx = ctx, .data = user_data, };
- struct io_kiocb *req = io_timeout_extract(ctx, &cd);
+ struct io_kiocb *req = io_timeout_find(ctx, &cd);
struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout);
struct io_timeout_data *data;
- if (IS_ERR(req))
- return PTR_ERR(req);
+ if (!req)
+ return -ENOENT;
data = req->async_data;
+ /* multishot requests only make sense with rel values */
+ if ((data->flags & IORING_TIMEOUT_MULTISHOT) && mode == HRTIMER_MODE_ABS)
+ return -EINVAL;
+
+ req = io_timeout_extract(ctx, &cd);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
/* for a multishot request this isn't a sequence, keep the repeats */
if (!(data->flags & IORING_TIMEOUT_MULTISHOT))
timeout->off = 0; /* noseq */
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 079/109] io_uring/timeout: dont end a linked timeout with 1
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (77 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 078/109] io_uring/timeout: no ABS update of multishot timeouts Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 080/109] io_uring: look up the file before queueing to io-wq Greg Kroah-Hartman
` (36 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit c74193430febdd120e35936dda3e5aeec3a54405 upstream.
A linked timeout that fires cancels the request it is linked to, and
completes with -ETIME if that worked out:
ret = io_try_cancel(req->tctx, &cd, 0);
...
io_req_set_res(req, ret ?: -ETIME, 0);
io_try_cancel() returns 0 or an error, except for requests that are parked
on ->futex_list or ->waitid_list. io_futex_cancel() and io_waitid_cancel()
return the number of requests that they have cancelled, and
io_try_cancel() hands it on.
So the linked timeout of an IORING_OP_FUTEX_WAIT, IORING_OP_FUTEX_WAITV or
IORING_OP_WAITID that it has cancelled completes with 1, each time.
The waitid test of liburing checks for that 1, and has to be changed to
expect -ETIME, which is what the linked timeout of any other request
completes with.
Fixes: f31ecf671ddc ("io_uring: add IORING_OP_WAITID support")
Fixes: 194bb58c6090 ("io_uring: add support for futex wake and wait")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-42-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/timeout.c | 3 +++
1 file changed, 3 insertions(+)
--- a/io_uring/timeout.c
+++ b/io_uring/timeout.c
@@ -342,6 +342,9 @@ static void io_req_task_link_timeout(str
};
ret = io_try_cancel(req->tctx, &cd, 0);
+ /* a number of requests for futex and waitid */
+ if (ret > 0)
+ ret = 0;
} else {
ret = -ECANCELED;
}
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 080/109] io_uring: look up the file before queueing to io-wq
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (78 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 079/109] io_uring/timeout: dont end a linked timeout with 1 Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 081/109] io_uring/poll: avoid retry wakeup loss when dropping ownership Greg Kroah-Hartman
` (35 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Josef Bacik, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <josef@toxicpanda.com>
Commit 98bc044d5a5374f8d3605fe55a8e2df6d6fc3f82 upstream.
io_prep_async_work() decides by the file of a request what kind of work
it is for io-wq. I/O on regular files and block devices is bounded work,
limited to min(SQ entries, 4 * CPUs) workers, for anything else that may
block indefinitely the limit is RLIMIT_NPROC. Writes to a regular file
are hashed by the inode, so that they are run one after the other instead
of having a bunch of workers fight for the inode lock.
Since the file is only assigned when a request is issued, a request that
is queued without an attempt to issue it, REQ_F_FORCE_ASYNC, doesn't have
one at that point. io_prep_async_work() takes that for "not a regular
file or block device":
- Reads and writes end up as unbound work. 32 reads with IOSQE_ASYNC from
a slow block device on a ring with 4 SQ entries get 32 workers, not 4.
- Writes aren't hashed. Four buffered writes to one file get four
workers, three of which do nothing but wait for the lock.
The late assignment is for the benefit of links, where a request may use
a fixed file that one of its predecessors installs. By the time a request
gets to io_queue_iowq() these are done, it is as good as issued. Assign
the file there.
io_assign_file() moves up and is forced inline. With a third caller the
compiler may stop inlining it, and every request that is issued would
pay for a call.
IORING_OP_SPLICE is left as it is, unbound and not hashed. The file of
the request is where it writes to, which says nothing about how long it
takes: a splice from an empty pipe or a socket into a regular file waits
for its input. As bounded work, four of these on a ring with 4 SQ entries
keep all other bounded work of the task from running, and as hashed work
one of them blocks all writes to that file.
REQ_F_FORCE_ASYNC is not only set for IOSQE_ASYNC. What changes, and
brings back what was the case before the file assignment was deferred:
- Requests with IOSQE_ASYNC or IOSQE_IO_DRAIN, and all requests that are
submitted while a drain is active: reads and writes on regular files
and block devices count against the limit for bounded workers, including
the one set with IORING_REGISTER_IOWQ_MAX_WORKERS, so fewer of them run
at the same time.
- Of these, writes to one regular file are run one at a time, unless they
are O_DIRECT and the filesystem has FOP_DIO_PARALLEL_WRITE.
- IORING_OP_FALLOCATE and IORING_OP_FTRUNCATE, which always go to io-wq,
are run one at a time per file, and in turn with these writes.
- The descriptor or the fixed file slot is looked up when the request is
queued, not when a worker gets around to it, so closing or replacing it
in between no longer has an effect on the request.
Fixes: 6bf9c47a3989 ("io_uring: defer file assignment")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-43-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io_uring.c | 43 +++++++++++++++++++++++++++++--------------
1 file changed, 29 insertions(+), 14 deletions(-)
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -466,17 +466,46 @@ static void io_prep_async_link(struct io
}
}
+/* Called for every request that is issued: keep it inline */
+static __always_inline bool io_assign_file(struct io_kiocb *req,
+ const struct io_issue_def *def,
+ unsigned int issue_flags)
+{
+ if (req->file || !def->needs_file)
+ return true;
+
+ if (req->flags & REQ_F_FIXED_FILE)
+ req->file = io_file_get_fixed(req, req->cqe.fd, issue_flags);
+ else
+ req->file = io_file_get_normal(req, req->cqe.fd);
+
+ return !!req->file;
+}
+
static void io_queue_iowq(struct io_kiocb *req)
{
struct io_uring_task *tctx = req->tctx;
BUG_ON(!tctx);
+ lockdep_assert_held(&req->ctx->uring_lock);
if ((current->flags & PF_KTHREAD) || !tctx->io_wq) {
io_req_task_queue_fail(req, -ECANCELED);
return;
}
+ /*
+ * The file is looked up as late as possible, as it may be the result
+ * of an earlier request of the link. This one is up now, and what kind
+ * of work it is depends on the file. If that fails, leave the error
+ * handling to the worker.
+ *
+ * Not for a splice: whatever it writes to, it may wait for its input
+ * for ever. It has to remain unbound work that isn't hashed.
+ */
+ if (req->opcode != IORING_OP_SPLICE)
+ io_assign_file(req, &io_issue_defs[req->opcode], 0);
+
/* init ->work of the whole link before punting */
io_prep_async_link(req);
@@ -1830,20 +1859,6 @@ static __cold void io_drain_req(struct i
ctx->drain_active = false;
}
-static bool io_assign_file(struct io_kiocb *req, const struct io_issue_def *def,
- unsigned int issue_flags)
-{
- if (req->file || !def->needs_file)
- return true;
-
- if (req->flags & REQ_F_FIXED_FILE)
- req->file = io_file_get_fixed(req, req->cqe.fd, issue_flags);
- else
- req->file = io_file_get_normal(req, req->cqe.fd);
-
- return !!req->file;
-}
-
#define REQ_ISSUE_SLOW_FLAGS (REQ_F_CREDS | REQ_F_ARM_LTIMEOUT)
static inline int __io_issue_sqe(struct io_kiocb *req,
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 081/109] io_uring/poll: avoid retry wakeup loss when dropping ownership
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (79 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 080/109] io_uring: look up the file before queueing to io-wq Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 082/109] io_uring/timeout: dont put a sequence target behind the last flush Greg Kroah-Hartman
` (34 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Juanlu Herrero, Anand Natarajan,
Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Juanlu Herrero <juanlu@fastmail.com>
Commit 937d38dcacd675dc0572d6c9b6743960df3128ed upstream.
If a wakeup sets IO_POLL_RETRY_FLAG between the owning task consuming
the existing instance of IO_POLL_RETRY_FLAG and the loop check
of io_poll_check_events(), then we stall on processing the request
until an unrelated wakeup comes along.
Using atomic_cmpxchg instead of atomic_sub_return to guarantee both the
count v and poll_refs are 0 and no wake-ups are missed.
Cc: stable@vger.kernel.org
Fixes: a26a35e9019f ("io_uring: make poll refs more robust")
Signed-off-by: Juanlu Herrero <juanlu@fastmail.com>
Assisted-by: LLM
Suggested-by: Anand Natarajan <anand@meta.com>
Link: https://patch.msgid.link/20261013-pu-cmpxchg-v1-1-4ddc10fd8a03@fastmail.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/poll.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
--- a/io_uring/poll.c
+++ b/io_uring/poll.c
@@ -310,7 +310,8 @@ static int io_poll_check_events(struct i
* Release all references, retry if someone tried to restart
* task_work while we were executing it.
*/
- } while (atomic_sub_return(v, &req->poll_refs) & IO_POLL_REF_MASK);
+ v &= IO_POLL_REF_MASK;
+ } while (atomic_cmpxchg(&req->poll_refs, v, 0) != v);
io_napi_add(req);
return IOU_POLL_NO_ACTION;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 082/109] io_uring/timeout: dont put a sequence target behind the last flush
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (80 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 081/109] io_uring/poll: avoid retry wakeup loss when dropping ownership Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 083/109] io_uring/timeout: dont flush with the sequence " Greg Kroah-Hartman
` (33 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jens Axboe <axboe@kernel.dk>
Commit fdfce905a58edea053ee7b68b5bd4348dd1b36e0 upstream.
io_timeout_fn() and io_kill_timeout() bump ->cq_timeouts when a timeout
fires, but its CQE is only posted later from task_work. Until then the
sequence io_timeout() computes is one below the last flush per fired
timeout. Now that ->cq_last_tm_flush is kept while sequence timeouts are
pending, a timeout armed in that window with a small offset gets a target
behind the base. Its distance from the base wraps, and it's never
completed by events, only by its timer. With DEFER_TASKRUN that window
is as wide as the application makes it, as submission runs before the
local task_work.
Nothing but those in flight CQEs separates the two, so use the last
flush as the base for the target when the sequence is behind it.
Fixes: c6c0b3eba88c ("io_uring/timeout: don't skip over reached sequences")
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/timeout.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
--- a/io_uring/timeout.c
+++ b/io_uring/timeout.c
@@ -637,7 +637,6 @@ int io_timeout(struct io_kiocb *req, uns
}
tail = data_race(ctx->cached_cq_tail) - atomic_read(&ctx->cq_timeouts);
- timeout->target_seq = tail + off;
/*
* Update the last seq here in case io_flush_timeouts() hasn't, it only
@@ -652,6 +651,15 @@ int io_timeout(struct io_kiocb *req, uns
base = ctx->cq_last_tm_flush;
/*
+ * A fired timeout bumps ->cq_timeouts before its CQE is posted, which
+ * leaves the sequence behind the last flush until it is. Nothing else
+ * separates the two then, so count from the flush.
+ */
+ if ((s32)(tail - base) < 0)
+ tail = base;
+ timeout->target_seq = tail + off;
+
+ /*
* Insertion sort, ensuring the first entry in the list is always
* the one we need first.
*/
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 083/109] io_uring/timeout: dont flush with the sequence behind the last flush
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (81 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 082/109] io_uring/timeout: dont put a sequence target behind the last flush Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 084/109] io_uring/timeout: dont lose a cancel as io-wq arms a timeout Greg Kroah-Hartman
` (32 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jens Axboe <axboe@kernel.dk>
Commit 85f85d573ffa7772e1ad4504d0cec5113a5c5cf1 upstream.
io_flush_timeouts() measures the events since the last flush as the
distance of the current sequence from ->cq_last_tm_flush. A fired
timeout bumps ->cq_timeouts before its CQE is posted, so until it is
the sequence can be behind the last flush, and the distance wraps:
last flush: seq 100, cq_last_tm_flush = 100
Y: IORING_OP_TIMEOUT, off 50, target_seq 150
pure timeout A fires, cq_timeouts = 1
task_work posts A's CQE, cached_cq_tail = 101
pure timeout B fires before the flush, cq_timeouts = 2
io_flush_timeouts(): seq = 99, events_got = 0xffffffff
Y, and every sequence timeout behind it, is ended with -ETIME although
no event happened. Two timeouts expiring close together is all it takes,
a completion with IOSQE_CQE_SKIP_SUCCESS after a single one does it too.
Nothing but those in flight CQEs separates the two, so count from the
last flush when the sequence is behind it, as io_timeout() does.
Fixes: f010505b78a4 ("io_uring: flush timeouts that should already have expired")
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/timeout.c | 8 ++++++++
1 file changed, 8 insertions(+)
--- a/io_uring/timeout.c
+++ b/io_uring/timeout.c
@@ -132,6 +132,14 @@ __cold void io_flush_timeouts(struct io_
raw_spin_lock_irq(&ctx->timeout_lock);
seq = READ_ONCE(ctx->cached_cq_tail) - atomic_read(&ctx->cq_timeouts);
+ /*
+ * A fired timeout bumps ->cq_timeouts before its CQE is posted, which
+ * leaves the sequence behind the last flush until it is. No event has
+ * happened then, see io_timeout().
+ */
+ if ((s32)(seq - ctx->cq_last_tm_flush) < 0)
+ seq = ctx->cq_last_tm_flush;
+
list_for_each_entry_safe(timeout, tmp, &ctx->timeout_list, list) {
struct io_kiocb *req = cmd_to_io_kiocb(timeout);
u32 events_needed, events_got;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 084/109] io_uring/timeout: dont lose a cancel as io-wq arms a timeout
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (82 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 083/109] io_uring/timeout: dont flush with the sequence " Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 085/109] io_uring/poll: dont loop on HUP with multiple references Greg Kroah-Hartman
` (31 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jens Axboe <axboe@kernel.dk>
Commit 89f193dc3ef462059387e9691bf6af4b1808379a upstream.
An IORING_OP_TIMEOUT run by io-wq, because of IOSQE_ASYNC, has the same
problem as poll, futex and waitid. io_wq_submit_work() looks at
IO_WQ_WORK_CANCEL once, before it issues the request. A cancel in between
flags the work, gets -EALREADY, and searches ->timeout_list in vain,
while the worker then adds the request and starts the timer:
io-wq worker io_try_cancel()
io_wq_submit_work()
IO_WQ_WORK_CANCEL isn't set
io_async_cancel_one()
sets IO_WQ_WORK_CANCEL
= -EALREADY
io_timeout_cancel() = -ENOENT
io_timeout()
adds the request to ->timeout_list
hrtimer_start()
= IOU_ISSUE_SKIP_COMPLETE
Nobody looks at the flag again, and the timeout runs its course. At ring
exit, io_uring_try_cancel_requests() cancels io-wq work and then kills
the timeouts the same way, so an exiting task can get stuck for the full
duration of such a timeout.
Look at the flag under ->timeout_lock, which the search takes as well,
and fail the request if it is set. If the search came first, the flag is
visible. If it comes later, it finds the request on the list.
Fixes: ce35a47a3a02 ("io_uring: add IOSQE_ASYNC")
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io_uring.h | 14 ++++++++++----
io_uring/timeout.c | 7 +++++++
2 files changed, 17 insertions(+), 4 deletions(-)
--- a/io_uring/io_uring.h
+++ b/io_uring/io_uring.h
@@ -419,16 +419,22 @@ static inline void io_ring_submit_lock(s
* cancellations find it, rather than completing it. Whoever tried to cancel
* the request before that found it running. It has flagged the work item,
* has searched the list in vain, and won't come back. To be called with
- * ->uring_lock held, which the search needs as well, once the request is on
- * the list: if the search came first, the flag is visible.
+ * the lock the search takes held: if the search came first, the flag is
+ * visible. For most lists that is ->uring_lock, timeouts use ->timeout_lock.
*/
+static inline bool __io_wq_cancel_seen(struct io_kiocb *req,
+ unsigned int issue_flags)
+{
+ return (issue_flags & IO_URING_F_IOWQ) &&
+ (atomic_read(&req->work.flags) & IO_WQ_WORK_CANCEL);
+}
+
static inline bool io_wq_cancel_seen(struct io_kiocb *req,
unsigned int issue_flags)
{
lockdep_assert_held(&req->ctx->uring_lock);
- return (issue_flags & IO_URING_F_IOWQ) &&
- (atomic_read(&req->work.flags) & IO_WQ_WORK_CANCEL);
+ return __io_wq_cancel_seen(req, issue_flags);
}
static inline void io_commit_cqring(struct io_ring_ctx *ctx)
--- a/io_uring/timeout.c
+++ b/io_uring/timeout.c
@@ -634,6 +634,13 @@ int io_timeout(struct io_kiocb *req, uns
raw_spin_lock_irq(&ctx->timeout_lock);
+ /* the list is searched under ->timeout_lock, see io_wq_cancel_seen() */
+ if (__io_wq_cancel_seen(req, issue_flags)) {
+ raw_spin_unlock_irq(&ctx->timeout_lock);
+ io_req_task_queue_fail(req, -ECANCELED);
+ return IOU_ISSUE_SKIP_COMPLETE;
+ }
+
/*
* sqe->off holds how many events that need to occur for this
* timeout event to be satisfied. If it isn't set, then this is
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 085/109] io_uring/poll: dont loop on HUP with multiple references
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (83 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 084/109] io_uring/timeout: dont lose a cancel as io-wq arms a timeout Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 086/109] io_uring/net: keep recv buffer select length within MAX_RW_COUNT Greg Kroah-Hartman
` (30 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Jens Axboe, Claude Fable 5.1
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jens Axboe <axboe@kernel.dk>
Commit cf1d5c9d335e2c673811da641531da82cb01393a upstream.
io_poll_check_events() drops down to one reference when a multishot
request sees HUP with more than one, so that the loop goes around once
more. It did so by subtracting one less than it had. With the cmpxchg
that only takes the local count down: the exchange fails, the next pass
reads the unchanged count, and goes through the same motions again. The
loop then only ends once io_poll_issue() stops the request.
Drop the extra references from the counter instead, and loop once more
without trying the exchange. The next pass sees one reference, like it
did before, and ends unless a wake up came in.
Fixes: 937d38dcacd6 ("io_uring/poll: avoid retry wakeup loss when dropping ownership")
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/poll.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
--- a/io_uring/poll.c
+++ b/io_uring/poll.c
@@ -223,12 +223,14 @@ static inline void io_poll_execute(struc
*/
static int io_poll_check_events(struct io_kiocb *req, io_tw_token_t tw)
{
+ bool again;
int v;
if (unlikely(tw.cancel))
return -ECANCELED;
do {
+ again = false;
v = atomic_read(&req->poll_refs);
if (unlikely(v != 1)) {
@@ -290,9 +292,15 @@ static int io_poll_check_events(struct i
} else {
int ret;
- /* multiple refs and HUP, ensure we loop once more */
- if ((req->cqe.res & (POLLHUP | POLLRDHUP)) && v != 1)
- v--;
+ /*
+ * Multiple refs and HUP, ensure we loop once more. Drop
+ * down to one ref now, so the next pass ends unless a
+ * wake up came in.
+ */
+ if ((req->cqe.res & (POLLHUP | POLLRDHUP)) && v != 1) {
+ atomic_sub(v - 1, &req->poll_refs);
+ again = true;
+ }
ret = io_poll_issue(req, tw);
if (ret == IOU_COMPLETE)
@@ -311,7 +319,7 @@ static int io_poll_check_events(struct i
* task_work while we were executing it.
*/
v &= IO_POLL_REF_MASK;
- } while (atomic_cmpxchg(&req->poll_refs, v, 0) != v);
+ } while (again || atomic_cmpxchg(&req->poll_refs, v, 0) != v);
io_napi_add(req);
return IOU_POLL_NO_ACTION;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 086/109] io_uring/net: keep recv buffer select length within MAX_RW_COUNT
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (84 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 085/109] io_uring/poll: dont loop on HUP with multiple references Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 087/109] io_uring/wait: wake real-time waiters from hard interrupt context Greg Kroah-Hartman
` (29 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Tamaki Yanagawa, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tamaki Yanagawa <ty@000ty.net>
Commit 96262cfab554cf11412de7a6247f0e82c94e130f upstream.
Since commit dcbd1c054848 ("io_uring/net: let io_recv_buf_select return
the length of the buffer region"), io_recv_buf_select() returns the
selected length as an int. For a non-bundle recv with sqe->len == 0 the
length comes from the ring provided buffer, which can be up to 4G, so a
buffer of 2G or more makes io_recv() fail the request with a bogus
negative result (e.g. -2147483648) instead of receiving into it. Clamp
the length to MAX_RW_COUNT, which import_ubuf() limits the transfer to
anyway.
Fixes: dcbd1c054848 ("io_uring/net: let io_recv_buf_select return the length of the buffer region")
Cc: stable@vger.kernel.org
Signed-off-by: Tamaki Yanagawa <ty@000ty.net>
Assisted-by: Claude:claude-opus-5-5
Link: https://patch.msgid.link/20261018042227.1105-1-ty@000ty.net
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/net.c | 2 ++
1 file changed, 2 insertions(+)
--- a/io_uring/net.c
+++ b/io_uring/net.c
@@ -1195,6 +1195,8 @@ static int io_recv_buf_select(struct io_
*sel = io_buffer_select(req, &len, sr->buf_group, issue_flags);
if (!sel->addr)
return -ENOBUFS;
+ /* ring provided buffers can be up to 4G, keep len an int */
+ len = min_t(size_t, len, MAX_RW_COUNT);
sr->buf = sel->addr;
sr->len = len;
map_ubuf:
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 087/109] io_uring/wait: wake real-time waiters from hard interrupt context
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (85 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 086/109] io_uring/net: keep recv buffer select length within MAX_RW_COUNT Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 088/109] io_uring/waitid: prevent cancel reference wrap Greg Kroah-Hartman
` (28 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Austin Schuh, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Austin Schuh <austin.linux@gmail.com>
Commit 5b01d5955ac206e38097f777eff6ab2a8b948c41 upstream.
Since commit cebf123c634a ("io_uring: implement our own schedule timeout
handling"), a CQ wait with a timeout sleeps on io_uring's own hrtimer
instead of an hrtimer sleeper. On PREEMPT_RT, an hrtimer that isn't
marked for hard interrupt expiry expires in the per-CPU ktimers thread,
which runs at SCHED_FIFO 1. A real-time task waiting in io_uring_enter()
is therefore not woken at its deadline while any other real-time task is
runnable on its CPU: the wakeup waits until every one of them has blocked.
hrtimer sleepers, which schedule_hrtimeout_range_clock() used to give
io_uring, don't have this problem: __hrtimer_setup_sleeper() marks the
timer hard when the sleeping task is in a real-time or deadline class.
Both of io_uring's callbacks are fine in hard interrupt context:
io_cqring_timer_wakeup() sets a flag and calls wake_up_process(), and
io_cqring_min_timer_wakeup() only reads ring state, under RCU, before
doing the same or re-arming the timer. Apply the sleeper rule to them.
Measured on a Jetson Orin Nano running 6.18.39-rt with this change
backported: a SCHED_FIFO 40 task waits in io_uring_enter() for 40,000
deadlines 500us apart, while a SCHED_FIFO 20 task runs bursts of up to
1ms on the same CPU. Before, 6,647 wakeups were more than 100us late,
the latest by 1,040us, and 2,758 deadlines passed without a wakeup at
all. After, none were more than 100us late and the latest was 18us.
This mirrors epoll: epoll_pwait2() waits on an hrtimer sleeper, so its
timeout is already hard for real-time tasks, and on the same setup it
was never more than 17us late.
[ 6.18: the wait code is still in io_uring.c ]
Fixes: cebf123c634a ("io_uring: implement our own schedule timeout handling")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
Link: https://patch.msgid.link/20261017171726.601443-1-austin.linux@gmail.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io_uring.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -48,6 +48,7 @@
#include <linux/uio.h>
#include <linux/bits.h>
+#include <linux/sched/rt.h>
#include <linux/sched/signal.h>
#include <linux/fs.h>
#include <linux/file.h>
@@ -2646,23 +2647,32 @@ out_wake:
return io_cqring_timer_wakeup(timer);
}
+/* Same policy as for hrtimer sleepers, see __hrtimer_setup_sleeper(). */
+static enum hrtimer_mode io_cqring_timer_mode(void)
+{
+ if (IS_ENABLED(CONFIG_PREEMPT_RT) && rt_or_dl_task_policy(current))
+ return HRTIMER_MODE_ABS_HARD;
+ return HRTIMER_MODE_ABS;
+}
+
static int io_cqring_schedule_timeout(struct io_wait_queue *iowq,
clockid_t clock_id, ktime_t start_time)
{
+ enum hrtimer_mode mode = io_cqring_timer_mode();
ktime_t timeout;
if (iowq->min_timeout) {
timeout = ktime_add_ns(iowq->min_timeout, start_time);
hrtimer_setup_on_stack(&iowq->t, io_cqring_min_timer_wakeup, clock_id,
- HRTIMER_MODE_ABS);
+ mode);
} else {
timeout = iowq->timeout;
hrtimer_setup_on_stack(&iowq->t, io_cqring_timer_wakeup, clock_id,
- HRTIMER_MODE_ABS);
+ mode);
}
hrtimer_set_expires_range_ns(&iowq->t, timeout, 0);
- hrtimer_start_expires(&iowq->t, HRTIMER_MODE_ABS);
+ hrtimer_start_expires(&iowq->t, mode);
if (!READ_ONCE(iowq->hit_timeout))
schedule();
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 088/109] io_uring/waitid: prevent cancel reference wrap
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (86 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 087/109] io_uring/wait: wake real-time waiters from hard interrupt context Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 089/109] io_uring: end the drain when the drain request completes Greg Kroah-Hartman
` (27 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Junyeong Lee, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junyeong Lee <jylee5498@gmail.com>
Commit 98787d3fa4e99564d2b2bc21255b21f670c450d0 upstream.
io_waitid packs the cancellation state into bit 31 of ->refs and uses
bits 30:0 as a 31-bit reference counter:
#define IO_WAITID_CANCEL_FLAG BIT(31)
#define IO_WAITID_REF_MASK GENMASK(30, 0)
__io_waitid_cancel() marks a request canceled, then increments the low
bits of ->refs to claim completion ownership. When another path owns the
request, the increment remains even though cancellation returns false.
The request remains discoverable by cancellation, so repeated failed
attempts can wrap the 31-bit reference field to zero. A later
cancellation can claim ownership and complete the request a second time.
Use atomic_fetch_or() when setting the cancel flag and inspect the old
flag value. The first cancel attempt continues to the existing ownership
increment, preserving its wake and issuer handoff semantics. Later
attempts see the cancel flag and return without incrementing the
reference field.
This makes repeated failed cancellation attempts idempotent while
leaving the first-cancel state transitions unchanged.
Fixes: f31ecf671ddc ("io_uring: add IORING_OP_WAITID support")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Junyeong Lee <jylee5498@gmail.com>
Link: https://patch.msgid.link/20261019152100.63565-1-jylee5498@gmail.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/waitid.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
--- a/io_uring/waitid.c
+++ b/io_uring/waitid.c
@@ -154,9 +154,12 @@ static bool __io_waitid_cancel(struct io
/*
* Mark us canceled regardless of ownership. This will prevent a
- * potential retry from a spurious wakeup.
+ * potential retry from a spurious wakeup. Only the first cancel
+ * attempt should take a reference for the ownership handoff.
*/
- atomic_or(IO_WAITID_CANCEL_FLAG, &iw->refs);
+ if (atomic_fetch_or(IO_WAITID_CANCEL_FLAG, &iw->refs) &
+ IO_WAITID_CANCEL_FLAG)
+ return false;
/* claim ownership */
if (atomic_fetch_inc(&iw->refs) & IO_WAITID_REF_MASK)
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 089/109] io_uring: end the drain when the drain request completes
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (87 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 088/109] io_uring/waitid: prevent cancel reference wrap Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 090/109] io_uring: keep the drain active when a deferred drain request is started Greg Kroah-Hartman
` (26 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Wei Gao, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jens Axboe <axboe@kernel.dk>
Commit ecb6817b769fd7d71af2c9f446188649020b2e53 upstream.
Since requests are drained based on allocated requests, the drain stays
active after the drain request has posted its CQE, until it is freed and
a new request finds the defer list empty. A drain request run by io-wq
is freed only after the worker drops its reference, so a request
submitted after the drain completed can still be deferred behind it,
and get issued much later than the application expects.
A drain request dispatched from the defer list runs alone. Track it,
and end the drain when its CQE is flushed and nothing else is deferred.
Have io-wq complete drain requests through task_work, so that this
happens under ->uring_lock before the CQE is visible. Linked drain
requests keep waiting until the link is freed.
Reported-by: Wei Gao <wegao@suse.com>
Link: https://lore.kernel.org/io-uring/20261018070154.16944-1-wegao@suse.com/
[ 6.18: ->drain_active and ->lockless_cq are bools here ]
Fixes: 8fb7aee05591 ("io_uring: drain based on allocates reqs")
Cc: stable@vger.kernel.org
Tested-by: Wei Gao <wegao@suse.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
include/linux/io_uring_types.h | 2 +
io_uring/io_uring.c | 42 ++++++++++++++++++++++++++++++++++++++---
2 files changed, 41 insertions(+), 3 deletions(-)
--- a/include/linux/io_uring_types.h
+++ b/include/linux/io_uring_types.h
@@ -450,6 +450,8 @@ struct io_ring_ctx {
atomic_t poll_wq_rcu_pending;
struct list_head defer_list;
unsigned nr_drained;
+ /* drain request dispatched from ->defer_list, until it completes */
+ struct io_kiocb *drain_req;
/* protected by ->completion_lock */
unsigned nr_req_allocated;
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -551,6 +551,9 @@ static __cold noinline void io_queue_def
lockdep_assert_held(&ctx->uring_lock);
__io_req_caches_free(ctx);
+ /* nothing outside of ->defer_list is alive */
+ if (ctx->nr_req_allocated == ctx->nr_drained)
+ ctx->drain_req = NULL;
while (!list_empty(&ctx->defer_list)) {
struct io_defer_entry *de = list_first_entry(&ctx->defer_list,
@@ -562,6 +565,9 @@ static __cold noinline void io_queue_def
list_del_init(&de->list);
ctx->nr_drained -= io_linked_nr(de->req);
+ /* it runs alone, track its completion unless it's a link */
+ if ((de->req->flags & REQ_F_IO_DRAIN) && !de->req->link)
+ ctx->drain_req = de->req;
io_req_task_queue(de->req);
kfree(de);
first = false;
@@ -1011,9 +1017,11 @@ static void io_req_complete_post(struct
/*
* Handle special CQ sync cases via task_work. DEFER_TASKRUN requires
- * the submitter task context, IOPOLL protects with uring_lock.
+ * the submitter task context, IOPOLL protects with uring_lock. A drain
+ * request ends the drain under uring_lock as its CQE is posted.
*/
- if (ctx->lockless_cq || (req->flags & REQ_F_REISSUE)) {
+ if (ctx->lockless_cq ||
+ (req->flags & (REQ_F_REISSUE | REQ_F_IO_DRAIN))) {
defer_complete:
req->io_task_work.func = io_req_task_complete;
io_req_task_work_add(req);
@@ -1612,11 +1620,33 @@ static void io_free_batch_list(struct io
} while (node);
}
+/*
+ * The dispatched drain request ran alone, and anything submitted after it was
+ * deferred. Once it completed, new requests may run if nothing is waiting.
+ */
+static __cold bool io_drain_req_completed(struct io_ring_ctx *ctx)
+{
+ struct io_wq_work_node *node;
+
+ __wq_list_for_each(node, &ctx->submit_state.compl_reqs) {
+ struct io_kiocb *req = container_of(node, struct io_kiocb,
+ comp_list);
+
+ /* a requeued request hasn't completed */
+ if (req == ctx->drain_req && !(req->flags & REQ_F_REISSUE)) {
+ ctx->drain_req = NULL;
+ return true;
+ }
+ }
+ return false;
+}
+
void __io_submit_flush_completions(struct io_ring_ctx *ctx)
__must_hold(&ctx->uring_lock)
{
struct io_submit_state *state = &ctx->submit_state;
struct io_wq_work_node *node;
+ bool drain_done = false;
__io_cq_lock(ctx);
__wq_list_for_each(node, &state->compl_reqs) {
@@ -1638,13 +1668,19 @@ void __io_submit_flush_completions(struc
}
__io_cq_unlock_post(ctx);
+ if (unlikely(ctx->drain_active) && ctx->drain_req)
+ drain_done = io_drain_req_completed(ctx);
+
if (!wq_list_empty(&state->compl_reqs)) {
io_free_batch_list(ctx, state->compl_reqs.first);
INIT_WQ_LIST(&state->compl_reqs);
}
- if (unlikely(ctx->drain_active))
+ if (unlikely(ctx->drain_active)) {
io_queue_deferred(ctx);
+ if (drain_done && list_empty(&ctx->defer_list))
+ ctx->drain_active = false;
+ }
ctx->submit_state.cq_flush = false;
}
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 090/109] io_uring: keep the drain active when a deferred drain request is started
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (88 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 089/109] io_uring: end the drain when the drain request completes Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 091/109] keys: Avoid the owner account dereference in named keyring lookup Greg Kroah-Hartman
` (25 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Karl Mehltretter, Jens Axboe
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jens Axboe <axboe@kernel.dk>
Commit 2295613cd3713beffc8665e956135a5d48dfc208 upstream.
ecb6817b769f ends the drain when the dispatched drain request has posted
its CQE and nothing else is deferred. But the deferred list is processed
in between, and if the request it starts is itself a drain request, the
drain is over before that one has even run. A request submitted after
that point then runs ahead of it:
1. P0 in flight, D1 and D2 (both IOSQE_IO_DRAIN) deferred behind it
2. P0 completes, D1 is started
3. D1 completes, drain_done is set, D2 is started, the list is empty
4. IO_RING_F_DRAIN_ACTIVE is cleared with D2 still in flight
Anything submitted now bypasses the drain. The same happens with D2 as
the head of a link, as those aren't tracked in ->drain_req at all.
Have io_queue_deferred() report whether it started a drain request, and
don't end the drain if it did. Starting non-drain requests and ending it
is still fine, they were only waiting on the drain request that just
completed.
[ 6.18: ->drain_active is a bool here ]
Fixes: ecb6817b769f ("io_uring: end the drain when the drain request completes")
Reported-by: Karl Mehltretter <kmehltretter@gmail.com>
Link: https://lore.kernel.org/io-uring/20261027043323.98916-1-kmehltretter@gmail.com/
Cc: stable@vger.kernel.org
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
io_uring/io_uring.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -545,9 +545,9 @@ unsigned io_linked_nr(struct io_kiocb *r
return nr;
}
-static __cold noinline void io_queue_deferred(struct io_ring_ctx *ctx)
+static __cold noinline bool io_queue_deferred(struct io_ring_ctx *ctx)
{
- bool drain_seen = false, first = true;
+ bool drain_seen = false, first = true, drain_queued = false;
lockdep_assert_held(&ctx->uring_lock);
__io_req_caches_free(ctx);
@@ -561,17 +561,21 @@ static __cold noinline void io_queue_def
drain_seen |= de->req->flags & REQ_F_IO_DRAIN;
if ((drain_seen || first) && ctx->nr_req_allocated != ctx->nr_drained)
- return;
+ return drain_queued;
list_del_init(&de->list);
ctx->nr_drained -= io_linked_nr(de->req);
- /* it runs alone, track its completion unless it's a link */
- if ((de->req->flags & REQ_F_IO_DRAIN) && !de->req->link)
- ctx->drain_req = de->req;
+ if (de->req->flags & REQ_F_IO_DRAIN) {
+ drain_queued = true;
+ /* it runs alone, track its completion unless it's a link */
+ if (!de->req->link)
+ ctx->drain_req = de->req;
+ }
io_req_task_queue(de->req);
kfree(de);
first = false;
}
+ return drain_queued;
}
void __io_commit_cqring_flush(struct io_ring_ctx *ctx)
@@ -1677,7 +1681,9 @@ void __io_submit_flush_completions(struc
}
if (unlikely(ctx->drain_active)) {
- io_queue_deferred(ctx);
+ /* a drain request started from the list keeps the drain going */
+ if (io_queue_deferred(ctx))
+ drain_done = false;
if (drain_done && list_empty(&ctx->defer_list))
ctx->drain_active = false;
}
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 091/109] keys: Avoid the owner account dereference in named keyring lookup
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (89 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 090/109] io_uring: keep the drain active when a deferred drain request is started Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 092/109] keys: Check the authorization payload in process keyring searches Greg Kroah-Hartman
` (24 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Cen Zhang, Jarkko Sakkinen
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang <zzzccc427@gmail.com>
commit 51409aaa01f8602552c336009fd22b226b0cdf1d upstream.
Named keyring lookup must keep the storage containing the owner UID
alive through the namespace mapping check. find_keyring_by_name()
reads keyring->user->uid under keyring_name_lock, but that lock protects
the keyring's name entry and allocation, not its separate key_user.
A named session-keyring join can overlap a privileged KEYCTL_CHOWN on
another CPU. If the keyring holds the last reference to its old dynamic
key_user and the ownership transfer succeeds, the following ordering is
possible:
Named join Chown
find_keyring_by_name() keyctl_chown_key()
read_lock(keyring_name_lock) down_write(key->sem)
load old keyring->user
replace key->user and key->uid
up_write(key->sem)
key_put(key)
key_user_put(old user): free
read old user->uid
read_unlock(keyring_name_lock)
Chown neither takes keyring_name_lock nor key_session_mutex, so it can
free the old account between the pointer load and the UID read. The
lookup then reads freed memory even though the keyring itself is alive.
Use keyring->uid for the mapping check. key_alloc() initializes this
inline owner UID and keyctl_chown_key() updates it on chown.
The existing name lock keeps its containing keyring allocated throughout
the check, so lookup no longer depends on the account's lifetime. This
also matches the owner UID used by the permission check.
KASAN report as below:
BUG: KASAN: slab-use-after-free in find_keyring_by_name+0x577/0x5d0
Read of size 4 at addr ffff8881128ae2ec by task keycase/500
Cc: stable@vger.kernel.org
Fixes: 2ea190d0a006 ("keys: skip keys from another user namespace")
Assisted-by: LLM
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Link: https://lore.kernel.org/r/pm-key-management-objects-candidate-0002-v3-2baa8cad2bb554364e10@gmail.com
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
security/keys/keyring.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -1158,7 +1158,7 @@ struct key *find_keyring_by_name(const c
* grants Search permission and that hasn't been revoked
*/
list_for_each_entry(keyring, &ns->keyring_name_list, name_link) {
- if (!kuid_has_mapping(ns, keyring->user->uid))
+ if (!kuid_has_mapping(ns, keyring->uid))
continue;
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 092/109] keys: Check the authorization payload in process keyring searches
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (90 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 091/109] keys: Avoid the owner account dereference in named keyring lookup Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 093/109] keys: Protect the type name while describing a key Greg Kroah-Hartman
` (23 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Cen Zhang, Jarkko Sakkinen
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang <zzzccc427@gmail.com>
commit 0a0ec45bf577a9b4d4960567f20d3877ef480de9 upstream.
An authorization payload and its saved credentials must remain valid
while search_process_keyrings_rcu() searches the requestor's keyrings.
The function validates the authorization key, then separately loads
payload.data[0] and unconditionally reads rka->cred. Validation does
not prevent request_key_auth_revoke() from clearing the payload.
A request-key helper can assume authority and fork a descendant that
inherits the authorization key. If the descendant misses in its own
keyrings, it falls back to the saved requestor credentials. Successful
target instantiation invalidates the authorization key without clearing
the payload. After the helper exits, UMH_WAIT_PROC returns to the
requester, which calls complete_request_key() and revokes that key.
With the descendant paused after validation, the paths can interleave
as follows:
1. The descendant enters search_process_keyrings_rcu() under RCU and
key_validate() succeeds.
2. The helper instantiates the target, invalidates the authorization
key, and exits.
3. The requester resumes from UMH_WAIT_PROC, calls
complete_request_key(), and revokes the authorization key.
request_key_auth_revoke() sets its payload pointer to NULL.
4. The descendant loads NULL and dereferences rka->cred.
This NULL dereference can oops the kernel. RCU delays payload disposal
but does not prevent the pointer from being cleared.
Load the payload with dereference_key_rcu() and require a non-NULL
result before validating the authorization key and searching its saved
credentials. A non-NULL payload and its credentials remain alive for
the caller's RCU read-side section; a cleared payload skips the fallback
search and follows the existing error selection.
KASAN report as below:
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000005: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000028-0x000000000000002f]
RIP: 0010:search_process_keyrings_rcu+0x32b/0x790
Cc: stable@vger.kernel.org
Fixes: e59428f721ee ("keys: Move the RCU locks outwards from the keyring search functions")
Assisted-by: LLM
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Link: https://lore.kernel.org/r/pm-key-management-objects-candidate-0004-v3-598361863b76bc25d58d@gmail.com
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
security/keys/process_keys.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -556,9 +556,8 @@ key_ref_t search_process_keyrings_rcu(st
) {
const struct cred *cred = ctx->cred;
- if (key_validate(cred->request_key_auth) == 0) {
- rka = ctx->cred->request_key_auth->payload.data[0];
-
+ rka = dereference_key_rcu(cred->request_key_auth);
+ if (rka && key_validate(cred->request_key_auth) == 0) {
//// was search_process_keyrings() [ie. recursive]
ctx->cred = rka->cred;
key_ref = search_cred_keyrings_rcu(ctx);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 093/109] keys: Protect the type name while describing a key
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (91 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 092/109] keys: Check the authorization payload in process keyring searches Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 094/109] keys: make keyring indexes independent of kernel addresses Greg Kroah-Hartman
` (22 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Cen Zhang, Jarkko Sakkinen
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang <zzzccc427@gmail.com>
commit fced7238b7e80cf8aa688d5af976e4aa6e33c329 upstream.
keyctl_describe_key() must keep the key type's name alive until
kasprintf() has consumed it. The retained key reference keeps the key
and its description alive, but does not pin the module providing its
type. The name is passed to kasprintf() without holding key->sem.
kvasprintf() uses the saved name pointer in two formatting passes, with
a GFP_KERNEL allocation between them. With AF_RXRPC=m, a task that
passes View permission before type retirement can overlap a privileged
module unload in this order:
1. KEYCTL_DESCRIBE looks up the key, saves key->type->name in the
kasprintf() arguments and completes the first formatting pass.
2. While the formatter is delayed before its second pass,
af_rxrpc_exit() calls unregister_key_type(), which removes the type
from the registry and waits for key_gc_keytype().
3. The collector takes key->sem for writing, retypes the retained key
to key_type_dead and completes retirement. Unregistration and module
exit return, allowing free_module() to release the type and name.
4. The formatter resumes its second pass and reads the saved name from
released module storage, which can fault in string().
Hold key->sem for reading across kasprintf(). The collector then cannot
retype the key or complete retirement until both formatting passes have
finished. If retirement wins the lock first, formatting uses the core
key_type_dead name instead. Release the semaphore immediately after
kasprintf(), including on allocation failure, before copying to
userspace.
Oops report as below:
BUG: unable to handle page fault for address: fffffbfff8054794
#PF: supervisor read access in kernel mode
#PF: error_code(0x0000) - not-present page
RIP: 0010:string+0x23f/0x470
Call Trace:
vsnprintf+0x330/0x1110
Cc: stable@vger.kernel.org
Fixes: aa9d4437893f ("KEYS: Fix the size of the key description passed to/from userspace")
Assisted-by: LLM
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Link: https://lore.kernel.org/r/pm-key-management-objects-candidate-0009-v4-6b537c5732fd957c33fa@gmail.com
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
security/keys/keyctl.c | 2 ++
1 file changed, 2 insertions(+)
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -677,12 +677,14 @@ okay:
/* calculate how much information we're going to return */
ret = -ENOMEM;
+ down_read(&key->sem);
infobuf = kasprintf(GFP_KERNEL,
"%s;%d;%d;%08x;",
key->type->name,
from_kuid_munged(current_user_ns(), key->uid),
from_kgid_munged(current_user_ns(), key->gid),
key->perm);
+ up_read(&key->sem);
if (!infobuf)
goto error2;
infolen = strlen(infobuf);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 094/109] keys: make keyring indexes independent of kernel addresses
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (92 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 093/109] keys: Protect the type name while describing a key Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 095/109] keys: Protect the read method check against type removal Greg Kroah-Hartman
` (21 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Kyle Zeng, Jarkko Sakkinen
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <kylebot@openai.com>
commit de891c73c1cf41ac8de12c07fa2c84d6a912ad0c upstream.
KEYCTL_READ returns the serial numbers in a keyring in associative-array
traversal order. The first word of the index is a public, deterministic
hash of the type pointer, domain pointer and description. An unprivileged
user can therefore insert keys with known descriptions, predict their
order for each possible KASLR slide, and recover the kernel image address.
On an x86-64 build with 16 MiB alignment, 45 user keys suffice to
distinguish the candidate slides.
Remove the addresses from the whole index, not just its first hash word.
Assign each key type and domain a nonzero 64-bit ID and hash those IDs and
the description with a once-initialized SipHash key. Use the full IDs in
the later index chunks as well. Truncating or hashing the IDs there would
lose the guarantee that distinct type/domain/description tuples have a
differing index bit.
Allocate IDs lazily since private key types need not be registered. Cache
them in the index key so tree walks and copies of an existing index do not
need to dereference a type descriptor that may have been unregistered.
Keep the 32-bit chunk layout and diff_objects() bit offsets consistent,
and retain the separate root slot for keyrings used by recursive search.
The key comparison, permissions and userspace data format are unchanged;
only the address-dependent in-memory ordering is replaced.
Cc: stable@vger.kernel.org
Fixes: b2a4df200d57 ("KEYS: Expand the capacity of a keyring")
Assisted-by: Codex:gpt-6-astra
Signed-off-by: Kyle Zeng <kylebot@openai.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Link: https://lore.kernel.org/r/20261013231251.56170-1-kylebot@openai.com
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
include/linux/key-type.h | 1
include/linux/key.h | 5 +
security/keys/keyring.c | 150 +++++++++++++++++++++++++----------------------
3 files changed, 86 insertions(+), 70 deletions(-)
--- a/include/linux/key-type.h
+++ b/include/linux/key-type.h
@@ -158,6 +158,7 @@ struct key_type {
const void *in, const void *in2);
/* internal fields */
+ atomic64_t index_id; /* Opaque keyring index ID */
struct list_head link; /* link in types list */
struct lock_class_key lock_class; /* key->sem lock class */
} __randomize_layout;
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -107,6 +107,7 @@ struct keyring_name;
struct key_tag {
struct rcu_head rcu;
+ atomic64_t index_id; /* Opaque keyring index ID */
refcount_t usage;
bool removed; /* T when subject removed */
};
@@ -129,6 +130,8 @@ struct keyring_index_key {
struct key_type *type;
struct key_tag *domain_tag; /* Domain of operation */
const char *description;
+ u64 type_id; /* Cached index IDs */
+ u64 domain_id;
};
union key_payload {
@@ -251,6 +254,8 @@ struct key {
struct key_type *type; /* type of key */
struct key_tag *domain_tag; /* Domain of operation */
char *description;
+ u64 type_id;
+ u64 domain_id;
};
};
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -14,6 +14,9 @@
#include <linux/err.h>
#include <linux/user_namespace.h>
#include <linux/nsproxy.h>
+#include <linux/once.h>
+#include <linux/random.h>
+#include <linux/siphash.h>
#include <keys/keyring-type.h>
#include <keys/user-type.h>
#include <linux/assoc_array_priv.h>
@@ -147,49 +150,46 @@ static int keyring_instantiate(struct ke
}
/*
- * Multiply 64-bits by 32-bits to 96-bits and fold back to 64-bit. Ideally we'd
- * fold the carry back too, but that requires inline asm.
+ * Give key types and domains address-independent identities. Zero means that
+ * an ID has not yet been allocated. As with inode sequence numbers used by
+ * futexes, this relies on the 64-bit counter not wrapping in a machine's
+ * lifetime.
*/
-static u64 mult_64x32_and_fold(u64 x, u32 y)
+static u64 key_index_id(atomic64_t *id)
{
- u64 hi = (u64)(u32)(x >> 32) * y;
- u64 lo = (u64)(u32)(x) * y;
- return lo + ((u64)(u32)hi << 32) + (u32)(hi >> 32);
+ static atomic64_t next_id;
+ u64 old, new;
+
+ old = atomic64_read(id);
+ if (likely(old))
+ return old;
+
+ do {
+ new = atomic64_inc_return(&next_id);
+ } while (WARN_ON_ONCE(!new));
+
+ old = 0;
+ if (!atomic64_try_cmpxchg_relaxed(id, &old, new))
+ return old;
+ return new;
}
/*
- * Hash a key type and description.
+ * Hash a key type, domain and description. KEYCTL_READ exposes the tree's
+ * traversal order, so no part of the index may contain kernel addresses.
*/
static void hash_key_type_and_desc(struct keyring_index_key *index_key)
{
const unsigned level_shift = ASSOC_ARRAY_LEVEL_STEP;
const unsigned long fan_mask = ASSOC_ARRAY_FAN_MASK;
- const char *description = index_key->description;
- unsigned long hash, type;
- u32 piece;
+ static siphash_key_t hash_key __read_mostly;
+ unsigned long hash;
u64 acc;
- int n, desc_len = index_key->desc_len;
- type = (unsigned long)index_key->type;
- acc = mult_64x32_and_fold(type, desc_len + 13);
- acc = mult_64x32_and_fold(acc, 9207);
- piece = (unsigned long)index_key->domain_tag;
- acc = mult_64x32_and_fold(acc, piece);
- acc = mult_64x32_and_fold(acc, 9207);
-
- for (;;) {
- n = desc_len;
- if (n <= 0)
- break;
- if (n > 4)
- n = 4;
- piece = 0;
- memcpy(&piece, description, n);
- description += n;
- desc_len -= n;
- acc = mult_64x32_and_fold(acc, piece);
- acc = mult_64x32_and_fold(acc, 9207);
- }
+ get_random_once(&hash_key, sizeof(hash_key));
+ acc = siphash(index_key->description, index_key->desc_len, &hash_key);
+ acc = siphash_3u64(index_key->type_id, index_key->domain_id, acc,
+ &hash_key);
/* Fold the hash down to 32 bits if need be. */
hash = acc;
@@ -209,7 +209,7 @@ static void hash_key_type_and_desc(struc
/*
* Finalise an index key to include a part of the description actually in the
- * index key, to set the domain tag and to calculate the hash.
+ * index key, to set the domain tag and index IDs, and to calculate the hash.
*/
void key_set_index_key(struct keyring_index_key *index_key)
{
@@ -225,6 +225,14 @@ void key_set_index_key(struct keyring_in
index_key->domain_tag = &default_domain_tag;
}
+ /* Preserve the IDs when copying an existing key's index. In
+ * particular, its type may already have been unregistered.
+ */
+ if (!index_key->type_id)
+ index_key->type_id = key_index_id(&index_key->type->index_id);
+ if (!index_key->domain_id)
+ index_key->domain_id = key_index_id(&index_key->domain_tag->index_id);
+
hash_key_type_and_desc(index_key);
}
@@ -263,15 +271,19 @@ void key_remove_domain(struct key_tag *d
/*
* Build the next index key chunk.
*
+ * The hash and inline description are followed by the 64-bit type and domain
+ * IDs, low word first, and then by the rest of the description.
* We return it one word-sized chunk at a time.
*/
static unsigned long keyring_get_key_chunk(const void *data, int level)
{
+ const unsigned int id_chunks = sizeof(u64) / sizeof(unsigned long);
const struct keyring_index_key *index_key = data;
+ int desc_len = index_key->desc_len;
unsigned long chunk = 0;
- const u8 *d;
- int desc_len = index_key->desc_len, n = sizeof(chunk);
+ int n = sizeof(chunk);
unsigned int offset;
+ const u8 *d;
level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
switch (level) {
@@ -279,27 +291,30 @@ static unsigned long keyring_get_key_chu
return index_key->hash;
case 1:
return index_key->x;
- case 2:
- return (unsigned long)index_key->type;
- case 3:
- return (unsigned long)index_key->domain_tag;
- default:
- level -= 4;
- offset = sizeof(index_key->desc) + level * sizeof(long);
- if (desc_len <= offset)
- return 0;
-
- d = index_key->description + offset;
- desc_len -= offset;
- if (desc_len > n)
- desc_len = n;
- d += desc_len;
- do {
- chunk <<= 8;
- chunk |= *--d;
- } while (--desc_len > 0);
- return chunk;
}
+
+ level -= 2;
+ if (level < id_chunks)
+ return index_key->type_id >> (level * ASSOC_ARRAY_KEY_CHUNK_SIZE);
+ level -= id_chunks;
+ if (level < id_chunks)
+ return index_key->domain_id >> (level * ASSOC_ARRAY_KEY_CHUNK_SIZE);
+ level -= id_chunks;
+
+ offset = sizeof(index_key->desc) + level * sizeof(long);
+ if (desc_len <= offset)
+ return 0;
+
+ d = index_key->description + offset;
+ desc_len -= offset;
+ if (desc_len > n)
+ desc_len = n;
+ d += desc_len;
+ do {
+ chunk <<= 8;
+ chunk |= *--d;
+ } while (--desc_len > 0);
+ return chunk;
}
static unsigned long keyring_get_object_key_chunk(const void *object, int level)
@@ -331,6 +346,7 @@ static int keyring_diff_objects(const vo
const struct keyring_index_key *b = data;
unsigned long seg_a, seg_b;
int level, i;
+ u64 diff;
level = 0;
seg_a = a->hash;
@@ -339,28 +355,22 @@ static int keyring_diff_objects(const vo
goto differ;
level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8;
- /* The number of bits contributed by the hash is controlled by a
- * constant in the assoc_array headers. Everything else thereafter we
- * can deal with as being machine word-size dependent.
- */
+ /* The inline description contributes one machine-word-sized chunk. */
seg_a = a->x;
seg_b = b->x;
if ((seg_a ^ seg_b) != 0)
goto differ;
level += sizeof(unsigned long);
- /* The next bit may not work on big endian */
- seg_a = (unsigned long)a->type;
- seg_b = (unsigned long)b->type;
- if ((seg_a ^ seg_b) != 0)
- goto differ;
- level += sizeof(unsigned long);
-
- seg_a = (unsigned long)a->domain_tag;
- seg_b = (unsigned long)b->domain_tag;
- if ((seg_a ^ seg_b) != 0)
- goto differ;
- level += sizeof(unsigned long);
+ diff = a->type_id ^ b->type_id;
+ if (diff)
+ return level * 8 + __ffs64(diff);
+ level += sizeof(u64);
+
+ diff = a->domain_id ^ b->domain_id;
+ if (diff)
+ return level * 8 + __ffs64(diff);
+ level += sizeof(u64);
i = sizeof(a->desc);
if (a->desc_len <= i)
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 095/109] keys: Protect the read method check against type removal
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (93 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 094/109] keys: make keyring indexes independent of kernel addresses Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 096/109] KEYS: Fix key_reject_and_link() race with keyring restriction Greg Kroah-Hartman
` (20 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Jarkko Sakkinen, Cen Zhang,
David Howells
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang <zzzccc427@gmail.com>
commit 925c84e3cc21caf09de92d9bcafab0147790debf upstream.
keyctl_read_key() checks key->type->read without holding key->sem.
A reference keeps the key alive, but does not pin the module containing
its type. A surviving RxRPC key can therefore be read while AF_RXRPC
is being unloaded:
KEYCTL_READ task Module unload and key GC
---------------- ------------------------
lookup_user_key()
permission / possession checks
load key->type
af_rxrpc_exit()
unregister_key_type()
GC: down_write(key->sem)
GC: retype to key_type_dead
GC: up_write(key->sem)
unregister_key_type() returns
free_module()
read saved type's read member
The final access can read freed module storage. The read invocation in
__keyctl_read_key() is already protected by key->sem, but the earlier
method check is outside that critical section.
Move the method check into __keyctl_read_key(), after key_validate() and
under its existing read hold on key->sem. The check and invocation then
use the same protected type for length queries and every read retry.
The GC cannot retype the key until the semaphore is released, and type
unregistration waits for that GC pass before the module can be freed.
Return -EOPNOTSUPP when validation succeeds and the type has no read
method. A temporary buffer may now be allocated before this result;
allocation and copying to userspace remain outside the semaphore.
Assisted-by: LLM
Cc: stable@vger.kernel.org # v5.10+
Fixes: d3ec10aa9581 ("KEYS: Don't write out to userspace while holding key semaphore")
Suggested-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Reviewed-by: David Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/pm-key-management-objects-candidate-0010-v7-fc85721ff5cfb9e33ac6@gmail.com
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
security/keys/keyctl.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -808,8 +808,12 @@ static long __keyctl_read_key(struct key
down_read(&key->sem);
ret = key_validate(key);
- if (ret == 0)
- ret = key->type->read(key, buffer, buflen);
+ if (ret == 0) {
+ if (key->type->read)
+ ret = key->type->read(key, buffer, buflen);
+ else
+ ret = -EOPNOTSUPP;
+ }
up_read(&key->sem);
return ret;
}
@@ -863,11 +867,6 @@ long keyctl_read_key(key_serial_t keyid,
/* the key is probably readable - now try to read it */
can_read_key:
- if (!key->type->read) {
- ret = -EOPNOTSUPP;
- goto key_put_out;
- }
-
if (!buffer || !buflen) {
/* Get the key length from the read method */
ret = __keyctl_read_key(key, NULL, 0);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 096/109] KEYS: Fix key_reject_and_link() race with keyring restriction
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (94 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 095/109] keys: Protect the read method check against type removal Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 097/109] KEYS: trusted: Fix blob allocation size in tpm2_key_decode() Greg Kroah-Hartman
` (19 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Jarkko Sakkinen, Adrian Martinez
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Adrian Martinez <adrimg3196@gmail.com>
commit 0b157166e612b70f98a332b6c7be7eac6f8e9593 upstream.
The restrict_link check in key_reject_and_link() is done before taking
the keyring semaphore, racing with a concurrent keyring_restrict()
that installs the restriction while holding the semaphore in write
mode.
Move the check to after __key_link_lock(), so it is done under the
semaphore, matching the pattern used in __key_create_or_update().
Cc: stable@vger.kernel.org
Fixes: 5ac7eace2d00 ("KEYS: Add a facility to restrict new links into a keyring")
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Adrian Martinez <adrimg3196@gmail.com>
Link: https://lore.kernel.org/r/CACO1+RT+m==BY+ZPkE+fo536D0qu3wog4xE1dfuXQ56g+=Qv=A@mail.gmail.com
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
security/keys/key.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -588,11 +588,18 @@ int key_reject_and_link(struct key *key,
ret = -EBUSY;
if (keyring) {
- if (keyring->restrict_link)
- return -EPERM;
-
link_ret = __key_link_lock(keyring, &key->index_key);
if (link_ret == 0) {
+ /*
+ * Check the restriction under the keyring semaphore.
+ * keyring_restrict() installs it while holding the
+ * semaphore, so testing it beforehand races with a
+ * concurrent restriction install.
+ */
+ if (keyring->restrict_link) {
+ __key_link_end(keyring, &key->index_key, edit);
+ return -EPERM;
+ }
link_ret = __key_link_begin(keyring, &key->index_key, &edit);
if (link_ret < 0)
__key_link_end(keyring, &key->index_key, edit);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 097/109] KEYS: trusted: Fix blob allocation size in tpm2_key_decode()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (95 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 096/109] KEYS: Fix key_reject_and_link() race with keyring restriction Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 098/109] keys/trusted_keys: return immediately after TPM unseal failure Greg Kroah-Hartman
` (18 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Stefano Garzarella, Jarkko Sakkinen
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefano Garzarella <sgarzare@redhat.com>
commit 11077cbcc2695f71c944a84a5f08c6672ab759f1 upstream.
tpm2_key_decode() allocates 4 bytes more than needed. The ASN.1
callbacks tpm2_key_priv() and tpm2_key_pub() provide the lengths
of TPM2B_PRIVATE and TPM2B_PUBLIC, so ctx.priv_len and ctx.pub_len
already account for the 2-byte `size` field each of those structures
starts with.
I noticed this while reviewing commit 114f00d738f1 ("KEYS: trusted:
Fix tpm2_load_cmd() boundary check"), which correctly reports
ctx.priv_len + ctx.pub_len as the decoded blob size [1].
Let's allocate exactly that amount, matching the data copied into
the blob.
[1] https://lore.kernel.org/linux-integrity/apfoKo-BdwaLXtkT@sgarzare-redhat/
Cc: stable@vger.kernel.org # v5.13+
Fixes: f2219745250f ("security: keys: trusted: use ASN.1 TPM2 key format for the blobs")
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
Link: https://lore.kernel.org/r/20260923173506.41519-1-sgarzare@redhat.com
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
security/keys/trusted-keys/trusted_tpm2.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -124,7 +124,7 @@ static int tpm2_key_decode(struct truste
if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
return -EINVAL;
- blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL);
+ blob = kmalloc(ctx.priv_len + ctx.pub_len, GFP_KERNEL);
if (!blob)
return -ENOMEM;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 098/109] keys/trusted_keys: return immediately after TPM unseal failure
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (96 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 097/109] KEYS: trusted: Fix blob allocation size in tpm2_key_decode() Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 099/109] asymmetric_keys: reject trust keys without IDs Greg Kroah-Hartman
` (17 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Srish Srinivasan, Jarkko Sakkinen
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srish Srinivasan <ssrish@linux.ibm.com>
commit f92d0ccb05bf9089072cb067dda0dffd5394c4ef upstream.
trusted_tpm_unseal() proceeds to pcrlock() when the TPM unseal operation
fails. If pcrlock() succeeds, its return value overwrites the unseal error,
causing key instantiation to succeed.
Return immediately when unseal fails to preserve the original error.
Cc: stable@vger.kernel.org # v5.15+
Fixes: 5d0682be3189 ("KEYS: trusted: Add generic trusted keys framework")
Signed-off-by: Srish Srinivasan <ssrish@linux.ibm.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Link: https://lore.kernel.org/r/20260912062950.279104-2-ssrish@linux.ibm.com
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
security/keys/trusted-keys/trusted_tpm1.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -921,8 +921,10 @@ static int trusted_tpm_unseal(struct tru
ret = tpm2_unseal_trusted(chip, p, options);
else
ret = key_unseal(p, options);
- if (ret < 0)
+ if (ret < 0) {
pr_info("key_unseal failed (%d)\n", ret);
+ goto out;
+ }
if (options->pcrlock) {
ret = pcrlock(options->pcrlock);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 099/109] asymmetric_keys: reject trust keys without IDs
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (97 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 098/109] keys/trusted_keys: return immediately after TPM unseal failure Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 100/109] ftrace: Initialize modname and offset in print_rec() Greg Kroah-Hartman
` (16 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable; +Cc: Greg Kroah-Hartman, patches, Xiang Mei, Weiming Shi, Herbert Xu
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <bestswngs@gmail.com>
commit 9695df3b1db4ac4534274d38ee16e3b9b0bf910e upstream.
The PKCS#8 parser deliberately leaves the asym_key_ids payload slot
empty. key_or_keyring_common() unconditionally dereferences that slot
when a PKCS#8 key is supplied to a key_or_keyring restriction. Both the
plain and :chain forms reach this branch, allowing an unprivileged caller
to trigger a NULL pointer dereference through KEYCTL_RESTRICT_KEYRING
followed by add_key().
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000
KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
RIP: 0010:key_or_keyring_common (crypto/asymmetric_keys/restrict.c:205 crypto/asymmetric_keys/restrict.c:279)
Call Trace:
<TASK>
__key_create_or_update (security/keys/key.c:884)
key_create_or_update (security/keys/key.c:1021)
__do_sys_add_key (security/keys/keyctl.c:134)
do_syscall_64 (arch/x86/entry/common.c:52 arch/x86/entry/common.c:83)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130)
</TASK>
Kernel panic - not syncing: Fatal exception
Reject an asymmetric restriction key without key IDs. A PKCS#8 private
key cannot identify the signer of a candidate certificate and is not a
valid trust anchor.
Fixes: 3c58b2362ba8 ("KEYS: Implement PKCS#8 RSA Private Key parser [ver #2]")
Cc: stable@vger.kernel.org
Reported-by: Xiang Mei <xmei5@asu.edu>
Link: https://lore.kernel.org/r/20260429181629.110802-2-bestswngs@gmail.com
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
crypto/asymmetric_keys/restrict.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
--- a/crypto/asymmetric_keys/restrict.c
+++ b/crypto/asymmetric_keys/restrict.c
@@ -240,10 +240,14 @@ static int key_or_keyring_common(struct
if (IS_ERR(key))
key = NULL;
} else if (trusted->type == &key_type_asymmetric) {
+ const struct asymmetric_key_ids *kids;
const struct asymmetric_key_id **signer_ids;
- signer_ids = (const struct asymmetric_key_id **)
- asymmetric_key_ids(trusted)->id;
+ kids = asymmetric_key_ids(trusted);
+ if (!kids)
+ return -ENOKEY;
+
+ signer_ids = (const struct asymmetric_key_id **)kids->id;
/*
* The auth_ids come from the candidate key (the
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 100/109] ftrace: Initialize modname and offset in print_rec()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (98 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 099/109] asymmetric_keys: reject trust keys without IDs Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 101/109] coredump: hold RCU while releasing parked threads Greg Kroah-Hartman
` (15 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Gavin Inglis, Aaron Ahmed,
Steven Rostedt
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aaron Ahmed <aarnahmd@amazon.com>
commit e1f339ff3b83857b93baf8b88186275a413d445e upstream.
print_rec() leaves 'modname' and 'offset' uninitialized and
kallsyms_lookup() only sets them on success. When the lookup fails like
when a module is being loaded or unloaded, 'modname' holds a garbage
value, passes the 'if (modname)' check, and is dereferenced by
seq_printf().
Oops: general protection fault
RIP: 0010:string+0x48/0xe0
Call Trace:
vsnprintf+0x1f5/0x650
seq_printf+0x8e/0xb0
print_rec+0x6c/0xc0
t_show+0x4c/0x440
seq_read_iter+0x119/0x480
This can be triggered by reading available_filter_functions while a
module is being loaded or unloaded.
Initialize both variables so a failed lookup skips the module name.
Cc: stable@vger.kernel.org
Fixes: b39181f7c690 ("ftrace: Add FTRACE_MCOUNT_MAX_OFFSET to avoid adding weak function")
Link: https://patch.msgid.link/20261016230403.4916-1-aarnahmd@amazon.com
Tested-by: Gavin Inglis <giinglis@amazon.com>
Signed-off-by: Aaron Ahmed <aarnahmd@amazon.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
kernel/trace/ftrace.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -4370,9 +4370,9 @@ subsys_initcall(ftrace_check_for_weak_fu
static int print_rec(struct seq_file *m, unsigned long ip)
{
- unsigned long offset;
+ unsigned long offset = 0;
char str[KSYM_SYMBOL_LEN];
- char *modname;
+ char *modname = NULL;
const char *ret;
ret = kallsyms_lookup(ip, NULL, &offset, &modname, str);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 101/109] coredump: hold RCU while releasing parked threads
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (99 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 100/109] ftrace: Initialize modname and offset in print_rec() Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 102/109] signal: only SIGKILL and the freezers interrupt a coredumping task Greg Kroah-Hartman
` (14 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Oleg Nesterov,
Christian Brauner (Amutable)
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <brauner@kernel.org>
commit 61d58591aa0188bf2463e263962256316ff8b576 upstream.
coredump_finish() releases the parked threads by clearing ->task in
their core_thread entry and calling wake_up_process() on each of them.
It does that without holding a reference on the task and without being
inside rcu.
But calling wake_up_process(task) without rcu here isn't safe. A parked
thread doesn't need that wakeup to leave. A spurious wakeup or a
preemption after the store is enough for the task to go away.
So if the coredump client is preempted between the store and
wake_up_process() the thread can exit and be freed in the meantime and
try_to_wake_up() takes pi_lock in freed memory.
Hold rcu across the loop.
Fixes: a94e2d408eae ("coredump: kill mm->core_done")
Cc: stable@vger.kernel.org
Acked-by: Oleg Nesterov <oleg@redhat.com>
Link: https://patch.msgid.link/20260921-work-coredump-fixes-v3-1-8e4adb1619e6@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
fs/coredump.c | 3 +++
1 file changed, 3 insertions(+)
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -560,6 +560,8 @@ static void coredump_finish(bool core_du
current->signal->core_state = NULL;
spin_unlock_irq(¤t->sighand->siglock);
+ /* A released thread may exit and be freed before it is woken. */
+ guard(rcu)();
while ((curr = next) != NULL) {
next = curr->next;
task = curr->task;
@@ -568,6 +570,7 @@ static void coredump_finish(bool core_du
* ->task == NULL before we read ->next.
*/
smp_mb();
+ /* Any wakeup now lets the thread exit, rcu keeps it alive. */
curr->task = NULL;
wake_up_process(task);
}
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 102/109] signal: only SIGKILL and the freezers interrupt a coredumping task
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (100 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 101/109] coredump: hold RCU while releasing parked threads Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 103/109] signal: dont retarget shared signals in a dying thread group Greg Kroah-Hartman
` (13 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Oleg Nesterov,
Christian Brauner (Amutable)
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <brauner@kernel.org>
commit 563d606b93d7a1d23512832fc252d32c68860d87 upstream.
For quite a while now coredump has allowed either SIGKILL or freezing to
interrupt an ongoing coredump. Since we seem to like it complicated
"freezing" can mean a lot of things:
(1) Power Management induced freezing
(2) cgroup v1 freezer controller induced freezing
(3) cgroup v2 freezer controller induced freezing
So (1) and (2) are handled by checking freezing() but (3) isn't.
(3) uses cgroup_freeze_task() which sets JOBCTL_TRAP_FREEZE and calls
signal_wake_up() on every task in the cgroup. The trap isn't handled by
the coredump code.
Which means the current logic is inconsistent. Power and cgroup v1
freeze and abort the coredump. With cgroup v2 it depends on where the
core goes. A dump to a file completes and the cgroup v2 freeze has to
wait for it to finish. When dumping to a pipe or socket the dump is
interrupted at the next write because of trap's TIF_SIGPENDING. Which is
it? Let's be consistent and align (1)-(3): freezing interrupts an
ongoing coredump and doesn't make it wait until the dump is done.
All three freezers go through signal_wake_up() and so set
TIF_SIGPENDING on the dumper. And once zap_threads() has set core_state
that flag can't mean anything else anymore. prepare_signal() drops every
signal but SIGKILL for the group, the dying threads don't retarget
shared signals to the dumper, and zap_process() cleared the pending job
control traps. So the flag is the thing to check. Let dump_interrupted()
test task_sigpending() and nothing else. That is also what the pipe and
socket writes already fail on. TIF_NOTIFY_SIGNAL is left out on
purpose, task work must not cut a dump short.
A seized tracee that gets PTRACE_INTERRUPT during the dump is cut short
the same way. It already was for pipes and sockets.
The one freeze that check can miss is one requested before the dump
started. zap_threads() clears TIF_SIGPENDING and the cgroup v2 freezer
only wakes a task once while its JOBCTL_TRAP_FREEZE isn't part of
JOBCTL_PENDING_MASK and survives zap_process(). So refuse to start the
dump when freezing() is true or the trap is pending. The task then exits
without dumping, which is what happens today when the freeze interrupts
the dump at the first write.
Fixes: 403bad72b67d ("coredump: only SIGKILL should interrupt the coredumping task")
Fixes: 76f969e8948d ("cgroup: cgroup v2 freezer")
Cc: stable@vger.kernel.org
Suggested-by: Oleg Nesterov <oleg@redhat.com>
Link: https://lore.kernel.org/r/arJ6zZuI59BJnXpl@redhat.com
Link: https://patch.msgid.link/20260921-work-coredump-fixes-v3-2-8e4adb1619e6@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
fs/coredump.c | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -506,7 +506,9 @@ static int zap_threads(struct task_struc
int nr = -EAGAIN;
spin_lock_irq(&tsk->sighand->siglock);
- if (!(signal->flags & SIGNAL_GROUP_EXIT) && !signal->group_exec_task) {
+ /* A freeze requested before the dump would be lost with TIF_SIGPENDING. */
+ if (!(signal->flags & SIGNAL_GROUP_EXIT) && !signal->group_exec_task &&
+ !freezing(tsk) && !(tsk->jobctl & JOBCTL_TRAP_FREEZE)) {
/* Allow SIGKILL, see prepare_signal() */
signal->core_state = core_state;
nr = zap_process(signal, exit_code);
@@ -578,13 +580,8 @@ static void coredump_finish(bool core_du
static bool dump_interrupted(void)
{
- /*
- * SIGKILL or freezing() interrupt the coredumping. Perhaps we
- * can do try_to_freeze() and check __fatal_signal_pending(),
- * but then we need to teach dump_write() to restart and clear
- * TIF_SIGPENDING.
- */
- return fatal_signal_pending(current) || freezing(current);
+ /* Only SIGKILL and the freezers set it after zap_threads(). */
+ return task_sigpending(current);
}
static void wait_for_dump_helpers(struct file *file)
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 103/109] signal: dont retarget shared signals in a dying thread group
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (101 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 102/109] signal: only SIGKILL and the freezers interrupt a coredumping task Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 104/109] ptrace: refuse to change the signal mask of a user worker Greg Kroah-Hartman
` (12 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Oleg Nesterov,
Christian Brauner (Amutable)
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <brauner@kernel.org>
commit c0e659511d4f49087da095a95799edb5b775a705 upstream.
If the calling thread has blocked a signal then
retarget_shared_pending() slingshots a shared pending signal to a
sibling thread. Today it only skips PF_EXITING threads but not a
coredumping thread.
But if SIGNAL_GROUP_EXIT is set no thread in the thread-group can
dequeue such signals anymore as get_signal() sends SIGKILL to every
thread and prepare_signal() drops any other signal.
exit_signals() also returns early on SIGNAL_GROUP_EXIT. But neither
sigprocmask() nor the mask restore in sigtimedwait() do.
Say a thread sleeps in sigtimedwait() and a sibling thread crashes. The
sleeping thread gets woken by the zap call. It restores its mask on the
way out and retargets a signal that was queued for the coredumping
process. That means TIF_SIGPENDING is set on the coredumping task which
zap_threads() had cleared. So a blocking write sees TIF_SIGPENDING and
truncates the dump.
Stop retargeting signals when SIGNAL_GROUP_EXIT is set and avoid
needlessly truncating coredumps.
Fixes: e6fa16ab9c1e ("signal: sigprocmask() should do retarget_shared_pending()")
Cc: stable@vger.kernel.org
Acked-by: Oleg Nesterov <oleg@redhat.com>
Link: https://patch.msgid.link/20260921-work-coredump-fixes-v3-5-8e4adb1619e6@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
kernel/signal.c | 4 ++++
1 file changed, 4 insertions(+)
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -3178,6 +3178,10 @@ static void retarget_shared_pending(stru
sigset_t retarget;
struct task_struct *t;
+ /* Nobody dequeues them in a dying group, see get_signal(). */
+ if (tsk->signal->flags & SIGNAL_GROUP_EXIT)
+ return;
+
sigandsets(&retarget, &tsk->signal->shared_pending.signal, which);
if (sigisemptyset(&retarget))
return;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 104/109] ptrace: refuse to change the signal mask of a user worker
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (102 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 103/109] signal: dont retarget shared signals in a dying thread group Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 105/109] fork: release the files of a failed fork after sched_cancel_fork() Greg Kroah-Hartman
` (11 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Oleg Nesterov,
Christian Brauner (Amutable)
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <brauner@kernel.org>
commit cabc249e6560bee7bdfee9fbcb7ce4e951dcfd41 upstream.
A user worker is created with every signal other than SIGKILL and
SIGSTOP blocked. It never runs user code and that mask is what keeps
get_signal() from dequeuing anything else for it. complete_signal()
never picks a thread that blocks the signal, tkill() queues on the
worker but nothing dequeues it. ptrace_signal() requeues an injected
signal that the tracee blocks.
PTRACE_SETSIGMASK is the only way to change that mask from the outside.
A tracer that clears it makes the worker eligible for every signal.
Whatever the worker then dequeues it can only act on by leaving.
Refuse PTRACE_SETSIGMASK for a user worker with -EPERM, the way
ptrace_attach() refuses a kernel thread. A worker is guaranteed to only
ever dequeue SIGKILL or SIGSTOP and an injected signal stays pending on
it, which is what already happens when the mask isn't touched.
Taking the request and quietly leaving the mask alone was the other
option, the way set_current_blocked() keeps SIGKILL and SIGSTOP
unblocked whatever userspace asks for. But then a tracer gets success
back with nothing changed and no way to tell that apart from a mask that
took effect.
Fixes: e8b33b8cfafc ("Revert "kernel: treat PF_IO_WORKER like PF_KTHREAD for ptrace/signals"")
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260921-work-coredump-fixes-v3-9-8e4adb1619e6@kernel.org
Acked-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
kernel/ptrace.c | 6 ++++++
1 file changed, 6 insertions(+)
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -1211,6 +1211,12 @@ int ptrace_request(struct task_struct *c
case PTRACE_SETSIGMASK: {
sigset_t new_set;
+ /* A user worker only ever takes SIGKILL and SIGSTOP. */
+ if (child->flags & PF_USER_WORKER) {
+ ret = -EPERM;
+ break;
+ }
+
if (addr != sizeof(sigset_t)) {
ret = -EINVAL;
break;
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 105/109] fork: release the files of a failed fork after sched_cancel_fork()
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (103 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 104/109] ptrace: refuse to change the signal mask of a user worker Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 106/109] exit: hang up the tty before closing the files Greg Kroah-Hartman
` (10 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Chris Mason,
Christian Brauner (Amutable)
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <brauner@kernel.org>
commit 296a98c1fa50778c3b31eebed1a3f903f9684349 upstream.
Reorder copy_process() cleanup so sched_fork() taking scx_fork_rwsem
scx_pre_fork() is safe and isn't held around exiting files. Right now, a
fork that fails while another thread closed the bpf link fd will
deadlock against its own read side. It also blocks every fork on the
system:
copy_process() holds scx_fork_rwsem for read
exit_files()
close_files()
filp_close_sync()
bpf_scx_unreg()
kthread_flush_work() waits for the disable work
scx_root_disable()
percpu_down_write(&scx_fork_rwsem)
Simply release the child's files after sched_cancel_fork() dropped the
lock. The task starts out as a copy of its parent so p->files points to
the parent's table until copy_files() replaces it. exit_files() must not
run for a fork that failed before that. Clear p->files up front so
exit_files() is a no-op for those and make copy_files() set it
explicitly for CLONE_FILES.
Reported-by: Chris Mason <mason@kernel.org>
Link: https://patch.msgid.link/20260921-work-coredump-fixes-v3-7-8e4adb1619e6@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
kernel/fork.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1595,6 +1595,7 @@ static int copy_files(u64 clone_flags, s
if (clone_flags & CLONE_FILES) {
atomic_inc(&oldf->count);
+ tsk->files = oldf;
return 0;
}
@@ -2079,6 +2080,8 @@ __latent_entropy struct task_struct *cop
INIT_LIST_HEAD(&p->sibling);
rcu_copy_process(p);
p->vfork_done = NULL;
+ /* Set by copy_files(), exit_files() on the error path skips NULL. */
+ p->files = NULL;
spin_lock_init(&p->alloc_lock);
init_sigpending(&p->pending);
@@ -2175,7 +2178,7 @@ __latent_entropy struct task_struct *cop
goto bad_fork_cleanup_semundo;
retval = copy_fs(clone_flags, p);
if (retval)
- goto bad_fork_cleanup_files;
+ goto bad_fork_cleanup_semundo;
retval = copy_sighand(clone_flags, p);
if (retval)
goto bad_fork_cleanup_fs;
@@ -2468,8 +2471,6 @@ bad_fork_cleanup_sighand:
__cleanup_sighand(p->sighand);
bad_fork_cleanup_fs:
exit_fs(p); /* blocking */
-bad_fork_cleanup_files:
- exit_files(p); /* blocking */
bad_fork_cleanup_semundo:
exit_sem(p);
bad_fork_cleanup_security:
@@ -2480,6 +2481,8 @@ bad_fork_cleanup_perf:
perf_event_free_task(p);
bad_fork_sched_cancel_fork:
sched_cancel_fork(p);
+ /* ->release() of a file may need scx_fork_rwsem for write. */
+ exit_files(p); /* blocking */
bad_fork_cleanup_policy:
lockdep_free_task(p);
#ifdef CONFIG_NUMA
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 106/109] exit: hang up the tty before closing the files
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (104 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 105/109] fork: release the files of a failed fork after sched_cancel_fork() Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 107/109] pid_namespace: prevent TIF_NOTIFY_SIGNAL from interrupting the reaper Greg Kroah-Hartman
` (9 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Chris Mason, Oleg Nesterov,
Christian Brauner (Amutable)
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <brauner@kernel.org>
commit ad016f0131ebfec250a3360ea75d35128b3f4aa8 upstream.
do_exit() closes the task's files in exit_files() and hangs up the
controlling tty of a session leader in disassociate_ctty(1) after
that. Since commit d99d38540bf0 ("fs: make close_files() synchronous")
the final __fput() of every file runs inside exit_files(). So when the
session leader holds the last open of its tty the tty is released
before disassociate_ctty() runs. tty_release() clears signal->tty for
the whole session in session_clear_tty() once the count drops to zero
and sends no signal doing so. disassociate_ctty(1) then finds neither a
tty nor a tty_old_pgrp and does nothing. The foreground process group
loses its SIGHUP:
do_exit()
exit_files()
close_files()
tty_release() tty->count == 0
session_clear_tty() signal->tty = NULL, no signal
disassociate_ctty(1)
get_current_tty() NULL
signal->tty_old_pgrp NULL, nothing sent
That only affects real ttys. For a pty the master's open keeps the
slave's count above zero. And it only affects a foreground job that
holds no descriptor to the tty anymore while its session leader exits.
Everything else is unchanged. The DTR drop on the last close happens in
tty_port_shutdown() regardless, stopped jobs get their SIGHUP from
kill_orphaned_pgrp() and signal->tty is cleared either way.
Before that commit the final __fput() ran from exit_task_work() which
comes after disassociate_ctty(). That order isn't old. Until v3.14
exit_task_work() came right after exit_files() and before v3.6 fput()
was synchronous, so the tty was always released first. Commit
c39df5fa37b0 ("exit: call disassociate_ctty() before
exit_task_namespaces()") moved disassociate_ctty() up to fix a pppd
crash and in front of exit_task_work() as a side effect. The hangup in
this case has worked since then and that's eleven years of userspace
being able to rely on it.
Hang the tty up before closing the files. This is the ordinary hangup
with the file still open: __tty_hangup() swaps in hung_up_tty_fops and
tty_release() runs from the close afterwards as it does when a modem
drops the line. disassociate_ctty() stays in front of
exit_task_namespaces() which the pppd fix needs.
Reported-by: Chris Mason <mason@kernel.org>
Link: https://patch.msgid.link/20260921-work-coredump-fixes-v3-8-8e4adb1619e6@kernel.org
Acked-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
kernel/exit.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -974,10 +974,11 @@ void __noreturn do_exit(long code)
exit_sem(tsk);
exit_shm(tsk);
- exit_files(tsk);
- exit_fs(tsk);
+ /* Hang the tty up before the last close of it can clear the session. */
if (group_dead)
disassociate_ctty(1);
+ exit_files(tsk);
+ exit_fs(tsk);
exit_nsproxy_namespaces(tsk);
exit_task_work(tsk);
exit_thread(tsk);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 107/109] pid_namespace: prevent TIF_NOTIFY_SIGNAL from interrupting the reaper
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (105 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 106/109] exit: hang up the tty before closing the files Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 108/109] signal: allow taks to temporarily block TIF_NOTIFY_SIGNAL Greg Kroah-Hartman
` (8 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Oleg Nesterov,
Christian Brauner (Amutable)
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <brauner@kernel.org>
commit 5c0985dd3c32fc2bbd0e8beaf13cf52cd97376a2 upstream.
Since commit 7fea700e04bd ("zap_pid_ns_processes: clear
TIF_NOTIFY_SIGNAL along with TIF_SIGPENDING") zap_pid_ns_processes()
clears TIF_NOTIFY_SIGNAL before every kernel_wait4() call. Otherwise
kernel_wait4() doesn't sleep and returns -ERESTARTSYS if
signal_pending() is true and the reaper busy-waits.
But clearing TIF_NOTIFY_SIGNAL doesn't fix it. It can get raised again.
For example, klp_send_signals() sets it raw against any task that hasn't
transitioned yet. And it keeps sending it until the livepatch finishes.
The sleep loop waiting for pid_allocated to drop has the same problem
and it doesn't even clear anything. So a schedule() in
TASK_INTERRUPTIBLE returns immediately.
Use a no_notify_signal guard. signal_pending() will ignore
TIF_NOTIFY_SIGNAL and the pid namespace reaper sleeps until a child
exits or free_pid() wakes it.
Link: https://patch.msgid.link/20260824-work-tif_notify_signal-v2-5-6609e42b3157@kernel.org
Fixes: 7fea700e04bd ("zap_pid_ns_processes: clear TIF_NOTIFY_SIGNAL along with TIF_SIGPENDING")
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Cc: stable@vger.kernel.org
Suggested-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
kernel/pid_namespace.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -238,9 +238,10 @@ void zap_pid_ns_processes(struct pid_nam
* kernel_wait4() will also block until our children traced from the
* parent namespace are detached and become EXIT_DEAD.
*/
+ /* Task work must not busy-loop the reaper, see signal_pending(). */
+ guard(no_notify_signal)();
do {
clear_thread_flag(TIF_SIGPENDING);
- clear_thread_flag(TIF_NOTIFY_SIGNAL);
rc = kernel_wait4(-1, NULL, __WALL, NULL);
} while (rc != -ECHILD);
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 108/109] signal: allow taks to temporarily block TIF_NOTIFY_SIGNAL
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (106 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 107/109] pid_namespace: prevent TIF_NOTIFY_SIGNAL from interrupting the reaper Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 109/109] ACPI: scan: Avoid registering platform devices with resource overlaps Greg Kroah-Hartman
` (7 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Oleg Nesterov,
Christian Brauner (Amutable)
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <brauner@kernel.org>
commit 22b7f491be8f93ff72de0a3fe02b8ee9e64d3f21 upstream.
TIF_NOTIFY_SIGNAL is used to kick a task in uninterruptible sleep to
return to userspace and run task work and then go back to sleep. This
mechanism works well but breaks e.g., coredumps. dump_interrupted() only
allows fatal signals to interrupt a coredump and the whole regular write
path going to actual filesystems is impervious to TIF_NOTIFY_SIGNAL as
well.
Add PF_NO_NOTIFY_SIGNAL and helpers to raise and restore it. This is the
same approach as memalloc_nofs_save(). signal_pending() will not report
a fake pending signal via TIF_NOTIFY_SIGNAL if inside a
PF_NO_NOTIFY_SIGNAL section.
No functional changes.
Link: https://patch.msgid.link/20260824-work-tif_notify_signal-v2-1-6609e42b3157@kernel.org
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
include/linux/sched.h | 2 +-
include/linux/sched/signal.h | 27 +++++++++++++++++++++++++--
2 files changed, 26 insertions(+), 3 deletions(-)
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1764,7 +1764,7 @@ extern struct pid __rcu *cad_pid;
* I am cleaning dirty pages from some other bdi. */
#define PF_KTHREAD 0x00200000 /* I am a kernel thread */
#define PF_RANDOMIZE 0x00400000 /* Randomize virtual address space */
-#define PF__HOLE__00800000 0x00800000
+#define PF_NO_NOTIFY_SIGNAL 0x00800000 /* see no_notify_signal_save() */
#define PF__HOLE__01000000 0x01000000
#define PF__HOLE__02000000 0x02000000
#define PF_NO_SETAFFINITY 0x04000000 /* Userland is not allowed to meddle with cpus_mask */
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -2,6 +2,7 @@
#ifndef _LINUX_SCHED_SIGNAL_H
#define _LINUX_SCHED_SIGNAL_H
+#include <linux/cleanup.h>
#include <linux/rculist.h>
#include <linux/signal.h>
#include <linux/sched.h>
@@ -383,14 +384,36 @@ static inline int task_sigpending(struct
return unlikely(test_tsk_thread_flag(p,TIF_SIGPENDING));
}
+/* Prevent TIF_NOTIFY_SIGNAL from interrupting this task. */
+static inline unsigned int no_notify_signal_save(void)
+{
+ unsigned int flags = current->flags;
+
+ current->flags |= PF_NO_NOTIFY_SIGNAL;
+ return flags;
+}
+
+/* Restore the previous PF_NO_NOTIFY_SIGNAL state. */
+static inline void no_notify_signal_restore(unsigned int flags)
+{
+ current_restore_flags(flags, PF_NO_NOTIFY_SIGNAL);
+}
+
+DEFINE_LOCK_GUARD_0(no_notify_signal,
+ _T->flags = no_notify_signal_save(),
+ no_notify_signal_restore(_T->flags),
+ unsigned int flags)
+
static inline int signal_pending(struct task_struct *p)
{
/*
* TIF_NOTIFY_SIGNAL isn't really a signal, but it requires the same
* behavior in terms of ensuring that we break out of wait loops
- * so that notify signal callbacks can be processed.
+ * so that notify signal callbacks can be processed. Not for a task
+ * that asked not to be interrupted by it, see no_notify_signal_save().
*/
- if (unlikely(test_tsk_thread_flag(p, TIF_NOTIFY_SIGNAL)))
+ if (unlikely(test_tsk_thread_flag(p, TIF_NOTIFY_SIGNAL)) &&
+ likely(!(READ_ONCE(p->flags) & PF_NO_NOTIFY_SIGNAL)))
return 1;
return task_sigpending(p);
}
^ permalink raw reply [flat|nested] 117+ messages in thread
* [PATCH 6.18 109/109] ACPI: scan: Avoid registering platform devices with resource overlaps
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (107 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 108/109] signal: allow taks to temporarily block TIF_NOTIFY_SIGNAL Greg Kroah-Hartman
@ 2026-10-28 17:01 ` Greg Kroah-Hartman
2026-10-28 19:39 ` [PATCH 6.18 000/109] 6.18.60-rc1 review Brett A C Sheffield
` (6 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-28 17:01 UTC (permalink / raw)
To: stable
Cc: Greg Kroah-Hartman, patches, Julien, Paul Menzel, Andy Shevchenko,
Rafael J. Wysocki
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
commit f234fdaae1cad8c39265e7ca0a14633076ec7154 upstream.
If acpi_dev_get_resources() returns overlapping I/O or memory resources,
the subsequent registration of a platform device will fail with -EBUSY
due to a resource conflict. This is reported to happen on Acer Aspire
ES1-572 [1].
Avoid that by adjusting resources returned by acpi_dev_get_resources()
to eliminate partial overlaps between them.
This has not been regarded as necessary before because putting
overlapping resources into the _CRS of one device is really pointless,
but now that the issue has been reported to actually happen in the
field, it needs to be done.
Fixes: ab06eb920401 ("ACPI: scan: Register platform devices for fixed event buttons")
Fixes: 48fe2cddc85c ("tpm_crb: Convert ACPI driver to a platform one")
Reported-by: Julien <julien82453@gmail.com>
Tested-by: Julien <julien82453@gmail.com>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Closes: https://lore.kernel.org/linux-integrity/CAJOGg3z6LJPDsdPNBxajgy8_wQxfhYBRxe4EiurZf3kPU5A5Bw@mail.gmail.com/ [1]
Cc: All applicable <stable@vger.kernel.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
[ rjw: Tweaked the new message ]
Link: https://patch.msgid.link/12955541.O9o76ZdvQC@rafael.j.wysocki
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/acpi/acpi_platform.c | 39 +++++++++++++++++++++++++++++++++++++--
1 file changed, 37 insertions(+), 2 deletions(-)
--- a/drivers/acpi/acpi_platform.c
+++ b/drivers/acpi/acpi_platform.c
@@ -12,6 +12,7 @@
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
@@ -71,6 +72,36 @@ static struct notifier_block acpi_platfo
.notifier_call = acpi_platform_device_remove_notify,
};
+static unsigned int acpi_platform_adjust_resources(struct acpi_device *adev,
+ struct resource *new_res,
+ struct resource *resources,
+ unsigned int count)
+{
+ unsigned int i;
+
+ if (!(new_res->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
+ return count;
+
+ for (i = 0; i < count; ) {
+ struct resource *res = &resources[i];
+
+ if (resource_type(new_res) != resource_type(res) ||
+ !resource_union(new_res, res, new_res)) {
+ i++;
+ continue;
+ }
+
+ dev_info(&adev->dev, "%pR expanded due to overlap\n", new_res);
+ /*
+ * Eliminate the previously processed resource that overlapped
+ * with the new one because it is not necessary any more.
+ */
+ memmove(res, res + 1, (--count - i) * sizeof(*res));
+ }
+
+ return count;
+}
+
static void acpi_platform_fill_resource(struct acpi_device *adev,
const struct resource *src, struct resource *dest)
{
@@ -151,10 +182,14 @@ struct platform_device *acpi_create_plat
return ERR_PTR(-ENOMEM);
}
count = 0;
- list_for_each_entry(rentry, &resource_list, node)
+ list_for_each_entry(rentry, &resource_list, node) {
+ count = acpi_platform_adjust_resources(adev,
+ rentry->res,
+ resources,
+ count);
acpi_platform_fill_resource(adev, rentry->res,
&resources[count++]);
-
+ }
acpi_dev_free_resource_list(&resource_list);
}
}
^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [PATCH 6.18 000/109] 6.18.60-rc1 review
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (108 preceding siblings ...)
2026-10-28 17:01 ` [PATCH 6.18 109/109] ACPI: scan: Avoid registering platform devices with resource overlaps Greg Kroah-Hartman
@ 2026-10-28 19:39 ` Brett A C Sheffield
2026-10-29 3:09 ` Peter Schneider
` (5 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Brett A C Sheffield @ 2026-10-28 19:39 UTC (permalink / raw)
To: gregkh
Cc: stable, patches, linux-kernel, torvalds, akpm, linux, shuah,
patches, lkft-triage, pavel, jonathanh, f.fainelli,
sudipm.mukherjee, rwarsow, conor, hargar, broonie, achill, sr,
Brett A C Sheffield
# Librecast Test Results
044/044 [ OK ] liblcrq
010/010 [ OK ] libmld
120/120 [ OK ] liblibrecast
CPU/kernel: Linux auntie 6.18.60-rc1-g61d622c89a02 #1 SMP PREEMPT_DYNAMIC Wed Oct 28 18:29:26 -00 2026 x86_64 AMD Ryzen 9 9950X 16-Core Processor AuthenticAMD GNU/Linux
Tested-by: Brett A C Sheffield <bacs@librecast.net>
^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [PATCH 6.18 000/109] 6.18.60-rc1 review
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (109 preceding siblings ...)
2026-10-28 19:39 ` [PATCH 6.18 000/109] 6.18.60-rc1 review Brett A C Sheffield
@ 2026-10-29 3:09 ` Peter Schneider
2026-10-29 10:07 ` Pavel Machek
` (4 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Peter Schneider @ 2026-10-29 3:09 UTC (permalink / raw)
To: Greg Kroah-Hartman, stable
Cc: patches, linux-kernel, torvalds, akpm, linux, shuah, patches,
lkft-triage, pavel, jonathanh, f.fainelli, sudipm.mukherjee,
rwarsow, conor, hargar, broonie, achill, sr
Am 28.10.2026 um 17:59 schrieb Greg Kroah-Hartman:
> This is the start of the stable review cycle for the 6.18.60 release.
> There are 109 patches in this series, all will be posted as a response
> to this one. If anyone has any issues with these being applied, please
> let me know.
Builds, boots and works on my 2-socket Ivy Bridge Xeon E5-2697v2 server. No dmesg oddities or regressions found.
Tested-by: Peter Schneider <pschneider1968@googlemail.com>
Beste Grüße,
Peter Schneider
--
Climb the mountain not to plant your flag, but to embrace the challenge,
enjoy the air and behold the view. Climb it so you can see the world,
not so the world can see you. -- David McCullough Jr.
OpenPGP: 0xA3828BD796CCE11A8CADE8866E3A92C92C3FF244
Download: https://www.peters-netzplatz.de/download/pschneider1968_pub.asc
https://keys.mailvelope.com/pks/lookup?op=get&search=pschneider1968@googlemail.com
https://keys.mailvelope.com/pks/lookup?op=get&search=pschneider1968@gmail.com
^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [PATCH 6.18 000/109] 6.18.60-rc1 review
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (110 preceding siblings ...)
2026-10-29 3:09 ` Peter Schneider
@ 2026-10-29 10:07 ` Pavel Machek
2026-10-29 13:43 ` Miguel Ojeda
` (3 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Pavel Machek @ 2026-10-29 10:07 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: stable, patches, linux-kernel, torvalds, akpm, linux, shuah,
patches, lkft-triage, pavel, jonathanh, f.fainelli,
sudipm.mukherjee, rwarsow, conor, hargar, broonie, achill, sr
[-- Attachment #1: Type: text/plain, Size: 504 bytes --]
Hi!
> This is the start of the stable review cycle for the 6.18.60 release.
> There are 109 patches in this series, all will be posted as a response
> to this one. If anyone has any issues with these being applied, please
> let me know.
CIP testing did not find any problems here:
https://gitlab.com/cip-project/cip-testing/linux-stable-rc-ci/-/tree/linux-6.18.y
Tested-by: Pavel Machek (CIP) <pavel@nabladev.com>
Best regards,
Pavel
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [PATCH 6.18 000/109] 6.18.60-rc1 review
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (111 preceding siblings ...)
2026-10-29 10:07 ` Pavel Machek
@ 2026-10-29 13:43 ` Miguel Ojeda
2026-10-29 14:23 ` Ron Economos
` (2 subsequent siblings)
115 siblings, 0 replies; 117+ messages in thread
From: Miguel Ojeda @ 2026-10-29 13:43 UTC (permalink / raw)
To: gregkh
Cc: achill, akpm, broonie, conor, f.fainelli, hargar, jonathanh,
linux-kernel, linux, lkft-triage, patches, patches, pavel,
rwarsow, shuah, sr, stable, sudipm.mukherjee, torvalds,
Miguel Ojeda
On Wed, 28 Oct 2026 17:59:44 +0100 Greg Kroah-Hartman <gregkh@linuxfoundation.org> wrote:
>
> This is the start of the stable review cycle for the 6.18.60 release.
> There are 109 patches in this series, all will be posted as a response
> to this one. If anyone has any issues with these being applied, please
> let me know.
>
> Responses should be made by Fri, 30 Oct 2026 16:59:22 +0000.
> Anything received after that time might be too late.
Boot-tested under QEMU for Rust x86_64, arm64 and riscv64; built-tested
for loongarch64 and arm32:
Tested-by: Miguel Ojeda <ojeda@kernel.org>
Thanks!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [PATCH 6.18 000/109] 6.18.60-rc1 review
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (112 preceding siblings ...)
2026-10-29 13:43 ` Miguel Ojeda
@ 2026-10-29 14:23 ` Ron Economos
2026-10-29 15:21 ` Shuah Khan
2026-10-29 16:29 ` Florian Fainelli
115 siblings, 0 replies; 117+ messages in thread
From: Ron Economos @ 2026-10-29 14:23 UTC (permalink / raw)
To: Greg Kroah-Hartman, stable
Cc: patches, linux-kernel, torvalds, akpm, linux, shuah, patches,
lkft-triage, pavel, jonathanh, f.fainelli, sudipm.mukherjee,
rwarsow, conor, hargar, broonie, achill, sr
On 10/28/26 09:59, Greg Kroah-Hartman wrote:
> This is the start of the stable review cycle for the 6.18.60 release.
> There are 109 patches in this series, all will be posted as a response
> to this one. If anyone has any issues with these being applied, please
> let me know.
>
> Responses should be made by Fri, 30 Oct 2026 16:59:22 +0000.
> Anything received after that time might be too late.
>
> The whole patch series can be found in one patch at:
> https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.60-rc1.gz
> or in the git tree and branch at:
> git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git linux-6.18.y
> and the diffstat can be found below.
>
> thanks,
>
> greg k-h
Built and booted successfully on RISC-V RV64 (HiFive Unmatched).
Tested-by: Ron Economos <re@w6rz.net>
^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [PATCH 6.18 000/109] 6.18.60-rc1 review
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (113 preceding siblings ...)
2026-10-29 14:23 ` Ron Economos
@ 2026-10-29 15:21 ` Shuah Khan
2026-10-29 16:29 ` Florian Fainelli
115 siblings, 0 replies; 117+ messages in thread
From: Shuah Khan @ 2026-10-29 15:21 UTC (permalink / raw)
To: Greg Kroah-Hartman, stable
Cc: patches, linux-kernel, torvalds, akpm, linux, shuah, patches,
lkft-triage, pavel, jonathanh, f.fainelli, sudipm.mukherjee,
rwarsow, conor, hargar, broonie, achill, sr, Shuah Khan
On 10/28/26 10:59, Greg Kroah-Hartman wrote:
> This is the start of the stable review cycle for the 6.18.60 release.
> There are 109 patches in this series, all will be posted as a response
> to this one. If anyone has any issues with these being applied, please
> let me know.
>
> Responses should be made by Fri, 30 Oct 2026 16:59:22 +0000.
> Anything received after that time might be too late.
>
> The whole patch series can be found in one patch at:
> https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.60-rc1.gz
> or in the git tree and branch at:
> git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git linux-6.18.y
> and the diffstat can be found below.
>
> thanks,
>
> greg k-h
>
Compiled and booted on my test system. No dmesg regressions.
Tested-by: Shuah Khan <skhan@linuxfoundation.org>
thanks,
-- Shuah
^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [PATCH 6.18 000/109] 6.18.60-rc1 review
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
` (114 preceding siblings ...)
2026-10-29 15:21 ` Shuah Khan
@ 2026-10-29 16:29 ` Florian Fainelli
115 siblings, 0 replies; 117+ messages in thread
From: Florian Fainelli @ 2026-10-29 16:29 UTC (permalink / raw)
To: Greg Kroah-Hartman, stable
Cc: patches, linux-kernel, torvalds, akpm, linux, shuah, patches,
lkft-triage, pavel, jonathanh, sudipm.mukherjee, rwarsow, conor,
hargar, broonie, achill, sr
On 10/28/26 09:59, Greg Kroah-Hartman wrote:
> This is the start of the stable review cycle for the 6.18.60 release.
> There are 109 patches in this series, all will be posted as a response
> to this one. If anyone has any issues with these being applied, please
> let me know.
>
> Responses should be made by Fri, 30 Oct 2026 16:59:22 +0000.
> Anything received after that time might be too late.
>
> The whole patch series can be found in one patch at:
> https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.60-rc1.gz
> or in the git tree and branch at:
> git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git linux-6.18.y
> and the diffstat can be found below.
>
> thanks,
>
> greg k-h
On ARCH_BRCMSTB using 32-bit and 64-bit ARM kernels, build tested on
BMIPS_GENERIC:
Tested-by: Florian Fainelli <florian.fainelli@broadcom.com>
--
Florian
^ permalink raw reply [flat|nested] 117+ messages in thread
end of thread, other threads:[~2026-10-29 16:29 UTC | newest]
Thread overview: 117+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-28 16:59 [PATCH 6.18 000/109] 6.18.60-rc1 review Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 001/109] eventfs: fix SRCU read lock leak in eventfs_iterate() Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 002/109] ACPI: scan: Register platform devices for fixed event buttons Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 003/109] ice: fix rxq info registering in mbuf packets Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 004/109] ice: change XDP RxQ frag_size from DMA write length to xdp.frame_sz Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 005/109] rose: clear stale N(R)/N(S) before (re)transmitting a data frame Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 006/109] netrom: linearize and validate lengths in nr_rx_frame() Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 007/109] netrom: require full NET/ROM headers in nr_route_frame() Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 008/109] net: netrom: validate source address in nr_find_socket() Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 009/109] netrom: bound fragment reassembly in nr_queue_rx_frame() Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 010/109] ax25: fix OOB read after address header strip in ax25_rcv() Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 011/109] ax25: check frame length in ax25_decode() and ax25_rx_iframe() Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 012/109] net: ax25: fix integer overflow in ax25_rx_fragment() Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 013/109] netrom: use a single lock for the node and neighbour lists Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 014/109] netrom: look up and update routes in one critical section Greg Kroah-Hartman
2026-10-28 16:59 ` [PATCH 6.18 015/109] netrom: fix reference count leak in nr_rt_device_down Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 016/109] netrom: clear nr_neigh->ax25 under the routing lock in nr_link_failed() Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 017/109] netrom: fix nr_node leak when nr_del_node() removes the last route Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 018/109] rose: clear through routes when a neighbour is removed from the tables Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 019/109] rose: restart the link of a neighbour created over a live AX.25 connection Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 020/109] ax25: serialise the transmission of I frames on a connection Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 021/109] rose: process received frames and send with the socket locked Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 022/109] ax25: dont create two connections for the same link in ax25_send_frame() Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 023/109] ax25: read the connections ax25_dev only once when walking ax25_list Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 024/109] ax25: wait for ax25_send_frame() before detaching the connections of a device Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 025/109] ax25: dont accept frames on a device that is going down Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 026/109] ax25: send a private copy of each I frame Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 027/109] rose: clear the circuits of a neighbour that restarts the link Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 028/109] ax25: send unacknowledged I frames again when a link is re-established Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 029/109] rose: bring up the link of the preferred route passed over by a connect request Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 030/109] ax25: dont free an incoming SABM skb left on the listeners queue Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 031/109] block: avoid potential deadlock on zone revalidation failure Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 032/109] mm/damon/core: error damos_commit_quota_goal() for zero target_value Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 033/109] net_sched: codel: fix stale state for empty flows in fq_codel Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 034/109] Bluetooth: L2CAP: refuse __l2cap_chan_add if chan already has conn Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 035/109] tracing: Hold trace_types_lock when updating modules in persistent instances Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 036/109] tracing: Hold trace_types_lock when resetting last boot data from readers Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 037/109] tracing: Reject incomplete event filter expressions Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 038/109] tracing: Save the event file instead of the compatible histogram Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 039/109] tracing: Unregister field variable histograms when the command fails Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 040/109] tracing: Fix 64-bit and signed histogram fields on 32-bit kernels Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 041/109] tracing/histograms: Verify event_call identity when updating synth_event refcount Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 042/109] net: gso: limit recursive IP-in-IP segmentation Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 043/109] io-wq: order the exit bit against worker creation task work Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 044/109] io_uring/waitid: own the request before clearing ->head Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 045/109] io_uring/cmd: dont leave list poison in ->hash_node Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 046/109] io_uring/wait: dont spin on a stuck overflow list Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 047/109] io_uring/io-wq: dont lose the signal that ends work Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 048/109] io_uring/io-wq: dont hide a link from cancellations Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 049/109] io_uring: dont flood a cancelling task with task_work Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 050/109] io_uring/io-wq: dont go to sleep on pending work Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 051/109] io_uring/io-wq: dont strand work when a worker exits Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 052/109] io_uring/io-wq: dont exit with a creation pending Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 053/109] io_uring/io-wq: let cancel find picked up hashed work Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 054/109] io_uring/poll: dont lose a cancel as io-wq arms poll Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 055/109] io_uring: dont lose a cancel as io-wq parks a request Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 056/109] io_uring/cancel: dont forget that the work was running Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 057/109] io_uring/cancel: recognize "no timeout" on 32-bit Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 058/109] io_uring/cancel: let CANCEL_ALL find parked requests Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 059/109] io_uring: wake ring pollers for local task_work Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 060/109] io_uring/wait: look at all CQ waiters on wake up Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 061/109] io_uring/poll: drop stale double poll entry on rearm Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 062/109] io_uring/futex: allocate private hash before waiting Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 063/109] io_uring: drop the CQE cache if a refill fails Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 064/109] io_uring/register: dont wrap 32b CQEs when resizing Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 065/109] io_uring/register: handle index wrap " Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 066/109] io_uring/fdinfo: fix CQE dump of CQE32 rings Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 067/109] io_uring/timeout: dont skip over reached sequences Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 068/109] io_uring/timeout: keep repeat count on multishot update Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 069/109] io_uring/wait: no early -ETIME if min_wait finds work Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 070/109] io_uring/napi: make stale entries time out after 60s Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 071/109] io_uring/napi: dont busy poll past an expired timeout Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 072/109] io_uring/zcrx: fix buffer offsets in areas above 4GB Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 073/109] io_uring/kbuf: handle index wrap in last buffer check Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 074/109] io_uring/sqpoll: let the thread sleep on disabled rings Greg Kroah-Hartman
2026-10-28 17:00 ` [PATCH 6.18 075/109] io_uring/openclose: keep the lock in io_pipe_fixed() Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 076/109] io_uring: dont pass restart error codes to userspace Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 077/109] io_uring/net: dont retry recv on uncommitted buffers Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 078/109] io_uring/timeout: no ABS update of multishot timeouts Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 079/109] io_uring/timeout: dont end a linked timeout with 1 Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 080/109] io_uring: look up the file before queueing to io-wq Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 081/109] io_uring/poll: avoid retry wakeup loss when dropping ownership Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 082/109] io_uring/timeout: dont put a sequence target behind the last flush Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 083/109] io_uring/timeout: dont flush with the sequence " Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 084/109] io_uring/timeout: dont lose a cancel as io-wq arms a timeout Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 085/109] io_uring/poll: dont loop on HUP with multiple references Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 086/109] io_uring/net: keep recv buffer select length within MAX_RW_COUNT Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 087/109] io_uring/wait: wake real-time waiters from hard interrupt context Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 088/109] io_uring/waitid: prevent cancel reference wrap Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 089/109] io_uring: end the drain when the drain request completes Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 090/109] io_uring: keep the drain active when a deferred drain request is started Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 091/109] keys: Avoid the owner account dereference in named keyring lookup Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 092/109] keys: Check the authorization payload in process keyring searches Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 093/109] keys: Protect the type name while describing a key Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 094/109] keys: make keyring indexes independent of kernel addresses Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 095/109] keys: Protect the read method check against type removal Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 096/109] KEYS: Fix key_reject_and_link() race with keyring restriction Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 097/109] KEYS: trusted: Fix blob allocation size in tpm2_key_decode() Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 098/109] keys/trusted_keys: return immediately after TPM unseal failure Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 099/109] asymmetric_keys: reject trust keys without IDs Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 100/109] ftrace: Initialize modname and offset in print_rec() Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 101/109] coredump: hold RCU while releasing parked threads Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 102/109] signal: only SIGKILL and the freezers interrupt a coredumping task Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 103/109] signal: dont retarget shared signals in a dying thread group Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 104/109] ptrace: refuse to change the signal mask of a user worker Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 105/109] fork: release the files of a failed fork after sched_cancel_fork() Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 106/109] exit: hang up the tty before closing the files Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 107/109] pid_namespace: prevent TIF_NOTIFY_SIGNAL from interrupting the reaper Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 108/109] signal: allow taks to temporarily block TIF_NOTIFY_SIGNAL Greg Kroah-Hartman
2026-10-28 17:01 ` [PATCH 6.18 109/109] ACPI: scan: Avoid registering platform devices with resource overlaps Greg Kroah-Hartman
2026-10-28 19:39 ` [PATCH 6.18 000/109] 6.18.60-rc1 review Brett A C Sheffield
2026-10-29 3:09 ` Peter Schneider
2026-10-29 10:07 ` Pavel Machek
2026-10-29 13:43 ` Miguel Ojeda
2026-10-29 14:23 ` Ron Economos
2026-10-29 15:21 ` Shuah Khan
2026-10-29 16:29 ` Florian Fainelli
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.