| CVE |
Vendors |
Products |
Updated |
CVSS v3.1 |
| In the Linux kernel, the following vulnerability has been resolved:
enic: fix tx_hang_reset use-after-free on device removal
enic_remove() cancels the reset and change_mtu_work items but does not
cancel tx_hang_reset. A TX timeout that fires while the device is being
removed can schedule enic_tx_hang_reset() so that it runs after
free_netdev(), resulting in a use-after-free.
cancel_work_sync() alone is not sufficient here: the still-live watchdog
and notify paths can re-schedule these work items in the window between
the cancel and unregister_netdev(). Use disable_work_sync(), which
cancels the work and blocks any subsequent schedule_work() from
requeuing it, and apply it to the reset and change_mtu_work items as
well so the same requeue race is closed for all teardown work. |
| In the Linux kernel, the following vulnerability has been resolved:
xsk: validate metadata when processing requests
The zero-copy path validates TX metadata while obtaining the descriptor
context, then reads it again later when preparing the hardware request.
User space can change the metadata between those operations and bypass the
original validation.
Validate the metadata in xsk_tx_metadata_request() and use the resulting
flags snapshot for every feature check. Read request fields once so all
zero-copy drivers process only values observed after successful
validation. |
| In the Linux kernel, the following vulnerability has been resolved:
net/smc: fix TOCTOU race between smc_listen_out() and listener close
smc_listen_out() reads lsmc->sk.sk_state without the listener lock,
then acquires lock_sock_nested() only after the check passes. This
opens a window where smc_close_active() can transition the listener
to SMC_CLOSED, call smc_close_cleanup_listen() to drain the accept
queue, and release the lock, all between the lockless read and the
delayed lock acquisition:
smc_listen_work (smc_hs_wq) smc_close_active()
------------------------------- -------------------------
release_sock(child)
if (sk_state == SMC_LISTEN) TRUE
lock_sock(listener)
sk_state = SMC_CLOSED
smc_close_cleanup_listen()
release_sock(listener)
flush_work(tcp_listen_work)
lock_sock_nested(listener)
smc_accept_enqueue(listener, child) /* child enqueued on dead listener */
smc_close_active() flushes only tcp_listen_work. Work items already
dispatched onto smc_hs_wq for the CLC handshake continue running
unguarded. smc_accept_enqueue() takes a sock_hold() on the child that
is never released, so the child smc_sock, its clcsock, and the
reference all leak. A remote peer that opens TCP connections while the
server calls close() can exhaust kernel memory.
Move lock_sock_nested() to before the sk_state check so that the test
and the enqueue are atomic under the listener lock. |
| In the Linux kernel, the following vulnerability has been resolved:
vt: stabilize tty reference in kbd_keycode with tty_port_tty_get
kbd_keycode() reads vc->port.tty without acquiring a tty reference,
racing against con_shutdown() which clears port.tty under a different
lock. Use tty_port_tty_get()/tty_kref_put() to hold a proper reference
for the duration the tty pointer is needed. |
| In the Linux kernel, the following vulnerability has been resolved:
inet: frags: publish queues before arming timer
inet_frag_create() arms the fragment queue timer before inserting the
queue into the fqdir rhashtable. If the namespace fragment timeout is
zero or negative, the timer can run before the queue is published.
The timer callback then marks the queue complete, tries to remove a node
that is not in the hash table yet, and drops the anticipated hash
reference. Creation can subsequently publish the completed queue without
restoring that reference, leaving a stale hash node after the caller drops
the remaining reference.
Publish the queue first and arm the timer while holding the queue lock.
This makes timer expiry wait until the queue is visible in the hash table,
so inet_frag_kill() can remove the node and balance the hash reference. |
| In the Linux kernel, the following vulnerability has been resolved:
ipv4: fix use-after-free in fib_nhc_update_mtu()
fib_nhc_update_mtu() walks the nexthop exception table under RTNL, but
RTNL does not serialize this walk with PMTU exception updates. The walk
uses rcu_dereference_protected() with a constant true condition without
holding fnhe_lock.
The following interleaving can therefore occur:
CPU 0 CPU 1
fib_nhc_update_mtu() update_or_create_fnhe()
load fnhe spin_lock_bh(&fnhe_lock)
fnhe_remove_oldest()
unlink fnhe
kfree_rcu(fnhe, rcu)
<quiescent state>
access fnhe after grace period
KASAN reported:
BUG: KASAN: slab-use-after-free in fib_nhc_update_mtu+0x3df/0x410
Read of size 8 at addr ffff888107d49000 by task poc/90
Call Trace:
fib_nhc_update_mtu+0x3df/0x410
fib_sync_mtu+0x7a/0xd0
fib_netdev_event+0x229/0x3f0
netif_set_mtu_ext+0x33a/0x570
dev_set_mtu+0x88/0x120
The same walk updates fnhe_pmtu and fnhe_mtu_locked. These fields form a
pair and other writers serialize them with fnhe_lock. RCU alone prevents
reclamation, but would still allow concurrent writers to leave a mixed
pair.
Walk the table under RCU and acquire fnhe_lock only while updating each
exception. RCU keeps the current entry alive while the short critical
section serializes its paired PMTU fields. This avoids holding the global
lock while scanning all 2048 buckets for every nexthop. |
| In the Linux kernel, the following vulnerability has been resolved:
KVM: SVM: Serialize accesses to the owner and mirror list with separate lock
Interaction between KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM and
KVM_CAP_VM_COPY_ENC_CONTEXT_FROM can cause two separate issues:
- in sev_migrate_from(), when the destination KVM is a mirror, the mirror
entry is moved from the source's list to the owner's mirror_vms list,
without holding the owner's lock unlike other writers of the owner's
mirror list (sev_vm_copy_enc_context_from(), sev_vm_destroy()).
A concurrent COPY or destroy can race with sev_migrate_from() and
corrupt the list.
- In sev_vm_destroy(), the *owner* is still active and could receive
concurrently a KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM that causes
sev->enc_context_owner to change. In this case the incorrect VM
receives kvm_put_kvm().
The second issue needs particular care because the owner could disappear
altogether (even though the race window is impossibly small) between
reading it and locking it. There is thus no way to perform the checks
under the owner lock without putting struct kvm under SLAB_TYPESAFE_BY_RCU
(which would allow kvm_get_kvm_safe() under RCU critical section).
It is much simpler to just use a global lock, since the critical
sections are so small and the new lock is always a leaf lock. |
| In the Linux kernel, the following vulnerability has been resolved:
ring-buffer: Use current_context for safe per-CPU buffer swap
The ring_buffer_swap_cpu() function currently checks the per-CPU
committing counter to determine if a buffer is actively being written to
before performing the swap. However, there exists a race window where
this check can be bypassed:
ring_buffer_lock_reserve
cpu_buffer = buffer->buffers[cpu]; // cpu_buffer_a
rb_reserve_next_event
rb_start_commit // inc committing
if (unlikely(READ_ONCE(cpu_buffer->buffer) != buffer)) {...}
__rb_reserve_next
rb_move_tail
rb_end_commit(cpu_buffer); // dec committing => 0
/* interrupt hits here, successfully swaps! */
local_inc(&cpu_buffer->committing);
ring_buffer_unlock_commit
cpu_buffer = buffer->buffers[cpu]; // cpu_buffer_b
rb_commit
rb_end_commit
RB_WARN_ON(cpu_buffer, !local_read(&cpu_buffer->committing))
// triggers warning
The committing counter can temporarily drop to 0 during a single write
operation (within rb_move_tail), creating a window where swap can
succeed even though the write is still in progress. This leads to
inconsistent buffer state and triggers the RB_WARN_ON in rb_commit().
Replace the committing counter check with current_context checks, which
are set at the entry of ring_buffer_lock_reserve() and remain valid
throughout the entire write operation, providing a reliable indicator of
buffer busy state during swap. |
| In the Linux kernel, the following vulnerability has been resolved:
sched/psi: Shut down rtpoll_timer in psi_cgroup_free()
psi_schedule_rtpoll_work() is called locklessly from the scheduler hotpath
and can race psi_trigger_destroy() taking down the last rtpoll trigger under
rtpoll_trigger_lock:
psi_schedule_rtpoll_work() psi_trigger_destroy()
rcu_read_lock();
task = rcu_dereference(rtpoll_task);
rcu_assign_pointer(rtpoll_task, NULL);
timer_delete(&rtpoll_timer);
mod_timer(&rtpoll_timer, ...);
rcu_read_unlock();
synchronize_rcu();
kthread_stop(task_to_destroy);
The group can then be freed with the re-armed timer still pending, and
poll_timer_fn() runs on freed memory.
461daba06bdc ("psi: eliminate kthread_worker from psi trigger scheduling
mechanism") deleted the timer synchronously after the synchronize_rcu(),
which prevented this but raced trigger creation instead: the deletion could
cancel the timer that a new trigger set armed during the grace period and,
as creation also reinitialized the timer at the time, corrupt it.
8f91efd870ea ("psi: Fix race between psi_trigger_create/destroy") moved the
initialization into group_init() and the deletion into the locked section,
trading the creation races for the window above.
Neither placement in the destruction path works. A pending timer firing
while the group is alive is harmless though. poll_timer_fn() just wakes the
rtpoll waitqueue and doesn't re-arm itself. Bind the timer to the group's
lifetime instead and shut it down in psi_cgroup_free(). Nothing can arm it
by then. timer_shutdown_sync() because the timer is never armed again. |
| In the Linux kernel, the following vulnerability has been resolved:
thunderbolt: Prevent XDomain delayed work use-after-free on disconnect
tb_xdp_handle_request() runs on system_wq and queues
xd->state_work via queue_delayed_work() in three request handlers:
PROPERTIES_CHANGED_REQUEST, UUID_REQUEST (via start_handshake),
and LINK_STATE_CHANGE_REQUEST. Similarly, update_xdomain() queues
xd->properties_changed_work when local properties change.
Concurrently, tb_xdomain_remove() calls stop_handshake() which does
cancel_delayed_work_sync() on both delayed works. Later,
tb_xdomain_unregister() calls device_unregister() which eventually
frees the xdomain. Since commit 559c1e1e0134 ("thunderbolt: Run
tb_xdp_handle_request() in system workqueue") moved the request
handler off tb->wq, the handler and the remove path are no longer
serialized. If queue_delayed_work() executes after
cancel_delayed_work_sync() but before the xdomain is freed, the
delayed work fires on a freed object.
Add xd->removing that tb_xdomain_remove() sets under xd->lock
before calling stop_handshake(). Each external queue site holds
the same lock and checks removing before calling
queue_delayed_work(). This provides the mutual exclusion needed:
either the queue site acquires the lock first and queues work that
the subsequent cancel will see, or the remove path acquires the
lock first and the queue site observes removing == true and skips
the queue. |
| In the Linux kernel, the following vulnerability has been resolved:
qede: sync udp_tunnel ports outside qede_lock in the recovery path
A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports
configured wedges the rtnetlink control plane of the whole machine:
NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms
[qede_tx_timeout:586(ens6f1)]TX timeout on queue 2!
[qede_recovery_handler:2665(ens6f0)]Starting a recovery process
The recovery path deadlocks on the driver's own mutex:
qede_sp_task
rtnl_lock()
mutex_lock(&edev->qede_lock) <- taken
qede_recovery_handler
qede_load
udp_tunnel_nic_reset_ntf
__udp_tunnel_nic_device_sync
info->sync_table == qede_udp_tunnel_sync
mutex_lock(&edev->qede_lock) <- same task: deadlock
The mutex is not recursive, so the kworker blocks on itself with
rtnl_lock held, and neither lock is ever released. Every task that
calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6
addrconf, sshd) blocks forever while the node still answers ping.
In a vmcore from an affected production node rtnl_mutex.owner
decodes to the very kworker blocked at the innermost mutex_lock()
above.
Re-sync the tunnel ports from qede_sp_task() after the internal lock
is dropped, still under rtnl_lock as the udp_tunnel API requires.
This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf()
under rtnl without the internal lock.
qede_recovery_handler() now returns whether it has successfully
reloaded an open device, and the caller re-syncs the ports only in
that case. This keeps the old gating exactly: a device that was down
or a failed recovery returns false, as those paths never reached the
udp_tunnel_nic_reset_ntf() call before either.
This was the only user of the qede_lock()/qede_unlock() helpers, so
remove them. |
| In the Linux kernel, the following vulnerability has been resolved:
mm/hugetlb: fix list corruption in allocate_file_region_entries()
allocate_file_region_entries() tops up resv->region_cache with freshly
allocated file_region descriptors. The allocation uses GFP_KERNEL, so
resv->lock is dropped around it: the new entries are gathered on a
stack-local list head, allocated_regions, and spliced into
resv->region_cache once the lock is re-acquired.
The splice used list_splice(), which moves the entries but does not
re-initialize the source head, so allocated_regions is left pointing at an
entry that now lives on resv->region_cache. The top-up runs in a while
loop that re-checks the cache deficit after re-acquiring the lock. For a
shared mapping the resv_map is shared by every mapper of the hugetlbfs
inode, so a concurrent region_chg()/region_add()/region_del() on the same
resv_map can consume cache entries during the unlocked window and force a
second iteration. That iteration calls list_add() on the stale head and
corrupts the list; with CONFIG_DEBUG_LIST the __list_add_valid() check
trips:
list_add corruption. next->prev should be prev (ffffc900011ff7f8),
but was ffff88814c281460. (next=ffff88814c545640).
kernel BUG at lib/list_debug.c:31!
allocate_file_region_entries+0x191/0x420
region_chg+0x267/0x300
hugetlb_reserve_pages+0x387/0xc80
hugetlbfs_file_mmap+0x2ce/0x3f0
mmap_region+0x1348/0x1a80
do_mmap+0x85e/0xb90
vm_mmap_pgoff+0x18c/0x330
ksys_mmap_pgoff+0x2a1/0x3e0
do_syscall_64+0xd7/0x420
Without CONFIG_DEBUG_LIST the bad list_add() silently links a kernel-stack
address into resv->region_cache, leading to later use-after-free.
This was observed as a real host panic on a dense KVM host where a QEMU
guest-RAM hugetlbfs file was mapped MAP_SHARED by both QEMU and a separate
SPDK/DPDK vhost-user target, generating concurrent region_* traffic on one
shared resv_map.
Use list_splice_init() so the source head is re-initialized empty after
each splice, making the retry loop safe. |
| In the Linux kernel, the following vulnerability has been resolved:
wifi: mac80211: fix tid_tx use-after-free on BA session stop
ieee80211_stop_tx_ba_cb() hands tid_tx to kfree_rcu() through
ieee80211_remove_tid_tx(), and then reads tid_tx->ndp after dropping
sta->lock:
ieee80211_remove_tid_tx(sta, tid); /* kfree_rcu(tid_tx, rcu_head) */
...
spin_unlock_bh(&sta->lock);
if (start_txq)
ieee80211_agg_start_txq(sta, tid, false);
if (send_delba)
ieee80211_send_delba(..., tid_tx->ndp);
That read is not covered by an RCU read-side critical section, and it runs
in preemptible process context: both callers hold the wiphy mutex, reaching
it either from the ieee80211_ba_session_work() wiphy work or from
ieee80211_sta_tear_down_BA_sessions() during station teardown.
Softirqs can run in that window too, both from the local_bh_enable() that
ends ieee80211_agg_start_txq() and from any interrupt exit, so the RCU
callback can free tid_tx before the read.
Driving the function from a test module with the grace period forced into
that window, KASAN reports the read, and the free arrives on the ordinary
RCU softirq path:
BUG: KASAN: slab-use-after-free in ieee80211_stop_tx_ba_cb+0x3cd/0x400
Read of size 1 at addr ffff888002b9f52e by task kworker/0:1/10
[...]
Freed by task 57:
__kasan_slab_free+0x47/0x70
__rcu_free_sheaf_prepare+0x70/0x250
rcu_free_sheaf_nobarn+0x18/0x40
rcu_core+0x426/0x1310
handle_softirqs+0x144/0x590
__irq_exit_rcu+0xea/0x150
irq_exit_rcu+0x9/0x20
sysvec_apic_timer_interrupt+0x6b/0x80
asm_sysvec_apic_timer_interrupt+0x1a/0x20
send_delba is only set when tx_stop is set, which happens for
AGG_STOP_LOCAL_REQUEST alone, so this is reached on local teardown -
session idle timeout, PTK rekey, suspend, HW reconfig - and not from a
peer's DELBA.
Read ndp into a local before the session is freed, while sta->lock is still
held. tid_tx->ndp has a single writer, in
ieee80211_tx_ba_session_handle_start(), which cannot run concurrently here:
both paths are serialised by the wiphy mutex, and the session is already
marked HT_AGG_STATE_STOPPING at this point. tid_tx->ndp is also the only
tid_tx dereference left after ieee80211_remove_tid_tx() in this function.
[move/change the comment a bit to be more general not just on ndp,
initialize ndp directly] |
| In the Linux kernel, the following vulnerability has been resolved:
net: pktgen: fix proc entry use-after-free
pktgen_change_name() replaces pkt_dev->entry while holding t->if_lock.
pktgen_remove_device() removes the same entry before
_rem_dev_from_if_list() takes that lock.
This allows the following interleaving:
CPU 0 (NETDEV_CHANGENAME) CPU 1 (kpktgend)
if_lock(t)
proc_remove(pkt_dev->entry)
proc_remove(pkt_dev->entry)
pkt_dev->entry = proc_create_data(...)
if_unlock(t)
The kthread can pass the stale proc_dir_entry to proc_remove() after the
rename path has freed it. A reproducer with a widened race window reports:
BUG: KASAN: slab-use-after-free in proc_remove+0x78/0x80
Read of size 8 at addr ffff8881478fea70 by task kpktgend_0/67
Call Trace:
proc_remove+0x78/0x80
pktgen_remove_device.isra.0+0x11c/0x4c0
pktgen_thread_worker+0x1214/0x6bc0
kthread+0x2c6/0x3b0
Allocated by task 95:
__proc_create+0x204/0x790
proc_create_data+0x72/0xe0
pktgen_thread_write+0xd61/0x1510
Freed by task 28:
kmem_cache_free+0xcb/0x3d0
proc_free_inode+0x5b/0x80
rcu_core+0x50a/0x1850
The buggy address belongs to the object at ffff8881478fea00
which belongs to the cache proc_dir_entry of size 192
Move proc_remove() into the if_lock-protected list removal helper. Keep it
before list_del_rcu() to preserve the ordering required by add_device().
The rename path must then finish replacing the entry before removal, or
it observes that the device is no longer on the list. |
| In the Linux kernel, the following vulnerability has been resolved:
vxlan: use neigh_ha_snapshot() in route_shortcircuit()
The neighbour hardware address n->ha can be updated asynchronously by the
neighbour subsystem, protected by n->ha_lock seqlock. Reading n->ha without
holding the seqlock loop can lead to torn reads or reading a partially updated
MAC address.
Use neigh_ha_snapshot() in route_shortcircuit() to safely copy n->ha under
read_seqbegin()/read_seqretry() lock protection before using it.
Note that arp_reduce() and neigh_reduce() seem to have the same issue
left for future patches. |
| In the Linux kernel, the following vulnerability has been resolved:
drm/vc4: Zero the tile state data array before each BIN job
The binner BO is a single 16MB buffer split into 512KB slots that are
handed out to jobs at submission time and recycled as jobs complete,
without ever being cleared. Each slot holds the job's Tile State Data
Array (TSDA) at its start, followed by the tile allocation pool.
While the tile allocation pool is only walked by the render thread
through branches the binner generated during the current job, the
TSDA is the PTB's own per-tile bookkeeping and is consumed by the
hardware itself. Although the kernel sets the "Auto-initialise Tile
State Data Array" flag in the tile binning mode configuration, the
PTB demonstrably still acts on stale tile state left by the slot's
previous user: the binner ends up creating invalid command streams
with invalid primitive streams and branches, which can cause GPU hangs
as observed in [1][2].
Zero the TSDA when the job's binning slot is configured. This clears
48 bytes per tile (~24KB for a 1080p frame) in the submission path, and
guarantees the PTB never sees another job's tile state.
The tile count is only checked for being non-zero today, so the 8-bit
fields it comes from can describe a tile state array almost six times
larger than the slot it has to live in. Bound it before the slot is
handed out, since such size decides how much of the slot is left for
the tile alloc pool. |
| In the Linux kernel, the following vulnerability has been resolved:
drm/xe: Wait on external BO kernel fences in exec IOCTL
Before arming a user job, xe_exec_ioctl() only added the VM's
dma-resv KERNEL slot as a dependency. That slot covers rebinds and
the kernel operations of the VM's private BOs, but not external BOs
(bo->vm == NULL), which carry their kernel operations (evictions,
moves, ...) in their own dma-resv KERNEL slot.
The DMA_RESV_USAGE_KERNEL slot is the cross-driver contract for
memory management operations that must complete before the BO or its
backing store may be used: any accessor is required to wait on the
KERNEL fences before touching the resv. By skipping the external BOs'
KERNEL slots, the exec path violated that contract and could schedule
a user job while a kernel operation on an external BO mapped by the VM
was still in flight, racing against it and potentially reading or
writing memory that was being moved.
Replace the VM-only dependency with an iteration over every object
locked by the exec, adding each object's KERNEL slot as a job
dependency. This covers the VM resv (rebinds and private BOs) as well
as every external BO, mirroring the drm_gpuvm_resv_add_fence() call
that later publishes the job fence to the same set of objects.
Long-running mode continues to skip this, as before.
(cherry picked from commit a6b842acf3ddd1efc53a56de9260cfa718fb35e7) |
| In the Linux kernel, the following vulnerability has been resolved:
iommu/vt-d: Clear Present bit before tearing down scalable-mode context entry
device_pasid_table_teardown() zeroes the 128-bit scalable-mode context
entry with context_clear_entry() while the Present bit is still set. This
creates a window where the hardware can fetch a torn entry, with some
fields already zeroed while Present is still set, leading to unpredictable
behavior or spurious faults. The context-cache invalidation is issued only
after the entry has been zeroed, and intel_pasid_free_table() then frees
the PASID directory pages, so the IOMMU can keep walking a stale Present=1
entry that points at freed memory.
While x86 provides strong write ordering, the compiler may reorder the two
64-bit writes to the entry, and the hardware fetch is not guaranteed to be
atomic with respect to multiple CPU writes.
Commit c1e4f1dccbe9d ("iommu/vt-d: Clear Present bit before tearing down
context entry") fixed this exact pattern in domain_context_clear_one() and
the copied-context path, but device_pasid_table_teardown() was not
converted.
Align it with the "Guidance to Software for Invalidations" in the VT-d
spec, Section 6.5.3.3, using the same ownership handshake as the sibling
fix: clear only the Present bit, flush it to the IOMMU, perform the
context-cache invalidation, and only then zero the rest of the entry. |
| In the Linux kernel, the following vulnerability has been resolved:
ipv6: addrconf: bail out of dad_failure when state is no longer POSTDAD
addrconf_dad_failure() transitions ifp->state from DAD to POSTDAD
via addrconf_dad_end(), which drops ifp->lock on return. The lock
is re-acquired after net_info_ratelimited(). A concurrent
ipv6_del_addr() can take the lock in that window, set ifp->state
to DEAD and run list_del_rcu(&ifp->if_list).
addrconf_dad_failure() then overwrites DEAD with ERRDAD at errdad:
and schedules a new dad_work. The work calls ipv6_del_addr()
again, hitting the already-poisoned list entry:
general protection fault: 0000 [#1] SMP NOPTI
CPU: 4 PID: 217 Comm: kworker/4:1
Workqueue: ipv6_addrconf addrconf_dad_work
RIP: 0010:ipv6_del_addr+0xe9/0x280
RAX: dead000000000122
Call Trace:
addrconf_dad_stop+0x113/0x140
addrconf_dad_work+0x28c/0x430
process_one_work+0x1eb/0x3b0
worker_thread+0x4d/0x400
kthread+0x104/0x140
ret_from_fork+0x35/0x40
Fold the addrconf_dad_end() logic into addrconf_dad_failure() under
a single ifp->lock critical section. The STABLE_PRIVACY branch
temporarily drops ifp->lock around address regeneration, so at
lock_errdad: verify the state is still POSTDAD before transitioning
to ERRDAD; bail out otherwise to avoid overwriting a state set by
another path while the lock was released. |
| In the Linux kernel, the following vulnerability has been resolved:
ALSA: seq: midi: Serialize output teardown with event_input
event_process_midi() borrows msynth->output_rfile.output and then
passes the substream to dump_midi() and snd_rawmidi_kernel_write()
without synchronizing with the output open/close transition.
midisynth_use() also publishes output_rfile before
snd_rawmidi_output_params() has finished.
The last midisynth_unuse() can therefore release the same rawmidi file
and free substream->runtime before snd_rawmidi_kernel_write1() takes
its runtime buffer reference. That leaves the event_input path using a
stale substream or runtime and can end in a NULL-deref or use-after-free.
Fix this with two pieces of synchronization. Keep a short IRQ-safe
spinlock only for publishing or clearing output_rfile and for pairing
the output snapshot with an snd_use_lock_t reference. Once
event_process_midi() has taken that in-flight reference, it drops the
spinlock before calling snd_seq_dump_var_event(), dump_midi(), or
snd_rawmidi_kernel_write(). midisynth_unuse() now detaches the visible
rawmidi file under the same spinlock, waits for the in-flight writers
to drain, and only then drains and releases the saved file.
midisynth_use() likewise opens into a local snd_rawmidi_file and
publishes it only after snd_rawmidi_output_params() succeeds.
The buggy scenario involves two paths, with each column showing the
order within that path:
event_input path: last unuse path:
1. event_process_midi() snapshots 1. midisynth_unuse() starts
output_rfile.output. tearing down output_rfile.
2. dump_midi() reaches 2. snd_rawmidi_kernel_release()
snd_rawmidi_kernel_write() closes the output file.
before runtime is pinned. 3. close_substream() frees
3. The callback keeps using substream->runtime.
the borrowed substream.
Validation reproduced this kernel report:
KASAN null-ptr-deref in snd_rawmidi_kernel_write1+0x56/0x360
RIP: 0033:0x7fde7dd0837f
RIP: 0010:snd_rawmidi_kernel_write1+0x56/0x360 |