Skip to contentSkip to conversation

6.12.115-rc1 review

PATCH In review Greg Kroah-Hartman 15 participants 264 messages Oct 28 – Oct 30 Stable branch Page 1 of 2

lore
Share & tools
lore

Greg Kroah-Hartman

author maintainer
#0 lore raw

cover 6.12.115-rc1 review

This is the start of the stable review cycle for the 6.12.115 release. There are 247 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.12.115-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.12.y
and the diffstat can be found below.

thanks,

greg k-h

-------------
Pseudo-Shortlog of commits:

Greg Kroah-Hartman <[email protected]>

    Linux 6.12.115-rc1

Jan Kara <[email protected]>
fanotify: Fix crash in fanotify_init(2)

Christian Brauner <[email protected]>
signal: allow taks to temporarily block TIF_NOTIFY_SIGNAL

Christian Brauner <[email protected]>
pid_namespace: prevent TIF_NOTIFY_SIGNAL from interrupting the reaper

Christian Brauner <[email protected]>
fork: release the files of a failed fork after sched_cancel_fork()

Christian Brauner <[email protected]>
ptrace: refuse to change the signal mask of a user worker

Christian Brauner <[email protected]>
signal: don't retarget shared signals in a dying thread group

Christian Brauner <[email protected]>
signal: only SIGKILL and the freezers interrupt a coredumping task

Christian Brauner <[email protected]>
coredump: hold RCU while releasing parked threads

Aaron Ahmed <[email protected]>
ftrace: Initialize modname and offset in print_rec()

Weiming Shi <[email protected]>
asymmetric_keys: reject trust keys without IDs

Srish Srinivasan <[email protected]>
keys/trusted_keys: return immediately after TPM unseal failure

Stefano Garzarella <[email protected]>
KEYS: trusted: Fix blob allocation size in tpm2_key_decode()

Adrian Martinez <[email protected]>
KEYS: Fix key_reject_and_link() race with keyring restriction

Cen Zhang <[email protected]>
keys: Protect the read method check against type removal

Kyle Zeng <[email protected]>
keys: make keyring indexes independent of kernel addresses

Cen Zhang <[email protected]>
keys: Protect the type name while describing a key

Cen Zhang <[email protected]>
keys: Check the authorization payload in process keyring searches

Cen Zhang <[email protected]>
keys: Avoid the owner account dereference in named keyring lookup

Junyeong Lee <[email protected]>
io_uring/waitid: prevent cancel reference wrap

Austin Schuh <[email protected]>
io_uring/wait: wake real-time waiters from hard interrupt context

Tamaki Yanagawa <[email protected]>
io_uring/net: keep recv buffer select length within MAX_RW_COUNT

Jens Axboe <[email protected]>
io_uring/poll: don't loop on HUP with multiple references

Jens Axboe <[email protected]>
io_uring/timeout: don't lose a cancel as io-wq arms a timeout

Jens Axboe <[email protected]>
io_uring/timeout: don't flush with the sequence behind the last flush

Jens Axboe <[email protected]>
io_uring/timeout: don't put a sequence target behind the last flush

Juanlu Herrero <[email protected]>
io_uring/poll: avoid retry wakeup loss when dropping ownership

Josef Bacik <[email protected]>
io_uring: look up the file before queueing to io-wq

Josef Bacik <[email protected]>
io_uring/timeout: don't end a linked timeout with 1

Josef Bacik <[email protected]>
io_uring/timeout: no ABS update of multishot timeouts

Josef Bacik <[email protected]>
io_uring/net: don't retry recv on uncommitted buffers

Josef Bacik <[email protected]>
io_uring: don't pass restart error codes to userspace

Josef Bacik <[email protected]>
io_uring/sqpoll: let the thread sleep on disabled rings

Josef Bacik <[email protected]>
io_uring/kbuf: handle index wrap in last buffer check

Josef Bacik <[email protected]>
io_uring/napi: don't busy poll past an expired timeout

Josef Bacik <[email protected]>
io_uring/napi: make stale entries time out after 60s

Josef Bacik <[email protected]>
io_uring/wait: no early -ETIME if min_wait finds work

Josef Bacik <[email protected]>
io_uring/timeout: keep repeat count on multishot update

Josef Bacik <[email protected]>
io_uring/timeout: don't skip over reached sequences

Josef Bacik <[email protected]>
io_uring/poll: drop stale double poll entry on rearm

Josef Bacik <[email protected]>
io_uring/wait: look at all CQ waiters on wake up

Josef Bacik <[email protected]>
io_uring: wake ring pollers for local task_work

Josef Bacik <[email protected]>
io_uring/cancel: let CANCEL_ALL find parked requests

Josef Bacik <[email protected]>
io_uring/cancel: recognize "no timeout" on 32-bit

Josef Bacik <[email protected]>
io_uring/cancel: don't forget that the work was running

Josef Bacik <[email protected]>
io_uring: don't lose a cancel as io-wq parks a request

Josef Bacik <[email protected]>
io_uring/poll: don't lose a cancel as io-wq arms poll

Josef Bacik <[email protected]>
io_uring/io-wq: let cancel find picked up hashed work

Josef Bacik <[email protected]>
io_uring/io-wq: don't exit with a creation pending

Josef Bacik <[email protected]>
io_uring/io-wq: don't strand work when a worker exits

Josef Bacik <[email protected]>
io_uring: don't flood a cancelling task with task_work

Josef Bacik <[email protected]>
io_uring/io-wq: don't hide a link from cancellations

Josef Bacik <[email protected]>
io_uring/io-wq: don't lose the signal that ends work

Josef Bacik <[email protected]>
io_uring/wait: don't spin on a stuck overflow list

Josef Bacik <[email protected]>

    io_uring/cmd: don't leave list poison in ->hash_node

Josef Bacik <[email protected]>

    io_uring/waitid: own the request before clearing ->head

Christian Brauner <[email protected]>
io-wq: order the exit bit against worker creation task work

Lee Jones <[email protected]>
tracing/histograms: Verify event_call identity when updating synth_event refcount

Donggeun Yoo <[email protected]>
tracing: Fix 64-bit and signed histogram fields on 32-bit kernels

Donggeun Yoo <[email protected]>
tracing: Unregister field variable histograms when the command fails

Donggeun Yoo <[email protected]>
tracing: Save the event file instead of the compatible histogram

Kyle Zeng <[email protected]>
tracing: Reject incomplete event filter expressions

Pauli Virtanen <[email protected]>
Bluetooth: L2CAP: refuse __l2cap_chan_add if chan already has conn

Jonas Köppeler <[email protected]>
net_sched: codel: fix stale state for empty flows in fq_codel

Sasha Levin <[email protected]>
Revert "srcu: Use irq_work to start GP in tiny SRCU"

Damien Le Moal <[email protected]>
block: avoid potential deadlock on zone revalidation failure

Hyunwoo Kim <[email protected]>

    mm/pagewalk: fix stale walk->action escaping walk_pmd_range()

Bernard Pidoux <[email protected]>
ax25: don't free an incoming SABM skb left on the listener's queue

Bernard Pidoux <[email protected]>
rose: bring up the link of the preferred route passed over by a connect request

Bernard Pidoux <[email protected]>
ax25: send unacknowledged I frames again when a link is re-established

Bernard Pidoux <[email protected]>
rose: clear the circuits of a neighbour that restarts the link

Bernard Pidoux <[email protected]>
ax25: send a private copy of each I frame

Bernard Pidoux <[email protected]>
ax25: don't accept frames on a device that is going down

Bernard Pidoux <[email protected]>
ax25: wait for ax25_send_frame() before detaching the connections of a device

Bernard Pidoux <[email protected]>
ax25: read the connection's ax25_dev only once when walking ax25_list

Bernard Pidoux <[email protected]>
ax25: don't create two connections for the same link in ax25_send_frame()

Bernard Pidoux <[email protected]>
rose: process received frames and send with the socket locked

Bernard Pidoux <[email protected]>
ax25: serialise the transmission of I frames on a connection

Bernard Pidoux <[email protected]>
rose: restart the link of a neighbour created over a live AX.25 connection

Bernard Pidoux <[email protected]>
rose: clear through routes when a neighbour is removed from the tables

Bernard Pidoux <[email protected]>
netrom: fix nr_node leak when nr_del_node() removes the last route

Bernard Pidoux <[email protected]>

    netrom: clear nr_neigh->ax25 under the routing lock in nr_link_failed()

Junjie Cao <[email protected]>
netrom: fix reference count leak in nr_rt_device_down

Bernard Pidoux <[email protected]>
netrom: look up and update routes in one critical section

Bernard Pidoux <[email protected]>
netrom: use a single lock for the node and neighbour lists

Mashiro Chen <[email protected]>
net: ax25: fix integer overflow in ax25_rx_fragment()

Bernard Pidoux <[email protected]>
ax25: check frame length in ax25_decode() and ax25_rx_iframe()

Ashutosh Desai <[email protected]>
ax25: fix OOB read after address header strip in ax25_rcv()

Bernard Pidoux <[email protected]>
netrom: bound fragment reassembly in nr_queue_rx_frame()

Mashiro Chen <[email protected]>
net: netrom: validate source address in nr_find_socket()

Greg Kroah-Hartman <[email protected]>
netrom: require full NET/ROM headers in nr_route_frame()

Stanislav Fort <[email protected]>
netrom: linearize and validate lengths in nr_rx_frame()

Bernard Pidoux <[email protected]>
rose: clear stale N(R)/N(S) before (re)transmitting a data frame

Yun Zhou <[email protected]>
xfs: don't hold buffer locks across sync transaction commit in xfs_sync_sb_buf

Frank Sorenson <[email protected]>
cifs: add revalidation on FSCTL failure in smb2_duplicate_extents()

Tejun Heo <[email protected]>
sched_ext: Fix stall when a task enqueued with SCX_ENQ_LAST lands back on its CPU's local DSQ

Jesse Zhang <[email protected]>
drm/amdgpu: free the userptr ranges when a CS fails after parser_bos

Dmitry Kandybka <[email protected]>
tracing: Fix ignored return value of resize_buffer_duplicate_size()

Dibin Moolakadan Subrahmanian <[email protected]>
drm/i915/dmc: Harden DMC firmware payload parsing

Jann Horn <[email protected]>
futex: Fix mm reuse handling for FUT_OFF_MMSHARED

Wentao Liang <[email protected]>
i2c: viai2c-wmt: Fix clock leak in wmt_i2c_probe()

Enrico Zanda <[email protected]>
i2c: viai2c-wmt: Replace dev_err() with dev_err_probe() in probe function

Yao Kai <[email protected]>
workqueue: Fix unbound pool lifetime for pending pwqs

Breno Leitao <[email protected]>
workqueue: release pwq pools by pool type

Ido Schimmel <[email protected]>
ipv4: udp: Create exceptions before socket matching

Daehyeon Ko <[email protected]>
vsock/virtio: validate packet source for connected sockets

Bobby Eshleman <[email protected]>
virtio: set skb owner of virtio_transport_reset_no_sock() reply

Cen Zhang (Microsoft Security FORGE Labs) <[email protected]>
io_uring/cancel: don't spin on local work the task can't run

Taeyang Lee <[email protected]>
tracing: Keep the predicate limit separate from program capacity

Thomas Hellström <[email protected]>
drm/xe: Bump LRU for external objects on exec and rebind

Peter Ujfalusi <[email protected]>
ASoC: SOF: ipc4-topology: Fix shift-out-of-bounds for aggregated ALH capture

Christian Brauner <[email protected]>
ext4: free the sbi after a grace period

Christian Brauner <[email protected]>
ntfs3: free the sbi after a grace period

Cen Zhang (Microsoft) <[email protected]>
watch_queue: Fix note pool publication race in watch_queue_set_size()

K Prateek Nayak <[email protected]>
kernel/watch_queue: Use pipe_buf() to retrieve the pipe buffer

Breno Leitao <[email protected]>
net: call getsockopt_iter if available

Joseph Qi <[email protected]>
eventfs: fix SRCU read lock leak in eventfs_iterate()

Lorenzo Stoakes (ARM) <[email protected]>
perf: Track the perf mlock gift separately

Laurence Oberman <[email protected]>
scsi: st: Fix use-after-free race between st_remove() and st_open()

Paulo Alcantara <[email protected]>
smb: client: fix multiuser mount with krb5

Frank Sorenson <[email protected]>
smb: client: fix heap overflow in cifs_do_set_acl()

Aohan Mei <[email protected]>
net/sched: cls_flower: validate mask pointer after nla_next()

Aleksandr Khromov <[email protected]>
ksmbd: zero the FS_OBJECT_ID_INFORMATION buffer before filling it in

Aleksandr Khromov <[email protected]>
ksmbd: initialize FileSystemControlFlags in FS_CONTROL_INFORMATION

Aleksandr Khromov <[email protected]>
ksmbd: fill in FileSysIdentifier in FS_POSIX_INFORMATION

Jun Yang <[email protected]>
net: bridge: mcast: don't truncate the port group walk on teardown

Al Viro <[email protected]>
fs: get rid of __FMODE_NONOTIFY kludge

Hyunwoo Kim <[email protected]>
KVM: x86/mmu: Preserve nested TDP shadow page tables if they are used as roots

Lorenzo Stoakes (ARM) <[email protected]>
mm/huge_memory: bypass THP tuneables for huge pfnmap mappings

Florian Westphal <[email protected]>
netfilter: nft_byteorder: remove multi-register support

Sasha Levin <[email protected]>
Revert "s390/mm: Add missing secure storage access fixups for donated memory"

Matt Fleming <[email protected]>
mm/huge_memory: initialise workingset state before folio split

Kairui Song <[email protected]>
mm, madvise: fix potential workingset node list_lru leaks

David Rosca <[email protected]>
drm/amdgpu: Fix VCE 3 ring align_mask

Zihan Xi <[email protected]>
ipv4: avoid divide by zero in fib_rebalance

Niklas Schnelle <[email protected]>
s390/pci: Fix leak of uninitialized kernel data in SCLP report

Aswin Karuvally <[email protected]>
s390/ctcm: Prevent XID null dereference

Florian Schauer <[email protected]>
page_pool: keep frag_offset aligned for odd-sized requests

Charles Vosburgh <[email protected]>
sctp: validate chunk length in the inqueue parser

Chengfeng Ye <[email protected]>
tipc: protect node reset trace dump with node lock

Zqiang <[email protected]>
workqueue: Use raise_softirq() to trigger softirq in irq_work handler

Carlos Maiolino <[email protected]>
xfs: fix capability check in xfs

Bryam Vargas <[email protected]>
net/iucv: fix the recvmsg window update

Kaiwen Shi <[email protected]>

    mac802154: fix data race and NULL deref on local->assoc_dev

Chen Cheng <[email protected]>
dm-integrity: require stable writes for internal hash modes

Ben Cressey <[email protected]>
dm-integrity: fix infinite loop on discard with large tag size

Genjian Zhang <[email protected]>
dm-ebs: fix incorrect device offset check in ebs_ctr()

Ben Cressey <[email protected]>
dm-crypt: fix a tiny race condition in crypt_dec_pending

Benjamin Marzinski <[email protected]>
dm cache: fix issue with background work locking

Ming-Hung Tsai <[email protected]>
dm cache: fix demotion stats in passthrough mode

Etienne Perot <[email protected]>
cgroup: fix spurious SIGKILL of CLONE_INTO_CGROUP children

Cen Zhang (Microsoft Security FORGE Labs) <[email protected]>
tcp: fix use-after-free in do_tcp_getsockopt(TCP_CC_INFO)

Cen Zhang (Microsoft Security FORGE Labs) <[email protected]>
tcp: fix use-after-free in do_tcp_getsockopt(TCP_CONGESTION)

Heyang Tan <[email protected]>
wifi: p54: validate firmware record lengths

Tengda Wu <[email protected]>
tracing: Fix hist trigger timestamps for buffered events

Amit Machhiwal <[email protected]>
tracing: Do not initialize tracefs work if tracing is disabled

Taeyang Lee <[email protected]>
tracing: Check operator stack capacity before pushing

David Carlier <[email protected]>
tracing: Take trace_types_lock when dropping the buffer_meta reference

Donggeun Yoo <[email protected]>
tracing: Fix the shift out of bounds in the log2 histogram modifier

Christian König <[email protected]>
drm/exec: don't immediately add prelocked obj to array of locked objs

Jesse Zhang <[email protected]>
drm/amdgpu: don't drop the runtime PM reference twice in amdgpu_gpr

Timur Kristóf <[email protected]>
drm/amdgpu/sdma3: Work around POLL_REGMEM / FENCE hang

Yehyeong Lee <[email protected]>
scsi: libiscsi: Validate the task named by a management response

Yehyeong Lee <[email protected]>
scsi: libiscsi: Validate the ITT reflected in a Reject PDU

Yehyeong Lee <[email protected]>
scsi: libiscsi: Reject a negative task index from parse_pdu_itt

Yehyeong Lee <[email protected]>
scsi: xen/scsifront: Check for a NULL shadow entry on a backend response

Stanley Jhu <[email protected]>
scsi: bsg: Pass reply_len to bsg_setup_queue() to fit UFS ARPMB replies

Krystian Kaniewski <[email protected]>
ring-buffer: Fix false warning in ring_buffer_map_get_reader()

Donggeun Yoo <[email protected]>
ring-buffer: Fix the sub-buffer array base in rb_meta_subbuf_idx()

David Carlier <[email protected]>
ring-buffer: Do not show buffer_meta for CPUs without a buffer

Antoine Huret <[email protected]>
perf: Update cgroup time when rotating multiplexed events

Loic Poulain <[email protected]>
i2c: qcom-cci: Fix HALT_ACK reset request being dropped

Lee Jones <[email protected]>
tracing/histograms: Reject non-stacktrace fields for synthetic stacktrace fields

Sasha Levin <[email protected]>
tools: gpio: fix building from the top level with O=

Daehyeon Ko <[email protected]>
sctp: revalidate output stream after association connect wait

Mario Limonciello <[email protected]>
pinctrl: amd: Clear S4 wake bits when firmware has _AEI

Aohan Mei <[email protected]>
net: rds: fix uninitialized trans dereference in CM event handler

Jérémy Jean <[email protected]>
net/sched: fix use-after-free in __tcf_action_put()

Christian Brauner <[email protected]>
namei: test for a negative dentry the way traverse_mounts() does

Christian Brauner <[email protected]>
eventfs: Free the eventfs_inode after an rcu grace period too

Aviv Vaknin <[email protected]>
pid_namespace: make zap_pid_ns_processes() freezable

Kyle Zeng <[email protected]>
fsnotify: prevent mark additions during connector teardown

Amirmohammad Eftekhar <[email protected]>
KVM: nVMX: Reset MMU back to the root MMU if loading L2 state fails for !VM-Entry

EJ Campbell <[email protected]>
x86/kvm: Don't consume async #PF flags on a kernel #PF in NMI context

Christian Brauner <[email protected]>
unicode: free the map after a grace period

Sung Byeongchan <[email protected]>
autofs: ignore notification errors from a replaced pipe

Christian Brauner <[email protected]>
afs: free the super info after a grace period

Alexander Gruhlke <[email protected]>
PCI: Don't treat ~0 as ready in pci_dev_wait() with RRS SV

John Ogness <[email protected]>
printk: Postpone kthread startup

Haris Okanovic <[email protected]>
perf: Fix perf_pmu_migrate_context() leaving events behind

Peng Fan <[email protected]>
gpio: mxc: fix wakeup_pads bit operations

Alexander Stein <[email protected]>
gpio: mxc: use BIT() macro

Peng Fan <[email protected]>
gpio: mxc: fix both_edges bit operations

Bartosz Golaszewski <[email protected]>
gpio: mxc: use lock guards for the generic GPIO chip lock

Kent Gibson <[email protected]>
gpiolib: fix memory leak in gpiod_set_array_value_complex() on error paths

Joas Antonio dos Santos <[email protected]>
calipso: keep BIG TCP payload_len in calipso_skbuff_setattr()

Alice Mikityanska <[email protected]>
net/ipv6: Introduce payload_len helpers

Nicolai Buchwitz <[email protected]>
net: bcmgenet: unshare the skb before writing the control block

Kyle Zeng <[email protected]>
netlink: avoid hashing the network namespace pointer

Xingui Yang <[email protected]>
scsi: libsas: Fix SMP IO deadlock during HA resume

Martin Kaiser <[email protected]>
tracing: probes: error if BTF is required but unavailable

Kyle Zeng <[email protected]>
pipe: preserve existing fasync readers on writer setup failure

Sasha Levin <[email protected]>
samples: pidfd: fix userprogs typo

Cen Zhang <[email protected]>
ocfs2/dlm: Protect the recovery list during debugfs reads

Linfeng Sun <[email protected]>
scsi: target: iscsi: Fix race in discovery auth updates

Laurence Oberman <[email protected]>
scsi: st: Save the visited block for the partition the drive is in

Keith Busch <[email protected]>
nios2: fix __wsum type degradation in csum_fold()

Carlos Jones Jr <[email protected]>
spi: tegra114: assert CS GPIO for every transfer in a message

Kyle Zeng <[email protected]>
netlink: reset skb headers before unicast and multicast filtering

Michael Reeves <[email protected]>
regmap: Drop map lock during cache init and exit

Francesco Lavra <[email protected]>
regcache: Do not overwrite error code when finalizing cache after error

Andy Shevchenko <[email protected]>

    regcache: Add ->populate() callback to separate from ->init()

HyeongJun An <[email protected]>
ASoC: nau8822: Fix the 128x oversampling switch bits

HyeongJun An <[email protected]>
ASoC: wm8978: Fix the 128x oversampling switch bits

HyeongJun An <[email protected]>
ASoC: max98371: Fix the Speaker DHT Min Volume bits

HyeongJun An <[email protected]>
ASoC: pcm3168a: Fix the DAC zero flag polarity bit

HyeongJun An <[email protected]>
ASoC: cs42l52: Fix the NG Type Switch bit

HyeongJun An <[email protected]>
ASoC: cs42l56: Put the Limiter Cushion control on the limiter register

Avi Weiss <[email protected]>
ASoC: stm32: spdifrx: fix synchronization state check

Sergey Tiraspolsky <[email protected]>
ASoC: Intel: avs: Reset the stream unless it is already prepared

Allison Henderson <[email protected]>
net/rds: don't let the rdma_cm destroy an id RDS still owns on route failure

Michal Luczaj <[email protected]>
vsock: treat TCP_CLOSING as once-established

Dave Stevenson <[email protected]>
drm/vc4: hdmi: Move packet RAM enable to PM runtime

Adriel Santos <[email protected]>
pinctrl: qcom: ipq5018: replace gcc_plltest with pwm2 on gpio12

Svyatoslav Ryhel <[email protected]>
power: supply: generic-adc-battery: Fix temperature IIO channel conversion

Len Brown <[email protected]>
tools/power turbostat: Fix add_counter issue if > 24 counters.

Len Brown <[email protected]>
tools/power turbostat: Fix fd_perf leak on reset

Gilberto Conde <[email protected]>
pinctrl: qcom: ipq5018: add missing pwm3 function on gpio13

Michael Kelley <[email protected]>
Drivers: hv: Don't reset VMBus connection state on error/exit paths

Michael Kelley <[email protected]>
x86/hyperv: Avoid using per-cpu output page in hv_apicid_to_vp_index()

Waiman Long <[email protected]>
Drivers: hv: Avoid infinite retry loop in init_vp_index()

Xueqin Luo <[email protected]>
power: supply: ds2780: Fix ds2780_get_capacity() returning raw value

Karl Mehltretter <[email protected]>
power: supply: Fix maintenance charge table kernel-doc

Jinhui Guo <[email protected]>
soc/tegra: fuse: Add missing newline to APBMISC error message

Matteo Croce <[email protected]>
fat: stop reading directory entries past the end-of-directory marker

Abdul Wasey <[email protected]>
mptcp: move the OoO check back into __mptcp_try_fallback()

Bernard Pidoux <[email protected]>

    rose: purge rose_neigh->queue on final teardown

Bernard Pidoux <[email protected]>
ax25: bound REJ-driven fast retransmits with the existing N2 counter

Bernard Pidoux <[email protected]>
rose: hold a reference on incoming sockets queued for accept()

Bernard Pidoux <[email protected]>
rose: don't warn on stray CALL_ACCEPTED/CLEAR_CONFIRMATION in state 3

Bernard Pidoux <[email protected]>
rose: use timer_shutdown_sync() for t0timer teardown

Bernard Pidoux <[email protected]>
rose: guard rose_transmit_link() against a NULL neighbour

Bernard Pidoux <[email protected]>
rose: keep the route needed for routing loop detection

Bernard Pidoux <[email protected]>
rose: don't free fd-owned sockets when reaping in the heartbeat

Bernard Pidoux <[email protected]>
rose: clear neighbour pointer in rose_kill_by_device()

Bernard Pidoux <[email protected]>
rose: release netdev ref and destroy orphaned incoming sockets

Bernard Pidoux <[email protected]>
rose: disconnect orphaned STATE_2 sockets when device is gone

Bernard Pidoux <[email protected]>
rose: set SOCK_DESTROY in rose_kill_by_device() for prompt cleanup

-------------

Diffstat:

 Makefile                                           |   4 +-
 arch/nios2/include/asm/checksum.h                  |   2 +-
 arch/s390/mm/fault.c                               |  11 +-
 arch/s390/pci/pci_sysfs.c                          |   3 +
 arch/x86/hyperv/hv_init.c                          |   3 +-
 arch/x86/kernel/kvm.c                              |  32 +-
 arch/x86/kvm/mmu/mmu.c                             |   4 +-
 arch/x86/kvm/vmx/nested.c                          |   4 +-
 block/blk-zoned.c                                  |  37 +-
 block/blk.h                                        |   4 +-
 block/bsg-lib.c                                    |  13 +-
 block/genhd.c                                      |   2 +-
 crypto/asymmetric_keys/restrict.c                  |   8 +-
 drivers/base/regmap/internal.h                     |   1 +
 drivers/base/regmap/regcache-maple.c               |   9 +-
 drivers/base/regmap/regcache.c                     |  18 +-
 drivers/gpio/gpio-mxc.c                            |  69 ++--
 drivers/gpio/gpiolib.c                             |   9 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c             |   6 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c        |  23 +-
 drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c             |  25 +-
 drivers/gpu/drm/amd/amdgpu/vce_v3_0.c              |   2 +-
 drivers/gpu/drm/drm_exec.c                         |  77 +++--
 drivers/gpu/drm/i915/display/intel_dmc.c           |  21 +-
 drivers/gpu/drm/vc4/vc4_hdmi.c                     |  20 +-
 drivers/gpu/drm/xe/xe_exec.c                       |   7 +-
 drivers/gpu/drm/xe/xe_vm.c                         |  31 +-
 drivers/gpu/drm/xe/xe_vm.h                         |   2 +
 drivers/hv/channel_mgmt.c                          |  12 +-
 drivers/hv/connection.c                            |   1 -
 drivers/hv/vmbus_drv.c                             |   1 -
 drivers/i2c/busses/i2c-qcom-cci.c                  |   4 +-
 drivers/i2c/busses/i2c-viai2c-wmt.c                |  22 +-
 drivers/md/dm-cache-target.c                       |  59 ++--
 drivers/md/dm-crypt.c                              |   3 +-
 drivers/md/dm-ebs-target.c                         |   3 +-
 drivers/md/dm-integrity.c                          |   6 +-
 drivers/net/ethernet/broadcom/genet/bcmgenet.c     |  10 +
 drivers/net/wireless/intersil/p54/fwio.c           |  42 ++-
 drivers/pci/pci.c                                  |  15 +-
 drivers/pinctrl/pinctrl-amd.c                      |   9 +
 drivers/pinctrl/qcom/pinctrl-ipq5018.c             |  10 +-
 drivers/power/supply/ds2780_battery.c              |   2 +-
 drivers/power/supply/generic-adc-battery.c         |   9 +-
 drivers/s390/net/ctcm_mpc.c                        |   3 +-
 drivers/scsi/libiscsi.c                            |  14 +-
 drivers/scsi/libsas/sas_expander.c                 |  14 +-
 drivers/scsi/mpi3mr/mpi3mr_app.c                   |   2 +-
 drivers/scsi/scsi_transport_fc.c                   |   6 +-
 drivers/scsi/scsi_transport_iscsi.c                |   2 +-
 drivers/scsi/scsi_transport_sas.c                  |   6 +-
 drivers/scsi/st.c                                  |  18 +-
 drivers/scsi/xen-scsifront.c                       |   1 +
 drivers/soc/tegra/fuse/tegra-apbmisc.c             |   2 +-
 drivers/spi/spi-tegra114.c                         |   7 +
 drivers/target/iscsi/iscsi_target_configfs.c       |  26 +-
 drivers/ufs/core/ufs_bsg.c                         |   2 +-
 fs/afs/internal.h                                  |   2 +
 fs/afs/super.c                                     |   2 +-
 fs/autofs/waitq.c                                  |  25 +-
 fs/coredump.c                                      |  16 +-
 fs/ext4/ext4.h                                     |   2 +
 fs/ext4/extents_status.c                           |   7 +-
 fs/ext4/extents_status.h                           |   1 +
 fs/ext4/super.c                                    |  13 +-
 fs/fat/dir.c                                       |  44 ++-
 fs/fcntl.c                                         |   4 +-
 fs/namei.c                                         |   3 +-
 fs/notify/fanotify/fanotify_user.c                 |  25 +-
 fs/notify/mark.c                                   |  11 +
 fs/ntfs3/ntfs_fs.h                                 |   1 +
 fs/ntfs3/super.c                                   |  29 +-
 fs/ocfs2/dlm/dlmcommon.h                           |   2 +
 fs/ocfs2/dlm/dlmdebug.c                            |   5 +-
 fs/ocfs2/dlm/dlmrecovery.c                         |   2 +-
 fs/open.c                                          |  23 +-
 fs/pipe.c                                          |   6 +-
 fs/smb/client/cifssmb.c                            |  13 +-
 fs/smb/client/connect.c                            |  21 +-
 fs/smb/client/smb2ops.c                            |   7 +-
 fs/smb/server/smb2pdu.c                            |   5 +
 fs/tracefs/event_inode.c                           |  16 +-
 fs/unicode/utf8-core.c                             |  21 +-
 fs/xfs/libxfs/xfs_sb.c                             |  17 +-
 fs/xfs/xfs_fsmap.c                                 |   2 +-
 fs/xfs/xfs_ioctl.c                                 |   2 +-
 fs/xfs/xfs_iops.c                                  |   2 +-
 include/drm/drm_exec.h                             |  15 +-
 include/linux/bsg-lib.h                            |   3 +-
 include/linux/cgroup-defs.h                        |   5 +-
 include/linux/fs.h                                 |   6 +-
 include/linux/fsnotify_backend.h                   |   1 +
 include/linux/futex.h                              |  14 +-
 include/linux/huge_mm.h                            |   2 +-
 include/linux/io_uring_types.h                     |   3 +
 include/linux/ipv6.h                               |  22 ++
 include/linux/key-type.h                           |   1 +
 include/linux/key.h                                |   5 +
 include/linux/maple_tree.h                         |   4 +
 include/linux/mm_types.h                           |   3 +
 include/linux/power_supply.h                       |   6 +-
 include/linux/sched.h                              |   2 +-
 include/linux/sched/signal.h                       |  27 +-
 include/linux/sched/user.h                         |   8 +-
 include/linux/srcutiny.h                           |   4 -
 include/linux/unicode.h                            |   1 +
 include/net/af_vsock.h                             |   3 +
 include/net/ax25.h                                 |   6 +
 include/net/codel_impl.h                           |   1 +
 include/net/ipv6.h                                 |   2 -
 include/net/netfilter/nf_tables_ipv6.h             |   4 +-
 include/net/netrom.h                               |  18 +-
 include/net/rose.h                                 |  45 ++-
 include/uapi/asm-generic/fcntl.h                   |   1 -
 io_uring/cancel.c                                  |  29 +-
 io_uring/cancel.h                                  |   2 +
 io_uring/futex.c                                   |   7 +-
 io_uring/io-wq.c                                   | 110 +++++-
 io_uring/io-wq.h                                   |   4 +-
 io_uring/io_uring.c                                | 167 +++++++--
 io_uring/io_uring.h                                |  44 ++-
 io_uring/kbuf.c                                    |   2 +-
 io_uring/napi.c                                    |   4 +-
 io_uring/net.c                                     |  26 +-
 io_uring/poll.c                                    |  41 ++-
 io_uring/rw.c                                      |  18 -
 io_uring/sqpoll.c                                  |   7 +
 io_uring/tctx.c                                    |   1 +
 io_uring/timeout.c                                 |  91 +++--
 io_uring/uring_cmd.c                               |   2 +-
 io_uring/waitid.c                                  |  19 +-
 kernel/cgroup/cgroup.c                             |   6 +-
 kernel/events/core.c                               |  33 +-
 kernel/exit.c                                      |   2 +-
 kernel/fork.c                                      |  12 +-
 kernel/futex/core.c                                |  47 +--
 kernel/pid_namespace.c                             |   5 +-
 kernel/printk/printk.c                             |   2 +-
 kernel/ptrace.c                                    |   6 +
 kernel/rcu/srcutiny.c                              |  19 +-
 kernel/sched/ext.c                                 |  18 +-
 kernel/signal.c                                    |   4 +
 kernel/trace/ftrace.c                              |   4 +-
 kernel/trace/ring_buffer.c                         |  13 +-
 kernel/trace/trace.c                               |  13 +-
 kernel/trace/trace_events_filter.c                 |  15 +-
 kernel/trace/trace_events_hist.c                   |  52 +--
 kernel/trace/trace_events_synth.c                  |  10 +
 kernel/trace/trace_probe.c                         |   3 +-
 kernel/trace/trace_synth.h                         |   1 +
 kernel/watch_queue.c                               |  16 +-
 kernel/workqueue.c                                 |  24 +-
 mm/filemap.c                                       |   9 -
 mm/huge_memory.c                                   |  81 +++--
 mm/internal.h                                      |   6 +
 mm/khugepaged.c                                    |   3 +
 mm/pagewalk.c                                      |   6 +-
 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                             |  44 +++
 net/ax25/ax25_subr.c                               |  44 ++-
 net/bluetooth/l2cap_core.c                         |   7 +-
 net/bluetooth/l2cap_sock.c                         |   2 +
 net/bridge/br_multicast.c                          |  13 +-
 net/bridge/br_netfilter_ipv6.c                     |   2 +-
 net/bridge/netfilter/nf_conntrack_bridge.c         |   4 +-
 net/core/page_pool.c                               |   3 +-
 net/ipv4/fib_semantics.c                           |   2 +-
 net/ipv4/tcp.c                                     |  18 +-
 net/ipv4/tcp_cong.c                                |   4 +-
 net/ipv4/tcp_dctcp.c                               |   4 +-
 net/ipv4/tcp_minisocks.c                           |   2 +-
 net/ipv4/tcp_output.c                              |   2 +-
 net/ipv4/udp.c                                     |  11 +-
 net/ipv6/calipso.c                                 |   6 +-
 net/ipv6/ip6_input.c                               |   2 +-
 net/ipv6/ip6_offload.c                             |   7 +-
 net/ipv6/output_core.c                             |   7 +-
 net/iucv/af_iucv.c                                 |  42 ++-
 net/mac802154/ieee802154_i.h                       |   7 +-
 net/mac802154/main.c                               |   1 +
 net/mac802154/scan.c                               |  51 ++-
 net/mptcp/protocol.c                               |  14 +-
 net/netfilter/ipvs/ip_vs_xmit.c                    |   2 +-
 net/netfilter/nf_conntrack_ovs.c                   |   2 +-
 net/netfilter/nf_log_syslog.c                      |   2 +-
 net/netfilter/nft_byteorder.c                      |  51 ++-
 net/netlink/af_netlink.c                           |  10 +-
 net/netrom/af_netrom.c                             |  33 +-
 net/netrom/nr_in.c                                 |  26 ++
 net/netrom/nr_route.c                              | 383 +++++++++++----------
 net/rds/rdma_transport.c                           |  38 +-
 net/rose/af_rose.c                                 |  73 +++-
 net/rose/rose_in.c                                 |  44 ++-
 net/rose/rose_link.c                               |  47 +++
 net/rose/rose_loopback.c                           |   2 +-
 net/rose/rose_out.c                                |   1 +
 net/rose/rose_route.c                              | 200 +++++++++--
 net/rose/rose_timer.c                              |  87 ++++-
 net/sched/act_api.c                                |  11 +-
 net/sched/cls_flower.c                             |   5 +
 net/sched/sch_cake.c                               |   2 +-
 net/sctp/inqueue.c                                 |   6 +-
 net/sctp/socket.c                                  |  33 +-
 net/socket.c                                       |  54 ++-
 net/tipc/node.c                                    |   2 +
 net/vmw_vsock/af_vsock.c                           |  49 ++-
 net/vmw_vsock/virtio_transport_common.c            |   9 +-
 samples/pidfd/Makefile                             |   2 +-
 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 +-
 sound/soc/codecs/cs42l52.c                         |   2 +-
 sound/soc/codecs/cs42l56.c                         |   2 +-
 sound/soc/codecs/max98371.c                        |   2 +-
 sound/soc/codecs/nau8822.c                         |   4 +-
 sound/soc/codecs/pcm3168a.c                        |   2 +-
 sound/soc/codecs/wm8978.c                          |   4 +-
 sound/soc/intel/avs/pcm.c                          |   2 +-
 sound/soc/sof/ipc4-topology.c                      |  16 +-
 sound/soc/stm/stm32_spdifrx.c                      |   2 +-
 tools/gpio/Makefile                                |   6 +
 tools/power/x86/turbostat/turbostat.c              |  11 +-
 .../test.d/filter/event-filter-incomplete.tc       |  24 ++
 229 files changed, 3065 insertions(+), 1216 deletions(-)
author maintainer
#1 lore raw

PATCH 1/247 rose: set SOCK_DESTROY in rose_kill_by_device() for prompt cleanup

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bernard Pidoux <[email protected]>

When rose_kill_by_device() is called (via NETDEV_DOWN on module exit or interface removal), it calls rose_disconnect() which transitions sockets to ROSE_STATE_0 and sets SOCK_DEAD. However, rose_heartbeat_expiry() only calls rose_destroy_socket() at ROSE_STATE_0 if SOCK_DESTROY is set -- the SOCK_DEAD path is reserved for TCP_LISTEN sockets. Without SOCK_DESTROY, orphaned sockets in ROSE_STATE_2 (clearing) loop indefinitely in the heartbeat without ever being freed, keeping the module use-count elevated and blocking modprobe -r rose until the T1 timer (up to 200 s) expires.

Set SOCK_DESTROY immediately after rose_disconnect() so the heartbeat destroys the socket at its next tick (within 5 s), allowing clean module unload.

(cherry picked from commit 741a4863ad570889c75f7a8e404567d8f3e46335)
---
 net/rose/af_rose.c | 5 +++++
 1 file changed, 5 insertions(+)
net/rose/af_rose.c +5 -0
index 03a298ecb8320..314f11fec831d 100644
@@ -211,6 +211,11 @@ static void rose_kill_by_device(struct net_device *dev)
 		spin_lock_bh(&rose_list_lock);
 		if (rose->device == dev) {
 			rose_disconnect(sk, ENETUNREACH, ROSE_OUT_OF_ORDER, 0);
+			/* Mark for destruction so rose_heartbeat_expiry()
+			 * cleans up the socket at its next tick rather than
+			 * looping forever in ROSE_STATE_0 with no owner.
+			 */
+			sock_set_flag(sk, SOCK_DESTROY);
 			if (rose->neighbour)
 				rose_neigh_put(rose->neighbour);
 			netdev_put(rose->device, &rose->dev_tracker);
Signature · 1 line
2.53.0
author maintainer
#2 lore raw

PATCH 2/247 rose: disconnect orphaned STATE_2 sockets when device is gone

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bernard Pidoux <[email protected]>

When ax25stop brings down ROSE interfaces, sockets in ROSE_STATE_2 (awaiting CLEAR CONFIRM) whose device pointer is already NULL are not reached by rose_kill_by_device() and wait for T3 (up to 180s) before self-cleaning via rose_timer_expiry(). This keeps the rose module usecount at 1, blocking rmmod for the full T3 duration.

In rose_heartbeat_expiry(), detect ROSE_STATE_2 sockets with no device, cancel T3, release the neighbour reference, and call rose_disconnect()
+ sock_set_flag(SOCK_DESTROY). The next heartbeat tick (<=5s) then destroys the socket via the existing ROSE_STATE_0/SOCK_DESTROY path, allowing clean module unload within 10s instead of up to 180s.

(cherry picked from commit d4f4cf9f09a3f5fafa8f09110a7c1b5d10f2f261)
---
 net/rose/rose_timer.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
net/rose/rose_timer.c +14 -0
index 53d198ac96e89..92c07ceaa3899 100644
@@ -139,6 +139,20 @@ static void rose_heartbeat_expiry(struct timer_list *t)
 		}
 		break;
 
+	case ROSE_STATE_2:
+		/* Device gone before CLEAR CONFIRM arrived: stop waiting for T3
+		 * and disconnect now instead of blocking rmmod for up to 180s. */
+		if (!rose->device) {
+			rose_stop_timer(sk);
+			if (rose->neighbour) {
+				rose_neigh_put(rose->neighbour);
+				rose->neighbour = NULL;
+			}
+			rose_disconnect(sk, ENETDOWN, -1, -1);
+			sock_set_flag(sk, SOCK_DESTROY);
+		}
+		break;
+
 	case ROSE_STATE_3:
 		/*
 		 * Check for the state of the receive buffer.
Signature · 1 line
2.53.0
author maintainer
#3 lore raw

PATCH 3/247 rose: release netdev ref and destroy orphaned incoming sockets

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bernard Pidoux <[email protected]>

Two related cleanup gaps left the module unremovable after a loopback session:

1. rose_destroy_socket() did not release the device reference. When an unaccepted incoming socket (created by rose_rx_call_request()) is destroyed via rose_heartbeat_expiry(), it is removed from rose_list before rose_kill_by_device() can find it, so the netdev_hold() taken in rose_rx_call_request() was never matched by netdev_put(). Add the release at the top of rose_destroy_socket() guarded by a NULL check so that rose_release() and rose_kill_by_device(), which already call netdev_put() and set device = NULL, are not affected.

2. rose_heartbeat_expiry() STATE_0 cleanup required TCP_LISTEN in addition to SOCK_DEAD. Unaccepted incoming sockets are TCP_ESTABLISHED, so the condition was never true and those sockets lingered forever, holding the module use count above zero and blocking rmmod. Drop the TCP_LISTEN restriction: any STATE_0 + SOCK_DEAD socket is orphaned and should be destroyed.

Together with the earlier rose_make_new() double-hold fix these three patches allow clean rmmod after loopback sessions.

(cherry picked from commit df12be096302d2c947388acc25764456c7f18cc1)
---
 net/rose/af_rose.c    | 9 +++++++++
 net/rose/rose_timer.c | 9 +++++----
 2 files changed, 14 insertions(+), 4 deletions(-)
net/rose/af_rose.c +9 -0
index 314f11fec831d..5aedbbf105c33 100644
@@ -363,6 +363,7 @@ static void rose_destroy_timer(struct timer_list *t)
  */
 void rose_destroy_socket(struct sock *sk)
 {
+	struct rose_sock *rose = rose_sk(sk);
 	struct sk_buff *skb;
 
 	rose_remove_socket(sk);
@@ -370,6 +371,14 @@ void rose_destroy_socket(struct sock *sk)
 	rose_stop_idletimer(sk);
 	rose_stop_timer(sk);
 
+	/* Drop any device reference not already released by rose_kill_by_device()
+	 * or rose_release() -- e.g. incoming sockets that were never accepted.
+	 */
+	if (rose->device) {
+		netdev_put(rose->device, &rose->dev_tracker);
+		rose->device = NULL;
+	}
+
 	rose_clear_queues(sk);		/* Flush the queues */
 
 	while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
net/rose/rose_timer.c +5 -4
index 92c07ceaa3899..b2c5f4653b11d 100644
@@ -128,10 +128,11 @@ static void rose_heartbeat_expiry(struct timer_list *t)
 	}
 	switch (rose->state) {
 	case ROSE_STATE_0:
-		/* Magic here: If we listen() and a new link dies before it
-		   is accepted() it isn't 'dead' so doesn't get removed. */
-		if (sock_flag(sk, SOCK_DESTROY) ||
-		    (sk->sk_state == TCP_LISTEN && sock_flag(sk, SOCK_DEAD))) {
+		/* Destroy any orphaned STATE_0 socket: either explicitly
+		 * flagged SOCK_DESTROY, or SOCK_DEAD (covers both unaccepted
+		 * incoming connections and listening sockets whose link died).
+		 */
+		if (sock_flag(sk, SOCK_DESTROY) || sock_flag(sk, SOCK_DEAD)) {
 			bh_unlock_sock(sk);
 			rose_destroy_socket(sk);
 			sock_put(sk);
Signature · 1 line
2.53.0
author maintainer
#4 lore raw

PATCH 4/247 rose: clear neighbour pointer in rose_kill_by_device()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bernard Pidoux <[email protected]>

rose_kill_by_device() drops the neighbour reference but leaves rose->neighbour pointing at it, unlike every other rose_neigh_put() site (see "rose: clear neighbour pointer after rose_neigh_put() in state machines"). The heartbeat STATE_0 reaping path then puts the same neighbour a second time, causing a rose_neigh refcount underflow and a use-after-free.

Set rose->neighbour = NULL after the put, restoring the invariant.

(cherry picked from commit 606e42d195b467480d4d405f8814c48d1651a76a)
---
 net/rose/af_rose.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
net/rose/af_rose.c +9 -1
index 5aedbbf105c33..e0294a5183b47 100644
@@ -216,8 +216,16 @@ static void rose_kill_by_device(struct net_device *dev)
 			 * looping forever in ROSE_STATE_0 with no owner.
 			 */
 			sock_set_flag(sk, SOCK_DESTROY);
-			if (rose->neighbour)
+			if (rose->neighbour) {
 				rose_neigh_put(rose->neighbour);
+				/* Clear the pointer after dropping the reference, as
+				 * every other rose_neigh_put() site does.  Otherwise
+				 * rose_heartbeat_expiry() (STATE_0 reaping) sees a stale
+				 * rose->neighbour and puts it a second time -> rose_neigh
+				 * refcount underflow / use-after-free.
+				 */
+				rose->neighbour = NULL;
+			}
 			netdev_put(rose->device, &rose->dev_tracker);
 			rose->device = NULL;
 		}
Signature · 1 line
2.53.0
author maintainer
#5 lore raw

PATCH 5/247 rose: dont free fd-owned sockets when reaping in the heartbeat

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bernard Pidoux <[email protected]>

The heartbeat reaps orphaned ROSE sockets after their bound device goes down. A socket still attached to a struct socket (sk->sk_socket != NULL -- e.g. an incoming connection an fpad client has accepted and kept open) is owned by that userspace fd: rose_release() frees it on close(). Freeing it from the heartbeat left the fd dangling, so the eventual close() touched freed memory -- slab-use-after-free in rose_release().

Reap only sockets with sk->sk_socket == NULL (unaccepted incoming connections and post-close orphans). For an fd-owned socket whose device went down, disconnect it and fall through to the switch so close() does the teardown. Also release the neighbour reference held by orphaned incoming sockets before tearing them down.

(cherry picked from commit 56576518920edd7b6c3479477d8d490fe2ebdaaa)
---
 net/rose/rose_timer.c | 57 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 56 insertions(+), 1 deletion(-)
net/rose/rose_timer.c +56 -1
index b2c5f4653b11d..3cd7e020422a5 100644
@@ -126,13 +126,68 @@ static void rose_heartbeat_expiry(struct timer_list *t)
 		sk_reset_timer(sk, &sk->sk_timer, jiffies + HZ/20);
 		goto out;
 	}
+
+	/* The bound device went down while we still hold a reference on it.
+	 * This catches the narrow race where rose_loopback_timer() created a
+	 * socket in the window after rose_kill_by_device()'s NETDEV_DOWN sweep
+	 * but before rose_insert_socket() -- leaving a STATE_3 socket that no
+	 * other branch reaps.  A down device means the link is dead, so tear
+	 * the socket down regardless of state.  rose_destroy_socket() releases
+	 * the held netdev reference (rose->device still set).
+	 */
+	if (rose->device && !netif_running(rose->device)) {
+		if (rose->neighbour) {
+			rose_neigh_put(rose->neighbour);
+			rose->neighbour = NULL;
+		}
+		rose_disconnect(sk, ENETDOWN, -1, -1);
+
+		/* Only reap the socket if userspace no longer holds it.  A socket
+		 * still attached to a struct socket (sk->sk_socket != NULL -- e.g.
+		 * a connection an fpad client has accepted and kept open) is owned
+		 * by that fd: rose_release() will destroy it on close().  Dropping
+		 * the last reference here leaves the open fd dangling, so the
+		 * eventual close() touches freed memory -> slab-use-after-free in
+		 * rose_release().  Unaccepted incoming sockets and post-close
+		 * orphans have sk->sk_socket == NULL and stay safe to reap here.
+		 */
+		if (!sk->sk_socket) {
+			sock_set_flag(sk, SOCK_DESTROY);
+			bh_unlock_sock(sk);
+			rose_destroy_socket(sk);
+			sock_put(sk);
+			return;
+		}
+
+		/* Owned by userspace: the link is down and the socket is now
+		 * disconnected (rose_disconnect() moved it to STATE_0).  Fall
+		 * through to the switch, which re-arms the heartbeat; the close()
+		 * will tear the socket down. */
+	}
+
 	switch (rose->state) {
 	case ROSE_STATE_0:
 		/* Destroy any orphaned STATE_0 socket: either explicitly
 		 * flagged SOCK_DESTROY, or SOCK_DEAD (covers both unaccepted
 		 * incoming connections and listening sockets whose link died).
 		 */
-		if (sock_flag(sk, SOCK_DESTROY) || sock_flag(sk, SOCK_DEAD)) {
+		if ((sock_flag(sk, SOCK_DESTROY) || sock_flag(sk, SOCK_DEAD)) &&
+		    !sk->sk_socket) {
+			/* Reap only orphaned sockets (sk->sk_socket == NULL).  A
+			 * socket still owned by a userspace fd reaches here via the
+			 * STATE_2 device-gone branch, which sets SOCK_DESTROY without
+			 * knowing about the fd; freeing it would race rose_release()
+			 * at close() -> use-after-free.  Leave it for close().
+			 *
+			 * Orphaned incoming sockets (rose_rx_call_request) hold a
+			 * neighbour reference; release it before teardown, as the
+			 * STATE_2 and device-down branches do.  rose_destroy_socket()
+			 * does not drop it.
+			 */
+			if (rose->neighbour) {
+				rose_neigh_put(rose->neighbour);
+				rose->neighbour = NULL;
+			}
 			bh_unlock_sock(sk);
 			rose_destroy_socket(sk);
 			sock_put(sk);
Signature · 1 line
2.53.0
author maintainer
#6 lore raw

PATCH 6/247 rose: keep the route needed for routing loop detection

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bernard Pidoux <[email protected]>

rose_route_frame() walks the route list before parsing the Call Request facilities. When a CALL REQUEST arrives on a LCI that already belongs to a route, the "Remove an existing unused route" branch deletes that route with rose_remove_route() and breaks out of the walk, so that a new call is able to reuse a stale LCI.

The routing loop detection that follows looks for a route recording the same random number and the same pair of callsigns as the incoming call. The route that has just been deleted is exactly the one it would have matched, so a call coming back to us around a routing loop is not
detected: it is routed again, a new route is allocated, and the next copy repeats the cycle. Two nodes whose routes point at each other then relay the same call to each other as fast as the CPU allows.

This was observed on an AX.25 network where two nodes each believed the route to a third one went through the other. The same CALL REQUEST, carrying the same random number, was re-sent every 400 us, interleaved with CLEAR REQUEST "Not obtainable", diagnostic 120, until the AX.25 link collapsed. Over nine days one node emitted 204 million frames on a link that normally carries a few frames per minute. The outgoing LCI stayed at 001 the whole time, because deleting the route freed it for immediate reuse.

Parse the facilities before walking the route list, and in both branches remove the route only when the incoming call differs from the one it records. When it is the same call, keep the route and clear the call with diagnostic 120, which is what the loop detection would have done. The predicate is factored out as rose_same_call() and reused by the loop detection itself.

Tested with two ROSE nodes routing a call back to each other. Before this change the kernel relayed 377 CALL REQUEST in ten seconds, never emitted a CLEAR, and the AX.25 link dropped. After it, the first call is relayed once, the next copy is cleared with diagnostic 120, and the link stays up.

(cherry picked from commit a7e80823ff751d751411f62770bbd61f01be81db)
---
 net/rose/rose_route.c | 71 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 56 insertions(+), 15 deletions(-)
net/rose/rose_route.c +56 -15
index 28746ae5a2582..000d7a8bf7535 100644
@@ -851,6 +851,21 @@ void rose_link_device_down(struct net_device *dev)
 	}
 }
 
+/*
+ *	Is this Call Request the very same call as the one an existing route
+ *	was built for?  Both the loop detection and the reuse of a stale LCI
+ *	below rely on this test.
+ */
+static bool rose_same_call(const struct rose_route *rose_route,
+			   const rose_address *src_addr,
+			   const struct rose_facilities_struct *facilities)
+{
+	return rose_route->rand == facilities->rand &&
+	       rosecmp(src_addr, &rose_route->src_addr) == 0 &&
+	       ax25cmp(&facilities->dest_call, &rose_route->src_call) == 0 &&
+	       ax25cmp(&facilities->source_call, &rose_route->dest_call) == 0;
+}
+
 /*
  *	Route a frame to an appropriate AX.25 connection.
  *	A NULL ax25_cb indicates an internally generated frame.
@@ -961,6 +976,16 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
 		goto out;
 	}
 
+	memset(&facilities, 0x00, sizeof(struct rose_facilities_struct));
+
+	if (frametype == ROSE_CALL_REQUEST &&
+	    !rose_parse_facilities(skb->data + ROSE_CALL_REQ_FACILITIES_OFF,
+				   skb->len - ROSE_CALL_REQ_FACILITIES_OFF,
+				   &facilities)) {
+		rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76);
+		goto out;
+	}
+
 	/*
 	 *	Route it to the next in line if we have an entry for it.
 	 */
@@ -969,7 +994,21 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
 		if (rose_route->lci1 == lci &&
 		    rose_route->neigh1 == rose_neigh) {
 			if (frametype == ROSE_CALL_REQUEST) {
-				/* F6FBB - Remove an existing unused route */
+				if (rose_same_call(rose_route, src_addr,
+						   &facilities)) {
+					/*
+					 *	The very same call coming back
+					 *	to us: a routing loop.  Keep
+					 *	the route, the loop detection
+					 *	below is what matches on it.
+					 */
+					rose_transmit_clear_request(rose_neigh,
+								    lci,
+								    ROSE_NOT_OBTAINABLE,
+								    120);
+					goto out;
+				}
+				/* Remove an existing unused route */
 				rose_remove_route(rose_route);
 				break;
 			} else if (rose_route->neigh2 != NULL) {
@@ -990,7 +1029,21 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
 		if (rose_route->lci2 == lci &&
 		    rose_route->neigh2 == rose_neigh) {
 			if (frametype == ROSE_CALL_REQUEST) {
-				/* F6FBB - Remove an existing unused route */
+				if (rose_same_call(rose_route, src_addr,
+						   &facilities)) {
+					/*
+					 *	The very same call coming back
+					 *	to us: a routing loop.  Keep
+					 *	the route, the loop detection
+					 *	below is what matches on it.
+					 */
+					rose_transmit_clear_request(rose_neigh,
+								    lci,
+								    ROSE_NOT_OBTAINABLE,
+								    120);
+					goto out;
+				}
+				/* Remove an existing unused route */
 				rose_remove_route(rose_route);
 				break;
 			} else if (rose_route->neigh1 != NULL) {
@@ -1021,24 +1074,12 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
 		goto out;
 	}
 
-	memset(&facilities, 0x00, sizeof(struct rose_facilities_struct));
-
-	if (!rose_parse_facilities(skb->data + ROSE_CALL_REQ_FACILITIES_OFF,
-				   skb->len - ROSE_CALL_REQ_FACILITIES_OFF,
-				   &facilities)) {
-		rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76);
-		goto out;
-	}
-
 	/*
 	 *	Check for routing loops.
 	 */
 	rose_route = rose_route_list;
 	while (rose_route != NULL) {
-		if (rose_route->rand == facilities.rand &&
-		    rosecmp(src_addr, &rose_route->src_addr) == 0 &&
-		    ax25cmp(&facilities.dest_call, &rose_route->src_call) == 0 &&
-		    ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) {
+		if (rose_same_call(rose_route, src_addr, &facilities)) {
 			rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120);
 			goto out;
 		}
Signature · 1 line
2.53.0
author maintainer
#7 lore raw

PATCH 7/247 rose: guard rose_transmit_link() against a NULL neighbour

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bernard Pidoux <[email protected]>

rose_kick() and rose_write_internal() call rose_transmit_link(skb, rose->neighbour) without checking that rose->neighbour is still set. Since commit e8eb0c6faa88 ("rose: clear neighbour pointer after rose_neigh_put() in state machines"), the state machines routinely set rose->neighbour to NULL once the underlying AX.25 link is gone, while leaving the socket in ROSE_STATE_3. rose_kick() only checks the state, not the neighbour, so a write() on such a socket reaches rose_transmit_link() with neigh == NULL, which immediately dereferences neigh->loopback and crashes:

  Unable to handle kernel NULL pointer dereference at
  virtual address 0000000000000036
  ...
  pc : rose_transmit_link+0x14/0x1c8 [rose]
  lr : rose_kick+0xec/0x198 [rose]
  Call trace:
   rose_transmit_link+0x14/0x1c8 [rose] (P)
   rose_kick+0xec/0x198 [rose]
   rose_sendmsg+0x260/0x3c8 [rose]

Reproduced on f6bvp-8 (a real ROSE/FPAC node) when the BBS daemon wrote to a socket whose neighbour had just been cleared. Confirmed by recompiling rose_link.o with a static_assert on offsetof(struct rose_neigh, loopback), which matches the faulting address (0x36) exactly.

Drop the skb and return early when neigh is NULL, matching what rose_transmit_link() already does for other frames it cannot send.

Fixes e8eb0c6faa88 ("rose: clear neighbour pointer after rose_neigh_put() in state machines")
(cherry picked from commit 2aa2cf6eb000bd324c8296a877680186b5df5fe9)
---
 net/rose/rose_link.c | 5 +++++
 1 file changed, 5 insertions(+)
net/rose/rose_link.c +5 -0
index 0f77ae8ef944a..cbfb11fc3ac52 100644
@@ -263,6 +263,11 @@ void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh)
 {
 	unsigned char *dptr;
 
+	if (!neigh) {
+		kfree_skb(skb);
+		return;
+	}
+
 	if (neigh->loopback) {
 		rose_loopback_queue(skb, neigh);
 		return;
Signature · 1 line
2.53.0
author maintainer
#8 lore raw

PATCH 8/247 rose: use timer_shutdown_sync() for t0timer teardown

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bernard Pidoux <[email protected]>

rose_t0timer_expiry() re-arms itself via rose_start_t0timer() at its own tail. rose_neigh_put() and rose_remove_neigh() stop it with timer_delete_sync() before freeing (or unlinking) the neighbour, but that only guarantees the callback is not running *at the moment the call returns* -- it does nothing to stop the very invocation that was just waited out from re-arming the timer on its way out. That re-arm races the kfree() in rose_neigh_put(): the timer can fire again on freed memory, and rose_t0timer_expiry() -> rose_transmit_restart_request() -> rose_send_frame() -> ax25_send_frame() dereferences the freed neigh->digipeat, use-after-free.

This matches a syzbot report (KASAN slab-use-after-free read in ax25_find_cb()) that stayed open with both cause and fix bisection failing -- consistent with a hole that timer_delete_sync() alone cannot close for a self-rearming timer, a case documented in its own kerneldoc ("there is no way to get this correct with timer_delete_sync()").

Use timer_shutdown_sync() instead in both call sites. Unlike timer_delete_sync(), it also marks the timer so that any further add_timer()/mod_timer() on it is silently ignored, which closes the window regardless of how the self-rearm and the free are interleaved. ftimer's handler is a no-op and never re-arms, but it is switched the same way for consistency: both timers are being torn down for good in both of these call sites.

Reported-by [email protected]
(cherry picked from commit 1cc5d2ead3cc0fc370f27728360bf8def008ee18)

[ Adjusted context: 6.12 still calls del_timer_sync() in
rose_remove_neigh(). ]

---
 include/net/rose.h    | 28 ++++++++++++++++++----------
 net/rose/rose_route.c | 10 ++++++++--
 2 files changed, 26 insertions(+), 12 deletions(-)
include/net/rose.h +18 -10
index 95d1f9c582dcc..0d957b8dd0ba3 100644
@@ -161,17 +161,25 @@ static inline void rose_neigh_put(struct rose_neigh *rose_neigh)
 {
 	if (refcount_dec_and_test(&rose_neigh->use)) {
 		/* We are dropping the last reference, so we are about to free the
-		 * neighbour.  Its timers may still be armed -- t0timer in particular
-		 * re-arms itself in rose_t0timer_expiry().  rose_remove_neigh()
-		 * cancels them before its own put, but callers that drop the final
-		 * reference without first calling rose_remove_neigh() (the socket
-		 * heartbeat reaping path) would otherwise kfree() a neighbour with a
-		 * live timer -> use-after-free.  timer_delete_sync() (not the async
-		 * variant) is required: it waits out a concurrently running handler
-		 * and loops until the self-rearming timer stays stopped.
+		 * neighbour.  t0timer is self-rearming: rose_t0timer_expiry() calls
+		 * rose_start_t0timer() at its own tail, so a plain timer_delete_sync()
+		 * is not enough here.  It only guarantees that the callback is not
+		 * running *at the moment it returns* -- it does nothing to stop the
+		 * very invocation we just waited out from re-arming the timer on its
+		 * way out, which races the kfree() below (syzbot: use-after-free read
+		 * in ax25_find_cb(), reached via rose_t0timer_expiry() ->
+		 * rose_transmit_restart_request() -> rose_send_frame() ->
+		 * ax25_send_frame(), dereferencing the freed neigh->digipeat).
+		 * timer_shutdown_sync() closes that hole: once it returns, any
+		 * further add_timer()/mod_timer() on this timer is silently ignored,
+		 * so a self-rearm racing the free can no longer bring the timer back
+		 * to life on freed memory.  ftimer's handler is a no-op and never
+		 * re-arms, but it is shut down the same way here for consistency --
+		 * this is final teardown, neither timer has any business firing
+		 * again.
 		 */
-		timer_delete_sync(&rose_neigh->ftimer);
-		timer_delete_sync(&rose_neigh->t0timer);
+		timer_shutdown_sync(&rose_neigh->ftimer);
+		timer_shutdown_sync(&rose_neigh->t0timer);
 		if (rose_neigh->ax25)
 			ax25_cb_put(rose_neigh->ax25);
 		kfree(rose_neigh->digipeat);
net/rose/rose_route.c +8 -2
index 000d7a8bf7535..fd196f2e2fd64 100644
@@ -229,8 +229,14 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh)
 {
 	struct rose_neigh *s;
 
-	del_timer_sync(&rose_neigh->ftimer);
-	del_timer_sync(&rose_neigh->t0timer);
+	/* Being removed from the list is final teardown for this neighbour:
+	 * shut the timers down rather than just deleting them, so a t0timer
+	 * self-rearm losing the race with this removal can't bring it back to
+	 * life later.  See the comment in rose_neigh_put() (include/net/rose.h)
+	 * for the full story.
+	 */
+	timer_shutdown_sync(&rose_neigh->ftimer);
+	timer_shutdown_sync(&rose_neigh->t0timer);
 
 	skb_queue_purge(&rose_neigh->queue);
 
Signature · 1 line
2.53.0
author maintainer
#9 lore raw

PATCH 9/247 rose: dont warn on stray CALL_ACCEPTED/CLEAR_CONFIRMATION in state 3

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bernard Pidoux <[email protected]>

rose_state3_machine() logs "ROSE: unknown %02X in state 3" at KERN_WARNING for any frame type it does not handle during data transfer. Two of them reach that default arm routinely on real AX.25 links and alarm sysops watching the console, even though they are harmless:

- CALL_ACCEPTED (0x0F): a late or duplicated Call Accepted that arrives after the socket has already moved to STATE_3, typically a retransmission on a slow link.

- CLEAR_CONFIRMATION (0x17): crossed clearing, or a clear confirmation left over from a previous incarnation of a reused logical channel.

In both cases the frame is simply dropped: no state change, no teardown, nothing freed. Only the noise is a problem.

Drop these two frame types silently. Keep reporting any other, genuinely unexpected frame type, but through net_warn_ratelimited() so a misbehaving peer cannot flood the kernel log.

(cherry picked from commit 8649e4074ca29a6a8ef0076c2892ebc980b7153f)
---
 net/rose/rose_in.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)
net/rose/rose_in.c +12 -1
index 666153a3dd8f7..69b2af24e85a6 100644
@@ -202,7 +202,18 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety
 		break;
 
 	default:
-		printk(KERN_WARNING "ROSE: unknown %02X in state 3\n", frametype);
+		/*
+		 * CALL_ACCEPTED (0x0F) and CLEAR_CONFIRMATION (0x17) may show
+		 * up in state 3 as a late or duplicated Call Accepted, or as
+		 * crossed clearing / a reused logical channel on a slow AX.25
+		 * link. These are harmless protocol races, so drop them
+		 * silently. Any other frame type is genuinely unexpected and
+		 * is still reported, rate limited to avoid flooding the log.
+		 */
+		if (frametype != ROSE_CALL_ACCEPTED &&
+		    frametype != ROSE_CLEAR_CONFIRMATION)
+			net_warn_ratelimited("ROSE: unknown %02X in state 3\n",
+					     frametype);
 		break;
 	}
 
Signature · 1 line
2.53.0
author maintainer
#10 lore raw

PATCH 10/247 rose: hold a reference on incoming sockets queued for accept()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bernard Pidoux <[email protected]>

rose_rx_call_request() creates the socket for an incoming call and queues the call's skb on the listening socket with skb->sk pointing at it, without taking a reference. If the caller clears the call before it is accepted, rose_disconnect() leaves that socket in STATE_0 with SOCK_DEAD set, and since commit "rose: release netdev ref and destroy orphaned incoming sockets" rose_heartbeat_expiry() reaps such orphaned sockets and frees them. The skb stays on the listener's queue, so:

- closing the listener makes rose_destroy_socket() walk its queue and set flags and restart the heartbeat on the freed socket;
- accept() can hand the freed socket to user space.

On a live node running a KASAN kernel, stopping the FPAC daemons shortly after a local (loopback) call had been set up and dropped gave:

  BUG: KASAN: slab-use-after-free in rose_destroy_socket+0x24a/0x570 [rose]
  Write of size 8 ... by task fpacwpd
   rose_destroy_socket
   rose_release
  Allocated by task 0: ... rose_rx_call_request <- rose_loopback_timer
  Freed by task ...: ... sk_free <- rose_heartbeat_expiry
  refcount_t: addition on 0; use-after-free.
   rose_start_heartbeat <- rose_destroy_socket <- rose_release

followed a few seconds later by a page fault in the re-armed heartbeat and a kernel panic.

Make the listener's queue own a reference on each pending socket, taken when the call is queued and dropped by rose_accept() or by the listener's rose_destroy_socket() once the skb is off the queue. Have the heartbeat set SOCK_DESTROY under the socket lock before reaping a dead socket, as the other reaping paths already do, and make both consumers check it under the child's lock: rose_accept() skips a reaped socket and waits for the next call, and rose_destroy_socket() no longer re-arms the heartbeat of a socket that is gone.

Fixes df12be096302 ("rose: release netdev ref and destroy orphaned incoming sockets")
(cherry picked from commit 6b0223a4795f5db52dee038a7a29e9906122b0f7)
---
 net/rose/af_rose.c    | 44 ++++++++++++++++++++++++++++++++++++++-----
 net/rose/rose_timer.c |  5 +++++
 2 files changed, 44 insertions(+), 5 deletions(-)
net/rose/af_rose.c +39 -5
index e0294a5183b47..420408a5ad61c 100644
@@ -390,11 +390,25 @@ void rose_destroy_socket(struct sock *sk)
 	rose_clear_queues(sk);		/* Flush the queues */
 
 	while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
-		if (skb->sk != sk) {	/* A pending connection */
-			/* Queue the unaccepted socket for death */
-			sock_set_flag(skb->sk, SOCK_DEAD);
-			rose_start_heartbeat(skb->sk);
-			rose_sk(skb->sk)->state = ROSE_STATE_0;
+		struct sock *child = skb->sk;
+
+		if (child != sk) {	/* A pending connection */
+			/* Queue the unaccepted socket for death, unless
+			 * rose_heartbeat_expiry() has already reaped it or is
+			 * about to (SOCK_DESTROY).
+			 */
+			local_bh_disable();
+			bh_lock_sock_nested(child);
+			if (!sock_flag(child, SOCK_DESTROY)) {
+				sock_set_flag(child, SOCK_DEAD);
+				rose_start_heartbeat(child);
+				rose_sk(child)->state = ROSE_STATE_0;
+			}
+			bh_unlock_sock(child);
+			local_bh_enable();
+
+			skb->sk = NULL;
+			sock_put(child);	/* the queue's reference */
 		}
 
 		kfree_skb(skb);
@@ -980,6 +994,7 @@ static int rose_accept(struct socket *sock, struct socket *newsock,
 	 *	The write queue this time is holding sockets ready to use
 	 *	hooked into the SABM we saved
 	 */
+again:
 	for (;;) {
 		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
 
@@ -1005,12 +1020,27 @@ static int rose_accept(struct socket *sock, struct socket *newsock,
 		goto out_release;
 
 	newsk = skb->sk;
+	lock_sock_nested(newsk, SINGLE_DEPTH_NESTING);
+	if (sock_flag(newsk, SOCK_DESTROY)) {
+		/* The call went away and rose_heartbeat_expiry() reaped (or
+		 * is about to reap) the socket before it was accepted: drop
+		 * it and wait for the next one.
+		 */
+		release_sock(newsk);
+		skb->sk = NULL;
+		kfree_skb(skb);
+		sk_acceptq_removed(sk);
+		sock_put(newsk);	/* the queue's reference */
+		goto again;
+	}
 	sock_graft(newsk, newsock);
+	release_sock(newsk);
 
 	/* Now attach up the new socket */
 	skb->sk = NULL;
 	kfree_skb(skb);
 	sk_acceptq_removed(sk);
+	sock_put(newsk);		/* the queue's reference */
 
 out_release:
 	release_sock(sk);
@@ -1124,6 +1154,10 @@ int rose_rx_call_request(struct sk_buff *skb, struct net_device *dev, struct ros
 
 	rose_insert_socket(make);
 
+	/* The listener's queue holds a reference on the pending socket
+	 * until rose_accept() or rose_destroy_socket() takes it off.
+	 */
+	sock_hold(make);
 	skb_queue_head(&sk->sk_receive_queue, skb);
 
 	rose_start_heartbeat(make);
net/rose/rose_timer.c +5 -0
index 3cd7e020422a5..5ba0c89e4ee1b 100644
@@ -188,6 +188,11 @@ static void rose_heartbeat_expiry(struct timer_list *t)
 				rose_neigh_put(rose->neighbour);
 				rose->neighbour = NULL;
 			}
+			/* Tell rose_accept() and the listener's teardown, which
+			 * may still hold this socket on their queue, that it is
+			 * gone.
+			 */
+			sock_set_flag(sk, SOCK_DESTROY);
 			bh_unlock_sock(sk);
 			rose_destroy_socket(sk);
 			sock_put(sk);
Signature · 1 line
2.53.0
author maintainer
#11 lore raw

PATCH 11/247 ax25: bound REJ-driven fast retransmits with the existing N2 counter

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bernard Pidoux <[email protected]>

REJ deliberately bypasses the T1/N2 backoff to trigger an immediate go-back-N retransmit -- that is the whole point of REJ, fast recovery from a single lost or out-of-order frame. But unlike every other recovery path in this same state machine (T1 timeout, state 4 enquiry/DM, state 1/2 SABM retries), the REJ case in ax25_std_state3_machine() never touches n2count, so it has no bound.

If something keeps corrupting or reordering every retransmitted burst -- lossy RF, or packet reordering over a UDP-based transport such as axudp -- each attempt draws an immediate fresh REJ and the link retransmits at wire/CPU speed indefinitely, a "machine-gun" storm that never reaches the N2 give-up already relied on everywhere else in this file.

Reported independently twice: Chris Maness (KQ6UP) on a plain AX.25 link with no ROSE involved at all, and observed live on a ROSE/FPAC White Pages link (f6bvp-8<->f6bvp-10, same LAN, so plain network reordering over the Internet is not the trigger there -- likely lossy retransmission of the same PDU) reaching ~5900 pkt/s. Both hit the same code path since ax25_std_in.c is shared regardless of what rides on top of AX.25.

Count consecutive REJ-driven fast retransmits the same way a T1 timeout already does in this state, and once N2 of them have failed to get an in-sequence frame through, fall back to the paced state 4 enquiry -- which will genuinely disconnect after N2 further tries if the link stays bad, exactly like every other recovery path already does. A single frame accepted in sequence resets the counter, so an isolated REJ during otherwise healthy traffic never gets close to N2.

Deployed on three Raspberry Pi nodes (f6bvp-12, f6bvp-8, f6bvp-10) since 2026-09-13, running live production AX.25/ROSE/NET-ROM traffic since, with no recurrence of the machine-gun storm and no other AX.25 regression observed.

(cherry picked from commit a3aa611e37d4345ebcc98c847dedaf4bcf23c7ee)
---
 net/ax25/ax25_std_in.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
net/ax25/ax25_std_in.c +36 -0
index ba176196ae068..f280683f83fcf 100644
@@ -196,6 +196,38 @@ static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frame
 		if (ax25_validate_nr(ax25, nr)) {
 			ax25_frames_acked(ax25, nr);
 			ax25_calculate_rtt(ax25);
+			/*
+			 * REJ triggers an immediate go-back-N retransmit,
+			 * deliberately bypassing the T1/N2 backoff that
+			 * paces every other recovery path in this state
+			 * machine -- that is the whole point of REJ, fast
+			 * recovery from a single lost/out-of-order frame.
+			 * But unlike every other recovery path here, this
+			 * one never touches n2count, so it has no bound: if
+			 * something keeps corrupting or reordering each
+			 * retransmitted burst (lossy RF, or reordering over
+			 * a UDP-based link such as axudp), every attempt
+			 * draws an immediate fresh REJ and the link is
+			 * retransmitted at wire/CPU speed indefinitely
+			 * ("machine-gun" storm), never reaching the N2 give
+			 * up that bounds every other recovery attempt.
+			 * Count consecutive REJ-driven fast retransmits the
+			 * same way a T1 timeout would in this state, and
+			 * once N2 of them have failed to make it through,
+			 * fall back to the normal paced state 4 enquiry
+			 * (which will genuinely give up and disconnect after
+			 * N2 further tries if the link stays bad). A single
+			 * frame accepted in sequence resets the counter (see
+			 * the AX25_I case below), so an isolated REJ during
+			 * otherwise healthy traffic never gets close to N2.
+			 */
+			if (ax25->n2count == ax25->n2) {
+				ax25->n2count = 1;
+				ax25_std_transmit_enquiry(ax25);
+				ax25->state = AX25_STATE_4;
+				break;
+			}
+			ax25->n2count++;
 			ax25_stop_t1timer(ax25);
 			ax25_start_t3timer(ax25);
 			ax25_requeue_frames(ax25);
@@ -226,6 +258,10 @@ static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frame
 			if (ax25->condition & AX25_COND_OWN_RX_BUSY)
 				ax25->vr = ns;	/* ax25->vr - 1 */
 			ax25->condition &= ~AX25_COND_REJECT;
+			/* Genuine forward progress: a frame made it through in
+			 * sequence, so this is not part of a REJ storm.
+			 */
+			ax25->n2count = 0;
 			if (pf) {
 				ax25_std_enquiry_response(ax25);
 			} else {
Signature · 1 line
2.53.0
author maintainer
#12 lore raw

PATCH 12/247 rose: purge rose_neigh->queue on final teardown

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bernard Pidoux <[email protected]>

rose_transmit_link() queues an outgoing frame on rose_neigh->queue instead of sending it immediately whenever the neighbour isn't "restarted" yet (link not up) -- notably the CLEAR_REQUEST built by rose_write_internal() when a socket is released (e.g. its owning process is killed) while the underlying AX.25 link to that neighbour is still coming up.

rose_neigh_put() already tears down both timers and drops the ax25/ digipeat references before freeing the neighbour, but never empties this queue. kfree(rose_neigh) only frees the rose_neigh struct itself (the sk_buff_head embedded in it is just three next/prev/len words), not the skbs still linked into it -- every frame still queued here at that point leaks for good.

rose_remove_neigh() (rose_route.c) already purges this same queue, but it is only one of about two dozen call sites that can drop the last reference and trigger the real kfree() in rose_neigh_put() -- every socket releasing its own rose->neighbour reference (af_rose.c, rose_in.c, rose_timer.c) can just as well be the one that does. Doing the purge once, centrally, in rose_neigh_put() itself covers all of them regardless of which call site happens to be last, rather than relying on whichever caller happened to remember it.

Matches a leak pattern observed and documented back in May 2026 (mailbbs killed -> rose_release() -> rose_write_internal() -> rose_transmit_link() queues the CLEAR_REQUEST -> skb never freed), never root-caused until now.

Deployed and running since 2026-09-13/15 on three independent nodes (f6bvp-12, f6bvp-8, f6bvp-10 -- three different kernel bases, 6.12.94/6.18.46/6.18.34), no regression observed.

(cherry picked from commit e59e4523182e4f58f4f29b5e4bce44c1ef152d70)
---
 include/net/rose.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)
include/net/rose.h +12 -0
index 0d957b8dd0ba3..49140668b3dd0 100644
@@ -180,6 +180,18 @@ static inline void rose_neigh_put(struct rose_neigh *rose_neigh)
 		 */
 		timer_shutdown_sync(&rose_neigh->ftimer);
 		timer_shutdown_sync(&rose_neigh->t0timer);
+		/* rose_transmit_link() queues a frame here instead of sending
+		 * it immediately whenever the neighbour isn't "restarted" yet
+		 * (link not up) -- e.g. the CLEAR_REQUEST built by
+		 * rose_write_internal() when a socket is released while its
+		 * underlying AX.25 link is still coming up. If this neighbour
+		 * is freed before that link ever comes up, nothing else ever
+		 * empties this queue: kfree() below only frees the
+		 * rose_neigh struct itself (including the three next/prev/len
+		 * words of the sk_buff_head), not the skbs it points to,
+		 * leaking every frame still queued here.
+		 */
+		skb_queue_purge(&rose_neigh->queue);
 		if (rose_neigh->ax25)
 			ax25_cb_put(rose_neigh->ax25);
 		kfree(rose_neigh->digipeat);
Signature · 1 line
2.53.0
author maintainer
#13 lore raw

PATCH 13/247 mptcp: move the OoO check back into __mptcp_try_fallback()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Abdul Wasey <[email protected]>

The 6.12.y backport of upstream commit 1bba3f219c5e ("mptcp: do not fallback when OoO is present") put the out-of-order queue check in __mptcp_finish_join() instead of __mptcp_try_fallback().

So the fix itself is missing, as fallback still happens with OoO data queued. Worse, the passive side now refuses every MP_JOIN while its OoO queue is not empty, which is the normal state of a connection already sending data over more than one subflow. The joining subflow is reset, so such a connection can't add a new path, and a path that comes back after an outage can't rejoin while data is in flight.

Move the check to __mptcp_try_fallback(), as upstream does.

Tested on 6.12.113 in netns: 6 connections (5 uploading) over two paths, then a third endpoint added mid-transfer. Without this patch only the idle connection gets a subflow on the new path and the other 5 joins are reset. With it, all 6 join. Same result in 3 of 3 runs each.

Fixes aab400cf8e35 ("mptcp: do not fallback when OoO is present")
---
 net/mptcp/protocol.c |   14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
net/mptcp/protocol.c +7 -7
@@ -74,6 +74,13 @@ bool __mptcp_try_fallback(struct mptcp_s
 	if (__mptcp_check_fallback(msk))
 		return true;
 
+	/* The caller possibly is not holding the msk socket lock, but
+	 * in the fallback case only the current subflow is touching
+	 * the OoO queue.
+	 */
+	if (!RB_EMPTY_ROOT(&msk->out_of_order_queue))
+		return false;
+
 	spin_lock_bh(&msk->fallback_lock);
 	if (!msk->allow_infinite_fallback) {
 		spin_unlock_bh(&msk->fallback_lock);
@@ -943,13 +950,6 @@ static bool __mptcp_finish_join(struct m
 	if (sk->sk_state != TCP_ESTABLISHED)
 		return false;
 
-	/* The caller possibly is not holding the msk socket lock, but
-	 * in the fallback case only the current subflow is touching
-	 * the OoO queue.
-	 */
-	if (!RB_EMPTY_ROOT(&msk->out_of_order_queue))
-		return false;
-
 	spin_lock_bh(&msk->fallback_lock);
 	if (!msk->allow_subflows) {
 		spin_unlock_bh(&msk->fallback_lock);
author maintainer
#14 lore raw

PATCH 14/247 fat: stop reading directory entries past the end-of-directory marker

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Matteo Croce <[email protected]>

commit 6a2875517c778ac1111b6920e94cbab91cda8724 upstream.

The FAT specification[1] (FAT Directory Structure -> "DIR_Name[0]") states:

If DIR_Name[0] == 0x00, then the directory entry is free (same as for 0xE5), and there are no allocated directory entries after this one (all of the DIR_Name[0] bytes in all of the entries after this one are also set to 0).

The special 0 value, rather than the 0xE5 value, indicates to FAT file system driver code that the rest of the entries in this directory do not need to be examined because they are all free.

Linux did not honour this. fat_get_entry() kept advancing past the 0x00 terminator; if the trailing on-disk slots were not zero-filled (buggy formatters, read-only media written by other operating systems, on-disk corruption) the driver surfaced arbitrary bytes as real directory entries. On a typical affected image, ls /mnt returns ~150 bogus entries with random binary names, multi-gigabyte sizes, dates ranging from 1980 to 2106, and a flood of -EIO from stat().

Earlier attempts (v1..v3, see [2][3][4]) added de->name[0] == 0 guards at each call site. As Hirofumi pointed out on v3, those guards reject the entry but fat_get_entry() has already advanced *pos past it; the next readdir() resumes after the marker and walks straight back into the garbage. His suggestion was to centralise the check.

This patch:

* Adds fat_get_entry_eod(), a small wrapper around fat_get_entry() that returns -1 when name[0] == 0 and seeks *pos to dir->i_size. Per spec every slot after the 0x00 marker is also zero, so jumping to the end of the directory is correct: subsequent reads return -1 from fat_bmap() without re-fetching trailing zero slots, and callers persisting *pos across invocations (notably readdir's ctx->pos) keep reporting end-of-directory on re-entry.

* Converts the read/search paths to use the new wrapper:
fat_parse_long(), fat_search_long(), __fat_readdir(), and fat_get_short_entry() -- the last covers fat_get_dotdot_entry(), fat_dir_empty(), fat_subdirs(), fat_scan(), and fat_scan_logstart() transitively.

* Leaves fat_add_entries() and __fat_remove_entries() on raw fat_get_entry(): the write paths legitimately need to operate on free/zero slots. fat_add_entries() additionally detects an allocated entry past a 0x00 marker (the spec violation that produces the garbage) and treats it as filesystem corruption:
fat_fs_error_ratelimit() is called -- which honours the configured errors= mount option (panic / remount-ro / continue) -- and the operation returns -EIO so we don't write fresh entries into an already-corrupt directory.

[1] https://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/fatgen103.doc
[2] https://lore.kernel.org/lkml/[email protected]/
[3] https://lore.kernel.org/lkml/[email protected]/
[4] https://lore.kernel.org/lkml/[email protected]/

[nb: keep i_pos in fat_add_entries(), it is still used here because
the fat_add_new_entries() rework is not in this tree]

---
 fs/fat/dir.c | 44 ++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 40 insertions(+), 4 deletions(-)
fs/fat/dir.c +40 -4
index acbec5bdd5210..ce6b87210e189 100644
@@ -128,6 +128,31 @@ static inline int fat_get_entry(struct inode *dir, loff_t *pos,
 	return fat__get_entry(dir, pos, bh, de);
 }
 
+/*
+ * Like fat_get_entry(), but honour the FAT end-of-directory marker:
+ * a dirent whose first name byte is NUL terminates iteration per the
+ * spec, which also guarantees that every following slot is zeroed.
+ * Skip straight to the end of the directory so the next call returns
+ * -1 from fat_bmap() without re-reading the trailing zero slots, and
+ * so callers that persist *pos across invocations (e.g. readdir's
+ * ctx->pos) keep reporting EOD.  Release *bh and set it to NULL to
+ * match fat_get_entry()'s contract that *bh is NULL on the -1 return.
+ */
+static int fat_get_entry_eod(struct inode *dir, loff_t *pos,
+			     struct buffer_head **bh,
+			     struct msdos_dir_entry **de)
+{
+	int err = fat_get_entry(dir, pos, bh, de);
+
+	if (err == 0 && (*de)->name[0] == 0) {
+		brelse(*bh);
+		*bh = NULL;
+		*pos = dir->i_size;
+		return -1;
+	}
+	return err;
+}
+
 /*
  * Convert Unicode 16 to UTF-8, translated Unicode, or ASCII.
  * If uni_xlate is enabled and we can't get a 1:1 conversion, use a
@@ -325,7 +350,7 @@ static int fat_parse_long(struct inode *dir, loff_t *pos,
 
 		if (ds->id & 0x40)
 			(*unicode)[offset + 13] = 0;
-		if (fat_get_entry(dir, pos, bh, de) < 0)
+		if (fat_get_entry_eod(dir, pos, bh, de) < 0)
 			return PARSE_EOF;
 		if (slot == 0)
 			break;
@@ -487,7 +512,7 @@ int fat_search_long(struct inode *inode, const unsigned char *name,
 
 	err = -ENOENT;
 	while (1) {
-		if (fat_get_entry(inode, &cpos, &bh, &de) == -1)
+		if (fat_get_entry_eod(inode, &cpos, &bh, &de) == -1)
 			goto end_of_dir;
 parse_record:
 		nr_slots = 0;
@@ -599,7 +624,7 @@ static int __fat_readdir(struct inode *inode, struct file *file,
 
 	bh = NULL;
 get_new:
-	if (fat_get_entry(inode, &cpos, &bh, &de) == -1)
+	if (fat_get_entry_eod(inode, &cpos, &bh, &de) == -1)
 		goto end_of_dir;
 parse_record:
 	nr_slots = 0;
@@ -882,7 +907,7 @@ static int fat_get_short_entry(struct inode *dir, loff_t *pos,
 			       struct buffer_head **bh,
 			       struct msdos_dir_entry **de)
 {
-	while (fat_get_entry(dir, pos, bh, de) >= 0) {
+	while (fat_get_entry_eod(dir, pos, bh, de) >= 0) {
 		/* free entry or long name entry or volume label */
 		if (!IS_FREE((*de)->name) && !((*de)->attr & ATTR_VOLUME))
 			return 0;
@@ -1299,6 +1324,7 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots,
 	struct msdos_dir_entry *de;
 	int err, free_slots, i, nr_bhs;
 	loff_t pos, i_pos;
+	bool saw_eod;
 
 	sinfo->nr_slots = nr_slots;
 
@@ -1307,12 +1333,15 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots,
 	bh = prev = NULL;
 	pos = 0;
 	err = -ENOSPC;
+	saw_eod = false;
 	while (fat_get_entry(dir, &pos, &bh, &de) > -1) {
 		/* check the maximum size of directory */
 		if (pos >= FAT_MAX_DIR_SIZE)
 			goto error;
 
 		if (IS_FREE(de->name)) {
+			if (de->name[0] == 0)
+				saw_eod = true;
 			if (prev != bh) {
 				get_bh(bh);
 				bhs[nr_bhs] = prev = bh;
@@ -1322,6 +1351,13 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots,
 			if (free_slots == nr_slots)
 				goto found;
 		} else {
+			if (saw_eod) {
+				fat_fs_error_ratelimit(sb,
+					"allocated dir entry found after end-of-directory marker (i_pos %lld)",
+					MSDOS_I(dir)->i_pos);
+				err = -EIO;
+				goto error;
+			}
 			for (i = 0; i < nr_bhs; i++)
 				brelse(bhs[i]);
 			prev = NULL;
Signature · 1 line
2.53.0
author maintainer
#15 lore raw

PATCH 15/247 soc/tegra: fuse: Add missing newline to APBMISC error message

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jinhui Guo <[email protected]>

[ Upstream commit 2f4a0fb66d313f03832f061bc3ec25b33c8198c9 ]

The error message printed by tegra_acpi_init_apbmisc() when failing to get APBMISC memory resources is missing a trailing newline. This can cause the message to be merged with the following kernel log line, making the log harder to read.

Add the missing newline to keep the error output properly formatted.

Fixes 7b0c505eb341 ("soc/tegra: fuse: Add tegra_acpi_init_apbmisc()")
---
 drivers/soc/tegra/fuse/tegra-apbmisc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/soc/tegra/fuse/tegra-apbmisc.c +1 -1
index e2ca5d55fd312..b506ed40e1080 100644
@@ -277,7 +277,7 @@ void tegra_acpi_init_apbmisc(void)
 
 	ret = acpi_dev_get_memory_resources(adev, &resource_list);
 	if (ret < 0) {
-		pr_err("failed to get APBMISC memory resources");
+		pr_err("failed to get APBMISC memory resources\n");
 		goto out_put_acpi_dev;
 	}
 
Signature · 1 line
2.53.0
author maintainer
#16 lore raw

PATCH 16/247 power: supply: Fix maintenance charge table kernel-doc

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Karl Mehltretter <[email protected]>

[ Upstream commit 45d4af4e80a25881b6b75e8e34872c555c43a41d ]

struct power_supply_maintenance_charge_table contains charge_safety_timer_minutes, but its kernel-doc describes safety_timer_minutes. This leaves the real member undescribed and adds a nonexistent member to the generated documentation.

Use the member's actual name.

Fixes d72ce7d32478 ("power: supply: ab8500: Standardize maintenance charging")
Assisted-by LLM
---
 include/linux/power_supply.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
include/linux/power_supply.h +3 -3
index 910d407ebe632..b03991a29a65e 100644
@@ -360,9 +360,9 @@ struct power_supply_vbat_ri_table {
  * @charge_voltage_max_uv: maintenance charging voltage that is usually a bit
  *   lower than the constant_charge_voltage_max_uv. We can apply this settings
  *   charge_current_max_ua until we get back up to this voltage.
- * @safety_timer_minutes: maintenance charging safety timer, with an expiry
- *   time in minutes. We will only use maintenance charging in this setting
- *   for a certain amount of time, then we will first move to the next
+ * @charge_safety_timer_minutes: maintenance charging safety timer, with an
+ *   expiry time in minutes. We will only use maintenance charging in this
+ *   setting for a certain amount of time, then we will first move to the next
  *   maintenance charge current and voltage pair in respective array and wait
  *   for the next safety timer timeout, or, if we reached the last maintencance
  *   charging setting, disable charging until we reach
Signature · 1 line
2.53.0
author maintainer
#17 lore raw

PATCH 17/247 power: supply: ds2780: Fix ds2780_get_capacity() returning raw value

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xueqin Luo <[email protected]>

[ Upstream commit a58cbc8b36ec0cd00d2ba3de7d003de818a27523 ]

ds2780_get_capacity() returns the raw register value instead of 0 on success. The capacity is already stored in *capacity, so the function should return 0 to indicate success.

Fixes 275ac74629c4 ("w1: add Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC support")
---
 drivers/power/supply/ds2780_battery.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/power/supply/ds2780_battery.c +1 -1
index 1e7f297f6cb18..7219ee3def5ad 100644
@@ -304,7 +304,7 @@ static int ds2780_get_capacity(struct ds2780_device_info *dev_info,
 		return ret;
 
 	*capacity = raw;
-	return raw;
+	return 0;
 }
 
 static int ds2780_get_status(struct ds2780_device_info *dev_info, int *status)
Signature · 1 line
2.53.0
author maintainer
#18 lore raw

PATCH 18/247 Drivers: hv: Avoid infinite retry loop in init_vp_index()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Waiman Long <[email protected]>

[ Upstream commit 32ea0a33e3a0fb7c10c601b43e1b2f6c06a3f5fd ]

There is a retry loop in init_vp_index() where the CPUs from a certain node are stripped out if they have already been in the allocated cpumask or not in HK_TYPE_MANAGED_IRQ housekeeping cpumask. If there is no CPU left, the allocated cpumask is ignored and the process is retried again. However, if the HK_TYPE_MANAGED_IRQ housekeeping cpumask turns out not to contain any CPU in that particular node, that will become an infinite retry loop. This particular problem was reported by sashiko [1]. This should rarely happen, but we still need to guard against this.

Fix this infinite loop problem by also skipping NUMA node that has no housekeeping CPU in the inner while loop of init_vp_index(). Also update the early abort check to check for the absence of online housekeeping CPUs instead of just the emptiness of the cpumask. As the outer for loop will only be reached if the housekeeping cpumask has at least one online CPU, a NUMA node with housekeeping CPUs will eventually be found.

Fixes 6640b5df1a38 ("Drivers: hv: vmbus: Don't assign VMbus channel interrupts to isolated CPUs")
---
 drivers/hv/channel_mgmt.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)
drivers/hv/channel_mgmt.c +9 -3
index 3c6011a48dabe..914d479d78723 100644
@@ -743,13 +743,14 @@ static void init_vp_index(struct vmbus_channel *channel)
 	u32 i, ncpu = num_online_cpus();
 	cpumask_var_t available_mask;
 	struct cpumask *allocated_mask;
+	const struct cpumask *node_mask;
 	const struct cpumask *hk_mask = housekeeping_cpumask(HK_TYPE_MANAGED_IRQ);
 	u32 target_cpu;
 	int numa_node;
 
 	if (!perf_chn ||
 	    !alloc_cpumask_var(&available_mask, GFP_KERNEL) ||
-	    cpumask_empty(hk_mask)) {
+	    !cpumask_intersects(hk_mask, cpu_online_mask)) {
 		/*
 		 * If the channel is not a performance critical
 		 * channel, bind it to VMBUS_CONNECT_CPU.
@@ -771,14 +772,19 @@ static void init_vp_index(struct vmbus_channel *channel)
 				next_numa_node_id = 0;
 				continue;
 			}
-			if (cpumask_empty(cpumask_of_node(numa_node)))
+			/*
+			 * Try next NUMA node if current NUMA node has no CPU
+			 * or doesn't contain any housekeeping CPU.
+			 */
+			node_mask = cpumask_of_node(numa_node);
+			if (!cpumask_intersects(node_mask, hk_mask))
 				continue;
 			break;
 		}
 		allocated_mask = &hv_context.hv_numa_map[numa_node];
 
 retry:
-		cpumask_xor(available_mask, allocated_mask, cpumask_of_node(numa_node));
+		cpumask_xor(available_mask, allocated_mask, node_mask);
 		cpumask_and(available_mask, available_mask, hk_mask);
 
 		if (cpumask_empty(available_mask)) {
Signature · 1 line
2.53.0
author maintainer
#19 lore raw

PATCH 19/247 x86/hyperv: Avoid using per-cpu output page in hv_apicid_to_vp_index()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Michael Kelley <[email protected]>

[ Upstream commit 7b74c117e2aac78e0f723e1c9c3644451c56b0d9 ]

hv_apicid_to_vp_index() currently uses the per-cpu hypercall input and output pages. This function is called when running in VTL2 and when running in an SEV-SNP CoCo VM with no paravisor. In the former case, the output page is allocated, but in the latter case it is not, so the hypervisor stores the output VP index in memory that has not been allocated by the guest.

Fix this by using the input page for both input and output. The hypercall has very small input and output, so sharing the same page for both is straightforward. An alternative fix is to allocate the per-cpu output page when running in an SEV-SNP CoCo guest, but this uses significantly more memory, particularly with larger vCPUs counts.

Fixes 86c48271e0d6 ("x86/hyperv: Fix APIC ID and VP index confusion in hv_snp_boot_ap()")
---
 arch/x86/hyperv/hv_init.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
arch/x86/hyperv/hv_init.c +2 -1
index 5d7e5b53e33ea..733802ffcc66e 100644
@@ -750,7 +750,8 @@ int hv_apicid_to_vp_index(u32 apic_id)
 	input->partition_id = HV_PARTITION_ID_SELF;
 	input->apic_ids[0] = apic_id;
 
-	output = *this_cpu_ptr(hyperv_pcpu_output_arg);
+	/* Treat input as having 2 APIC IDs so output is 64-bit aligned */
+	output = (void *)input + struct_size(input, apic_ids, 2);
 
 	control = HV_HYPERCALL_REP_COMP_1 | HVCALL_GET_VP_ID_FROM_APIC_ID;
 	status = hv_do_hypercall(control, input, output);
Signature · 1 line
2.53.0
author maintainer
#20 lore raw

PATCH 20/247 Drivers: hv: Dont reset VMBus connection state on error/exit paths

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Michael Kelley <[email protected]>

[ Upstream commit ec31dc39e835ccde0930daccece8c492d28282fc ]

In two places, current code forces vmbus_connection.conn_state to DISCONNECTED prior to calling vmbus_disconnect(). But vmbus_disconnect() calls vmbus_initiate_unload(), which unloads the VMBus connection only if the conn_state is CONNECTED. Consequently, the connection remains and the Hyper-V host might continue to send messages or post VMBus interrupts.

The problem was introduced in commit 74347a99e73ae ("x86/Hyper-V:
Unload vmbus channel in hv panic callback"), in that vmbus_initiate_unload() started checking the current connection state before doing the unload. This commit removed some occurrences where conn_state was forced to DISCONNECTED, but these two remained, apparently due to an oversight at the time.

Fix this by not forcing conn_state to DISCONNECTED during VMBus exit, or if a failure occurs when establishing the connection in the first place. Whatever value conn_state has at the time is then read by vmbus_initiate_unload() to determine if there is a connection to be unloaded.

Fixes 74347a99e73ae ("x86/Hyper-V: Unload vmbus channel in hv panic callback")
---
 drivers/hv/connection.c | 1 -
 drivers/hv/vmbus_drv.c  | 1 -
 2 files changed, 2 deletions(-)
drivers/hv/connection.c +0 -1
index 3cdba13b132f0..722c07ec05be4 100644
@@ -340,7 +340,6 @@ int vmbus_connect(void)
 cleanup:
 	pr_err("Unable to connect to host\n");
 
-	vmbus_connection.conn_state = DISCONNECTED;
 	vmbus_disconnect();
 
 	kfree(msginfo);
drivers/hv/vmbus_drv.c +0 -1
index 1ddbbc80cf13f..153633bda1426 100644
@@ -2899,7 +2899,6 @@ static void __exit vmbus_exit(void)
 
 	hv_remove_kexec_handler();
 	hv_remove_crash_handler();
-	vmbus_connection.conn_state = DISCONNECTED;
 	hv_stimer_global_cleanup();
 	vmbus_disconnect();
 	if (vmbus_irq == -1) {
Signature · 1 line
2.53.0
author maintainer
#21 lore raw

PATCH 21/247 pinctrl: qcom: ipq5018: add missing pwm3 function on gpio13

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Gilberto Conde <[email protected]>

[ Upstream commit c6c159fcdb4e9a678eda0831f971e4a3a488ec47 ]

GPIO13 supports the PWM3 function at mux select value 4, but the pingroup table leaves that slot empty and gpio13 is not listed in pwm3_groups. Selecting pwm3 on gpio13 is therefore rejected by the pinmux core:

ipq5018-tlmm: invalid group "gpio13" for function "pwm3"

The GL.iNet GL-X2000 drives its fan from PWM3 on this pin, and the vendor firmware muxes it with select value 4:

gpio13 : out low func4 8mA no pull

Add pwm3 to the fourth function slot of gpio13 and list gpio13 in pwm3_groups. With this the pin muxes as expected:

pin 13 (GPIO_13): device 1941010.pwm function pwm3 group gpio13

---
 drivers/pinctrl/qcom/pinctrl-ipq5018.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/pinctrl/qcom/pinctrl-ipq5018.c +2 -2
index 68f65b57003e9..18e68f7185bd5 100644
@@ -565,7 +565,7 @@ static const char * const qdss_cti_trig_out_b1_groups[] = {
 };
 
 static const char * const pwm3_groups[] = {
-	"gpio45",
+	"gpio45", "gpio13",
 };
 
 static const char * const qdss_cti_trig_in_b1_groups[] = {
@@ -691,7 +691,7 @@ static const struct msm_pingroup ipq5018_groups[] = {
 	PINGROUP(10, blsp0_spi, blsp1_uart0, led0, gcc_plltest, qdss_tracedata_a, _, _, _, _),
 	PINGROUP(11, blsp0_spi, blsp1_uart0, _, gcc_tlmm, qdss_tracedata_a, _, _, _, _),
 	PINGROUP(12, blsp0_spi, blsp0_i2c, blsp1_uart0, _, gcc_plltest, qdss_tracedata_a, _, _, _),
-	PINGROUP(13, blsp0_spi, blsp0_i2c, blsp1_uart0, _, qdss_tracedata_a, _, _, _, _),
+	PINGROUP(13, blsp0_spi, blsp0_i2c, blsp1_uart0, pwm3, qdss_tracedata_a, _, _, _, _),
 	PINGROUP(14, pcie0_clk, _, _, cri_trng0, qdss_tracedata_a, _, _, _, _),
 	PINGROUP(15, _, _, cri_trng1, qdss_tracedata_a, _, _, _, _, _),
 	PINGROUP(16, pcie0_wake, _, _, cri_trng, qdss_tracedata_a, _, _, _, _),
Signature · 1 line
2.53.0
author maintainer
#22 lore raw

PATCH 22/247 tools/power turbostat: Fix fd_perf leak on reset

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Len Brown <[email protected]>

[ Upstream commit 767c64414c4c5fffd6cbc3a1cf1fb8138b28645b ]

When turbostat notices a topology change,
it frees perf file descriptors for each CPU
to prepare to re-initialize.

Fix an off-by-one error in that per-cpu loop that resulted in a file descriptor leak of the file for the highest numbered CPU in the system.

Fixes 67bab430f4e7 ("tools/power turbostat: Group SMI counter with APERF and MPERF")
Assisted-by GitHub Copilot (claude-sonnet-4.6)
---
 tools/power/x86/turbostat/turbostat.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/power/x86/turbostat/turbostat.c +1 -1
index fb1c65f6ff9de..8b6180baa188d 100644
@@ -5194,7 +5194,7 @@ void free_fd_msr(void)
 	if (!msr_counter_info)
 		return;
 
-	for (int cpu = 0; cpu < topo.max_cpu_num; ++cpu) {
+	for (int cpu = 0; cpu <= topo.max_cpu_num; ++cpu) {
 		if (msr_counter_info[cpu].fd_perf != -1)
 			close(msr_counter_info[cpu].fd_perf);
 	}
Signature · 1 line
2.53.0
author maintainer
#23 lore raw

PATCH 23/247 tools/power turbostat: Fix add_counter issue if > 24 counters.

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Len Brown <[email protected]>

[ Upstream commit 154b96dab83fd953b9b82c8bcfa041cd6a9d74d3 ]

We allocate space for added counters ahead of time, and we arbitrarily chose 24 added counters of each type. If a future code flow hits this limit and continues, and then removes counters, we do a phantom increment and lose track of the number of added counters.

Increment the count only if there is no error.

Fixes 388e9c8134be ("tools/power turbostat: Make extensible via the --add parameter")
Assisted-by Claude: Claude Sonnet 4.6
---
 tools/power/x86/turbostat/turbostat.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
tools/power/x86/turbostat/turbostat.c +6 -3
index 8b6180baa188d..4947c38fc4f3c 100644
@@ -9330,10 +9330,11 @@ int add_counter(unsigned int msr_num, char *path, char *name,
 				fprintf(stderr, "%s: %s FOUND\n", __func__, name);
 			break;
 		}
-		if (sys.added_thread_counters++ >= MAX_ADDED_THREAD_COUNTERS) {
+		if (sys.added_thread_counters >= MAX_ADDED_THREAD_COUNTERS) {
 			warnx("ignoring thread counter %s", name);
 			return -1;
 		}
+		sys.added_thread_counters++;
 		break;
 	case SCOPE_CORE:
 		msrp = find_msrp_by_name(sys.cp, name);
@@ -9342,10 +9343,11 @@ int add_counter(unsigned int msr_num, char *path, char *name,
 				fprintf(stderr, "%s: %s FOUND\n", __func__, name);
 			break;
 		}
-		if (sys.added_core_counters++ >= MAX_ADDED_CORE_COUNTERS) {
+		if (sys.added_core_counters >= MAX_ADDED_CORE_COUNTERS) {
 			warnx("ignoring core counter %s", name);
 			return -1;
 		}
+		sys.added_core_counters++;
 		break;
 	case SCOPE_PACKAGE:
 		msrp = find_msrp_by_name(sys.pp, name);
@@ -9354,10 +9356,11 @@ int add_counter(unsigned int msr_num, char *path, char *name,
 				fprintf(stderr, "%s: %s FOUND\n", __func__, name);
 			break;
 		}
-		if (sys.added_package_counters++ >= MAX_ADDED_PACKAGE_COUNTERS) {
+		if (sys.added_package_counters >= MAX_ADDED_PACKAGE_COUNTERS) {
 			warnx("ignoring package counter %s", name);
 			return -1;
 		}
+		sys.added_package_counters++;
 		break;
 	default:
 		warnx("ignoring counter %s with unknown scope", name);
Signature · 1 line
2.53.0
author maintainer
#24 lore raw

PATCH 24/247 power: supply: generic-adc-battery: Fix temperature IIO channel conversion

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Svyatoslav Ryhel <[email protected]>

[ Upstream commit 6bca94e64255053ab2aa56f2588fda73d399b790 ]

Temperature IIO channel data is provided in milli-degrees, while POWER_SUPPLY_PROP_TEMP is expected to be in deci-degrees. Adjust the existing logic to reflect this conversion properly.

Fixes 33088c0513818 ("power: supply: generic-adc-battery: add temperature support")
---
 drivers/power/supply/generic-adc-battery.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/power/supply/generic-adc-battery.c +7 -2
index 7bdc6b2636090..aebc824aa2ede 100644
@@ -90,12 +90,17 @@ static int gab_read_channel(struct gab *adc_bat, enum gab_chan_type channel,
 	int ret;
 
 	ret = iio_read_channel_processed(adc_bat->channel[channel], result);
-	if (ret < 0)
+	if (ret < 0) {
 		dev_err(&adc_bat->psy->dev, "read channel error: %d\n", ret);
+		return ret;
+	}
+
+	if (channel == GAB_TEMP)
+		*result /= 100; /* Convert from milli- to deci-degree */
 	else
 		*result *= 1000;
 
-	return ret;
+	return 0;
 }
 
 static int gab_get_property(struct power_supply *psy,
Signature · 1 line
2.53.0
author maintainer
#25 lore raw

PATCH 25/247 pinctrl: qcom: ipq5018: replace gcc_plltest with pwm2 on gpio12

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Adriel Santos <[email protected]>

[ Upstream commit 709ba1c547eaf37da36ded2ecf3ea4e304cdfa9a ]

The pingroup table has gcc_plltest at function 5 of gpio12 and does not offer pwm2 on that pin, so the pinmux core rejects a request for pwm2 on gpio12 as an invalid group. On the hardware, function 5 of gpio12 carries the PWM2 output: the Xiaomi AX3000T v2 drives its blue status LED from PWM2 on gpio12.

The vendor kernel shipped on that router (Linux 4.4, stock firmware 2.0.28) has pwm2 in this slot. Its pingroup table, as read from the firmware image, lists these functions for gpio12:

gpio, blsp0_spi, blsp0_i2c, blsp1_uart0, _, pwm2, qdss_tracedata_a, _, _, _

and has pwm3 at function 4 of gpio13, matching commit c6c159fcdb4e ("pinctrl: qcom: ipq5018: add missing pwm3 function on gpio13").

Measured on the board: with gpio12 set to each of the 16 function values in turn, and PWM2 held at 0% and then at 100% duty, the pad level read back from GPIO_IN follows PWM2 only at function 5. There it does not follow PWM channels 0, 1 or 3, and the LED switches with PWM2. Functions 4 and 7, which the table leaves empty, do not carry PWM2.

Put pwm2 in function 5 of gpio12 and list gpio12 in pwm2_groups. Drop gpio12 from gcc_plltest_groups: none of its functions is gcc_plltest any more, and selecting it would trigger the WARN_ON() in msm_pinmux_set_mux().

Fixes 725d1c891658 ("pinctrl: qcom: Add IPQ5018 pinctrl driver")
Assisted-by LLM
---
 drivers/pinctrl/qcom/pinctrl-ipq5018.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/pinctrl/qcom/pinctrl-ipq5018.c +3 -3
index 18e68f7185bd5..2bbc409924f8b 100644
@@ -363,7 +363,7 @@ static const char * const blsp1_uart0_groups[] = {
 };
 
 static const char * const gcc_plltest_groups[] = {
-	"gpio10", "gpio12",
+	"gpio10",
 };
 
 static const char * const gcc_tlmm_groups[] = {
@@ -557,7 +557,7 @@ static const char * const qdss_cti_trig_in_b0_groups[] = {
 };
 
 static const char * const pwm2_groups[] = {
-	"gpio44",
+	"gpio12", "gpio44",
 };
 
 static const char * const qdss_cti_trig_out_b1_groups[] = {
@@ -690,7 +690,7 @@ static const struct msm_pingroup ipq5018_groups[] = {
 	PINGROUP(9, sdc1_clk, qspi_clk, _, qdss_tracedata_a, _, _, _, _, _),
 	PINGROUP(10, blsp0_spi, blsp1_uart0, led0, gcc_plltest, qdss_tracedata_a, _, _, _, _),
 	PINGROUP(11, blsp0_spi, blsp1_uart0, _, gcc_tlmm, qdss_tracedata_a, _, _, _, _),
-	PINGROUP(12, blsp0_spi, blsp0_i2c, blsp1_uart0, _, gcc_plltest, qdss_tracedata_a, _, _, _),
+	PINGROUP(12, blsp0_spi, blsp0_i2c, blsp1_uart0, _, pwm2, qdss_tracedata_a, _, _, _),
 	PINGROUP(13, blsp0_spi, blsp0_i2c, blsp1_uart0, pwm3, qdss_tracedata_a, _, _, _, _),
 	PINGROUP(14, pcie0_clk, _, _, cri_trng0, qdss_tracedata_a, _, _, _, _),
 	PINGROUP(15, _, _, cri_trng1, qdss_tracedata_a, _, _, _, _, _),
Signature · 1 line
2.53.0
author maintainer
#26 lore raw

PATCH 26/247 drm/vc4: hdmi: Move packet RAM enable to PM runtime

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Dave Stevenson <[email protected]>

[ Upstream commit 3f57b34485dc01e5a2db60a89a6ae5439a897e53 ]

Audio can be triggered by the HPD handler and start up before video has been restored, calling vc4_hdmi_write_infoframe() for the audio infoframe. vc4_hdmi_write_infoframe() needs Packet RAM to be on to store the infoframe, however, if the video hasn't been enabled, vc4_hdmi_encoder_post_crtc_enable() hasn't been called and therefore, the Packet RAM can be disabled by the time vc4_hdmi_write_infoframe() executes, causing the following splat:

[  201.570266] ------------[ cut here ]------------
[  201.570293] Packet RAM has to be on to store the packet.
[  201.570367] WARNING: drivers/gpu/drm/vc4/vc4_hdmi.c:683 at vc4_hdmi_write_infoframe+0x404/0x494 [vc4], CPU#0: wireplumber/1606
[...]
[  201.658650] Hardware name: Raspberry Pi 4 Model B Rev 1.5 (DT)
[...]
[  201.758499] Call trace:
[  201.760967]  vc4_hdmi_write_infoframe+0x404/0x494 [vc4] (P)
[  201.766607]  vc4_hdmi_write_audio_infoframe+0x1c/0x28 [vc4]
[  201.772247]  write_infoframe+0xbc/0x140 [drm_display_helper]
[  201.777975]  drm_atomic_helper_connector_hdmi_update_audio_infoframe+0x80/0xdc [drm_display_helper]
[  201.787140]  vc4_hdmi_audio_prepare+0x4ac/0x570 [vc4]
[  201.792252]  drm_connector_hdmi_audio_prepare+0x24/0x30 [drm_display_helper]
[  201.799390]  hdmi_codec_prepare+0x100/0x160 [snd_soc_hdmi_codec]
[  201.805471]  snd_soc_pcm_dai_prepare+0xd4/0x100 [snd_soc_core]
[  201.811375]  __soc_pcm_prepare+0x60/0x12c [snd_soc_core]
[  201.816752]  soc_pcm_prepare+0x34/0x58 [snd_soc_core]
[  201.821863]  snd_pcm_do_prepare+0x70/0x8c [snd_pcm]
[  201.826798]  snd_pcm_action_nonatomic+0xe4/0x174 [snd_pcm]
[  201.832350]  snd_pcm_prepare+0xc8/0xd8 [snd_pcm]
[  201.837021]  snd_pcm_common_ioctl+0x458/0x614 [snd_pcm]
[  201.842308]  snd_pcm_ioctl+0x30/0x48 [snd_pcm]
[  201.846802]  __arm64_sys_ioctl+0x88/0xd8
[  201.850769]  invoke_syscall+0x48/0xf4
[  201.854470]  el0_svc_common+0x80/0xd8
[  201.858171]  do_el0_svc+0x1c/0x28
[  201.861520]  el0_svc+0x3c/0xb0
[  201.864605]  el0t_64_sync_handler+0x68/0xdc
[  201.868834]  el0t_64_sync+0x198/0x19c
[  201.872536] ---[ end trace 0000000000000000 ]---

Considering that Packet RAM enable is essentially a power management configuration, move its enabling/disabling to the Runtime PM hooks.

Note that post_crtc_disable() still stops all infoframes, but keeps Packet RAM enabled, leaving it to be disabled by runtime suspend.

---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)
drivers/gpu/drm/vc4/vc4_hdmi.c +11 -9
index 9655027f1c0ab..7ea1622377b58 100644
@@ -857,7 +857,7 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder,
 
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
-	HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0);
+	HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, VC4_HDMI_RAM_PACKET_ENABLE);
 
 	HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_CLRRGB);
 
@@ -1616,9 +1616,6 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
 		WARN_ONCE(ret, "Timeout waiting for "
 			  "VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE\n");
 	} else {
-		HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
-			   HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
-			   ~(VC4_HDMI_RAM_PACKET_ENABLE));
 		HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
 			   HDMI_READ(HDMI_SCHEDULER_CONTROL) &
 			   ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
@@ -1637,9 +1634,6 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
 		WARN_ON(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) &
 			  VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE));
 
-		HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
-			   VC4_HDMI_RAM_PACKET_ENABLE);
-
 		spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
 		vc4_hdmi->packet_ram_enabled = true;
 
@@ -3125,6 +3119,11 @@ static int vc5_hdmi_init_resources(struct drm_device *drm,
 static int vc4_hdmi_runtime_suspend(struct device *dev)
 {
 	struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+	HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0);
+	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
 
 	clk_disable_unprepare(vc4_hdmi->hsm_clock);
 
@@ -3134,9 +3133,8 @@ static int vc4_hdmi_runtime_suspend(struct device *dev)
 static int vc4_hdmi_runtime_resume(struct device *dev)
 {
 	struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
-	unsigned long __maybe_unused flags;
+	unsigned long rate, flags;
 	u32 __maybe_unused value;
-	unsigned long rate;
 	int ret;
 
 	ret = clk_prepare_enable(vc4_hdmi->hsm_clock);
@@ -3178,6 +3176,10 @@ static int vc4_hdmi_runtime_resume(struct device *dev)
 	}
 #endif
 
+	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+	HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, VC4_HDMI_RAM_PACKET_ENABLE);
+	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
 	return 0;
 
 err_disable_clk:
Signature · 1 line
2.53.0
author maintainer
#27 lore raw

PATCH 27/247 vsock: treat TCP_CLOSING as once-established

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Michal Luczaj <[email protected]>

[ Upstream commit 4f6ac74fa594a918d41255c941256e1d51592d3b ]

Handle the TCP_ESTABLISHED -> TCP_CLOSING transition on OP_RST, which can race with the connect loop. Immediately break and return 0 on ESTABLISHED/CLOSING. The return value of connect() should reflect what happened up to the point the connection was established or failed. Events that occur afterwards must not affect it. E.g. even if OP_RW has already set sk_err, connect() should still return 0, not ENOBUFS because of it. Adapt the inaccurate comment above signal_pending().

Resetting a socket that is still present in connected_table can lead to memory corruption. The reporter noted lost transports for in-flight skbs, and I have reproduced crashes caused by re-insertion into connected_table.

list_add double add: new=, prev=, next=.
kernel BUG at lib/list_debug.c:35!

  Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
  Workqueue: vsock-loopback vsock_loopback_work
  RIP: 0010:__list_add_valid_or_report+0x11f/0x130
  Call Trace:
   vsock_insert_connected.cold+0xe/0x13
   virtio_transport_recv_pkt+0x10e9/0x1460
   vsock_loopback_work+0x305/0x480
   process_one_work+0xe4c/0x1560
   worker_thread+0x4f1/0xd60
   kthread+0x36e/0x470
   ret_from_fork+0x47b/0x6b0
   ret_from_fork_asm+0x1a/0x30

This fix is supplementary to commit 002541ef650b ("vsock: Ignore signal/timeout on connect() if already established"). Details under Link.

---
 net/vmw_vsock/af_vsock.c | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)
net/vmw_vsock/af_vsock.c +8 -9
index 1fa4dd2ca405b..228e45bf2d769 100644
@@ -1633,23 +1633,22 @@ static int vsock_connect(struct socket *sock, struct sockaddr *addr,
 		timeout = schedule_timeout(timeout);
 		lock_sock(sk);
 
-		/* Connection established. Whatever happens to socket once we
-		 * release it, that's not connect()'s concern. No need to go
+		/* Connection was established. Whatever happens to socket once
+		 * we release it, that's not connect()'s concern. No need to go
 		 * into signal and timeout handling. Call it a day.
 		 *
 		 * Note that allowing to "reset" an already established socket
 		 * here is racy and insecure.
 		 */
-		if (sk->sk_state == TCP_ESTABLISHED)
-			break;
+		if (sk->sk_state == TCP_ESTABLISHED ||
+		    sk->sk_state == TCP_CLOSING) {
+			err = 0;
+			goto out_wait;
+		}
 
 		/* If connection was _not_ established and a signal/timeout came
 		 * to be, we want the socket's state reset. User space may want
-		 * to retry.
-		 *
-		 * sk_state != TCP_ESTABLISHED implies that socket is not on
-		 * vsock_connected_table. We keep the binding and the transport
-		 * assigned.
+		 * to retry, so we keep the binding and the transport assigned.
 		 */
 		if (signal_pending(current) || timeout == 0) {
 			err = timeout == 0 ? -ETIMEDOUT : sock_intr_errno(timeout);
Signature · 1 line
2.53.0
author maintainer
#28 lore raw

PATCH 28/247 net/rds: dont let the rdma_cm destroy an id RDS still owns on route failure

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Allison Henderson <[email protected]>

[ Upstream commit 98d446b7c013254a8a7b49720557676a3a851fde ]

rds_rdma_cm_event_handler_cmn() hands the return value of rdma_resolve_route() straight back to the rdma_cm on RDMA_CM_EVENT_ADDR_RESOLVED. A synchronous failure there - -ENOMEM from the route work allocation, an SA query that cannot be set up, a RoCE route with no usable device - makes addr_handler() destroy the id the event was delivered on. That id is ic->i_cm_id, and nothing clears the pointer: the connection sits in RDS_CONN_CONNECTING with a freed id until its shutdown calls rdma_disconnect() and rdma_destroy_id() on it.

The ROUTE_RESOLVED case already avoids this: rds_ib_cm_initiate_connect() forces a zero return while ic->i_cm_id == cm_id, as the comment there explains. Do the same here - drop the connection and return 0 - so the id stays RDS's to destroy from the shutdown, and the reconnect gets a fresh one.

Fixes 55b7ed0b582f ("RDS: Common RDMA transport code")
Assisted-by LLM
---
 net/rds/rdma_transport.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)
net/rds/rdma_transport.c +11 -1
index b15cf316b23a2..21738a5690917 100644
@@ -89,9 +89,19 @@ static int rds_rdma_cm_event_handler_cmn(struct rdma_cm_id *cm_id,
 		if (conn) {
 			rdma_set_service_type(cm_id, conn->c_tos);
 			rdma_set_min_rnr_timer(cm_id, IB_RNR_TIMER_000_32);
-			/* XXX do we need to clean up if this fails? */
 			ret = rdma_resolve_route(cm_id,
 						 RDS_RDMA_RESOLVE_TIMEOUT_MS);
+			if (ret) {
+				/* A non-zero return has the rdma_cm destroy
+				 * the id, but it is still ic->i_cm_id, which
+				 * the connection's shutdown would then
+				 * disconnect and destroy again.  Drop the
+				 * connection and keep the id for that
+				 * shutdown.
+				 */
+				rds_conn_drop(conn);
+				ret = 0;
+			}
 		}
 		break;
 
Signature · 1 line
2.53.0
author maintainer
#29 lore raw

PATCH 29/247 ASoC: Intel: avs: Reset the stream unless it is already prepared

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Sergey Tiraspolsky <[email protected]>

[ Upstream commit f6be9f558a83fe30a03bd3adae461c821a0cef8c ]

Commit cfca1637bc2b ("ASoC: Intel: avs: Unprepare a stream when XRUN occurs") resets the HDA stream when ->prepare() is called in the XRUN state. User space does not always recover that way. PipeWire's ALSA backend recovers from an underrun with snd_pcm_drop(), snd_pcm_prepare() and a new start, so ->prepare() runs in the SETUP state, hdac_stream->prepared is still set and the reset is skipped. Every recovery attempt then fails the same way:

spa.alsa: hw:avsmax98927,1p: snd_pcm_avail after recover: Broken pipe

On a Google Pixelbook (Kaby Lake, max98927 speakers) this happens after an occasional underrun during normal playback and silences the speakers for one to four minutes, until the PCM is closed. It can be provoked by stopping PipeWire with SIGSTOP for 0.5 s during playback: in one run that single underrun led to 1297 failed recoveries over two minutes, until the sink was suspended. A plain alsa-lib program that recovers with snd_pcm_drop() and snd_pcm_prepare() hits the same loop, while snd_pcm_prepare() straight from XRUN recovers at once.

Skip the reset only when the stream is already PREPARED, the repeated ->prepare() case the check exists for, and reset it otherwise. With this change both kinds of recovery succeed with a single restart.

Fixes cfca1637bc2b ("ASoC: Intel: avs: Unprepare a stream when XRUN occurs")
Assisted-by Claude:claude-opus-5-5
---
 sound/soc/intel/avs/pcm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/intel/avs/pcm.c +1 -1
index 81fe4f0218c1a..78b330e225704 100644
@@ -660,7 +660,7 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so
 	data = snd_soc_dai_get_dma_data(dai, substream);
 	host_stream = data->host_stream;
 
-	if (runtime->state == SNDRV_PCM_STATE_XRUN)
+	if (runtime->state != SNDRV_PCM_STATE_PREPARED)
 		hdac_stream(host_stream)->prepared = false;
 	if (hdac_stream(host_stream)->prepared)
 		return 0;
Signature · 1 line
2.53.0
author maintainer
#30 lore raw

PATCH 30/247 ASoC: stm32: spdifrx: fix synchronization state check

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Avi Weiss <[email protected]>

[ Upstream commit 0936bbd35a247665c64886135835faa3c3f4f29f ]

The interrupt handler uses a logical AND to check whether SPDIFRX was in the SYNC state before clearing errors. Since both SYNC (1) and ENABLE (3) are nonzero, this also treats ENABLE as SYNC (and any other non-zero value that might be added).

The logical && basically just converts this to check for != 0.

So errors occurring while SPDIFRX is enabled cause the driver to retry synch instead of disconnecting the PCM stream.

Fix is to change the logical && to equality (==) and only retry when exactly in sync state.

Fixes 0c93c291321f2 ("ASoC: stm32: spdifrx: retry synchronization in sync state")
---
 sound/soc/stm/stm32_spdifrx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/stm/stm32_spdifrx.c +1 -1
index 9e30852de93cd..6cc6ff5f6c289 100644
@@ -728,7 +728,7 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
 
 	if (err) {
 		regmap_read(spdifrx->regmap, STM32_SPDIFRX_CR, &cr);
-		sync_state = FIELD_GET(SPDIFRX_CR_SPDIFEN_MASK, cr) &&
+		sync_state = FIELD_GET(SPDIFRX_CR_SPDIFEN_MASK, cr) ==
 			     SPDIFRX_SPDIFEN_SYNC;
 
 		/* SPDIFRX is in STATE_STOP. Disable SPDIFRX to clear errors */
Signature · 1 line
2.53.0
author maintainer
#31 lore raw

PATCH 31/247 ASoC: cs42l56: Put the Limiter Cushion control on the limiter register

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: HyeongJun An <[email protected]>

[ Upstream commit 203436d2aeb7093290459e344e1912a418a6ebfd ]

The Limiter Cushion control writes bits 4:2 of the ALC Thresholds register. Those bits are ALCMIN, which the ALC MIN control owns. The datasheet puts CUSH at bits 4:2 of the Limit Thresholds Control register, next to LMAX, which the Limiter MAX control already uses.

So setting the limiter cushion changes the ALC minimum threshold, and the cushion itself cannot be set.

Use CS42L56_LIM_THRESHOLD_CTL.

Fixes 272b5edd3b8f ("ASoC: Add support for CS42L56 CODEC")
Assisted-by Claude:claude-opus-5-5
---
 sound/soc/codecs/cs42l56.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/codecs/cs42l56.c +1 -1
index aaa10c459b52d..0058ce2d55e83 100644
@@ -449,7 +449,7 @@ static const struct snd_kcontrol_new cs42l56_snd_controls[] = {
 			0, 0x3f, 0, 0),
 	SOC_SINGLE_TLV("Limiter MAX", CS42L56_LIM_THRESHOLD_CTL,
 			5, 0x07, 1, alc_tlv),
-	SOC_SINGLE_TLV("Limiter Cushion", CS42L56_ALC_THRESHOLD,
+	SOC_SINGLE_TLV("Limiter Cushion", CS42L56_LIM_THRESHOLD_CTL,
 			2, 0x07, 1, alc_tlv),
 
 	SOC_SINGLE("NG Switch", CS42L56_NOISE_GATE_CTL, 6, 1, 1),
Signature · 1 line
2.53.0
author maintainer
#32 lore raw

PATCH 32/247 ASoC: cs42l52: Fix the NG Type Switch bit

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: HyeongJun An <[email protected]>

[ Upstream commit 8b08b98796d2da103f2432550c14a7c687e72080 ]

The NG Type Switch enum selects between "Apply Specific" and "Apply All", which is the NGALL bit. The datasheet puts NGALL at bit 7 of the Noise Gate Control register. The enum uses bit 6, which is NG, the noise gate enable owned by the NG Enable Switch. cs42l52.h names it CS42L52_NG_ENABLE_SHIFT. cs42l56 has the same Noise Gate register and puts its NG All Switch at bit 7.

So changing the noise gate type turns the noise gate on or off, and NGALL cannot be set.

Use bit 7.

Fixes dfe0f98b8d2e ("ASoC: Add support for CS42L52 Codec")
Assisted-by Claude:claude-opus-5-5
---
 sound/soc/codecs/cs42l52.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/codecs/cs42l52.c +1 -1
index 7128d4c62f504..dda012be02b96 100644
@@ -273,7 +273,7 @@ static const char * const cs42l52_ng_type_text[] = {
 };
 
 static SOC_ENUM_SINGLE_DECL(ng_type_enum,
-			    CS42L52_NOISE_GATE_CTL, 6,
+			    CS42L52_NOISE_GATE_CTL, 7,
 			    cs42l52_ng_type_text);
 
 static const char * const left_swap_text[] = {
Signature · 1 line
2.53.0
author maintainer
#33 lore raw

PATCH 33/247 ASoC: pcm3168a: Fix the DAC zero flag polarity bit

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: HyeongJun An <[email protected]>

[ Upstream commit b26edc925c1cfa0184cd270ed81d4833db045ce0 ]

The DAC Zero Flag Polarity enum uses PCM3168A_DAC_ATSPDA_SHIFT, which is bit 6 of register 70. That bit is ATSPDA, the attenuation speed, which the DAC Volume Rate Multiplier enum already owns. The datasheet puts ZREV, the zero flag polarity, at bit 0. The header defines PCM3168A_DAC_ZREV_SHIFT for it, but nothing uses it.

So changing the zero flag polarity changes the attenuation speed, and ZREV cannot be set.

Use PCM3168A_DAC_ZREV_SHIFT.

Fixes a9b17a638af5 ("ASoC: pcm3168a: Add driver for pcm3168a codec")
Assisted-by Claude:claude-opus-5-5
---
 sound/soc/codecs/pcm3168a.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/codecs/pcm3168a.c +1 -1
index ad1564e7d8faa..ce68b239b9ef9 100644
@@ -103,7 +103,7 @@ static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_func, PCM3168A_DAC_ATT_DEMP_ZF,
 static const char *const pcm3168a_pol[] = { "Active High", "Active Low" };
 
 static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_pol, PCM3168A_DAC_ATT_DEMP_ZF,
-		PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_pol);
+		PCM3168A_DAC_ZREV_SHIFT, pcm3168a_pol);
 
 static const char *const pcm3168a_con[] = { "Differential", "Single-Ended" };
 
Signature · 1 line
2.53.0
author maintainer
#34 lore raw

PATCH 34/247 ASoC: max98371: Fix the Speaker DHT Min Volume bits

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: HyeongJun An <[email protected]>

[ Upstream commit f7934340c56a84fc1b246ebf6b2e806d775f3b54 ]

Register 0x31 holds two 4-bit fields. The datasheet puts SPK_GAIN_MIN at bits 7:4 and DHT_VROT_PNT at bits 3:0. The Speaker DHT Min Volume and Speaker DHT Rotation Volume controls both use bits 3:0.

So setting the minimum speaker gain changes the DHT rotation point, and SPK_GAIN_MIN cannot be set.

Move Speaker DHT Min Volume to bits 7:4.

Fixes ca2cd6bc6663 ("ASoC: Add max98371 codec driver")
Assisted-by Claude:claude-opus-5-5
---
 sound/soc/codecs/max98371.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/codecs/max98371.c +1 -1
index 852db211ba1e7..c53f76f0ade76 100644
@@ -166,7 +166,7 @@ static const struct snd_kcontrol_new max98371_snd_controls[] = {
 			0, (1<<MAX98371_DHT_MAX_WIDTH)-1, 0,
 			max98371_dht_max_gain),
 	SOC_SINGLE_TLV("Speaker DHT Min Volume", MAX98371_DHT_GAIN,
-			0, (1<<MAX98371_DHT_GAIN_WIDTH)-1, 0,
+			4, (1<<MAX98371_DHT_GAIN_WIDTH)-1, 0,
 			max98371_dht_min_gain),
 	SOC_SINGLE_TLV("Speaker DHT Rotation Volume", MAX98371_DHT_GAIN,
 			0, (1<<MAX98371_DHT_ROT_WIDTH)-1, 0,
Signature · 1 line
2.53.0
author maintainer
#35 lore raw

PATCH 35/247 ASoC: wm8978: Fix the 128x oversampling switch bits

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: HyeongJun An <[email protected]>

[ Upstream commit d0567ca9a2faa33efbe5ae84ef7b86423a93bffd ]

The DAC and ADC 128x Oversampling Switches use bit 5 of R10 and R14. Table 66 of the WM8978 datasheet puts DACOSR128 and ADCOSR128 at bit 3 of those registers.

In R14, bit 5 is inside HPFCUT[6:4], which the High Pass Cut Off control owns. So turning on ADC oversampling changes the high pass cut-off, and the oversampling bit cannot be set. In R10, bit 5 is reserved, so the DAC switch has no effect.

Use bit 3 for both.

Fixes 823dba519122 ("ASoC: Fix broken bitfield definitions in WM8978")
Assisted-by Claude:claude-opus-5-5
---
 sound/soc/codecs/wm8978.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
sound/soc/codecs/wm8978.c +2 -2
index 40d22b36b7a96..a80538722dfdb 100644
@@ -255,9 +255,9 @@ static const struct snd_kcontrol_new wm8978_snd_controls[] = {
 
 	/* DAC / ADC oversampling */
 	SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL,
-		   5, 1, 0),
+		   3, 1, 0),
 	SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL,
-		   5, 1, 0),
+		   3, 1, 0),
 };
 
 /* Mixer #1: Output (OUT1, OUT2) Mixer: mix AUX, Input mixer output and DAC */
Signature · 1 line
2.53.0
author maintainer
#36 lore raw

PATCH 36/247 ASoC: nau8822: Fix the 128x oversampling switch bits

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: HyeongJun An <[email protected]>

[ Upstream commit 4db45fc1da3d62d0fa6e8f1e457ae92e8741ed07 ]

The DAC and ADC 128x Oversampling Switches use bit 5 of R10 and R14. The NAU8822 datasheet register map puts DACOS and ADCOS at bit 3 of those registers.

In R14, bit 5 is inside HPF[6:4], which the High Pass Cut Off control owns. So turning on ADC oversampling changes the high pass cut-off, and the oversampling bit cannot be set. In R10, bit 5 is reserved, so the DAC switch has no effect.

Use bit 3 for both.

Fixes 1e3cb6c321be ("ASoC: nau8822: new codec driver")
Assisted-by Claude:claude-opus-5-5
---
 sound/soc/codecs/nau8822.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
sound/soc/codecs/nau8822.c +2 -2
index fd4a96a12060e..dcf317ec97512 100644
@@ -380,9 +380,9 @@ static const struct snd_kcontrol_new nau8822_snd_controls[] = {
 		NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 0, 7, 0, boost_tlv),
 
 	SOC_SINGLE("DAC 128x Oversampling Switch",
-		NAU8822_REG_DAC_CONTROL, 5, 1, 0),
+		NAU8822_REG_DAC_CONTROL, 3, 1, 0),
 	SOC_SINGLE("ADC 128x Oversampling Switch",
-		NAU8822_REG_ADC_CONTROL, 5, 1, 0),
+		NAU8822_REG_ADC_CONTROL, 3, 1, 0),
 };
 
 /* LMAIN and RMAIN Mixer */
Signature · 1 line
2.53.0
author maintainer
#37 lore raw

PATCH 37/247 regcache: Add ->populate() callback to separate from ->init()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Andy Shevchenko <[email protected]>

[ Upstream commit 94a3a95f03154d8d4c6206950a7f6ef9a30baec6 ]

In the future changes we would like to change the flow of the cache handling. Add ->populate() callback in order to prepare for that.

Stable-dep-of: abff0a48b110 ("regmap: Drop map lock during cache init and exit")

---
 drivers/base/regmap/internal.h |  1 +
 drivers/base/regmap/regcache.c | 16 ++++++++++++++++
 2 files changed, 17 insertions(+)
drivers/base/regmap/internal.h +1 -0
index bdb450436cbc5..e3ce87ffdd8c2 100644
@@ -186,6 +186,7 @@ struct regcache_ops {
 	enum regcache_type type;
 	int (*init)(struct regmap *map);
 	int (*exit)(struct regmap *map);
+	int (*populate)(struct regmap *map);
 #ifdef CONFIG_DEBUG_FS
 	void (*debugfs_init)(struct regmap *map);
 #endif
drivers/base/regmap/regcache.c +16 -0
index d3659ba3cc112..d2630a55bb50b 100644
@@ -201,8 +201,24 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
 		if (ret)
 			goto err_free;
 	}
+
+	if (map->num_reg_defaults && map->cache_ops->populate) {
+		dev_dbg(map->dev, "Populating %s cache\n", map->cache_ops->name);
+		map->lock(map->lock_arg);
+		ret = map->cache_ops->populate(map);
+		map->unlock(map->lock_arg);
+		if (ret)
+			goto err_exit;
+	}
 	return 0;
 
+err_exit:
+	if (map->cache_ops->exit) {
+		dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name);
+		map->lock(map->lock_arg);
+		ret = map->cache_ops->exit(map);
+		map->unlock(map->lock_arg);
+	}
 err_free:
 	kfree(map->reg_defaults);
 	if (map->cache_free)
Signature · 1 line
2.53.0
author maintainer
#38 lore raw

PATCH 38/247 regcache: Do not overwrite error code when finalizing cache after error

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Francesco Lavra <[email protected]>

[ Upstream commit 9108f7fa493b4c88cbc09503e0c164244456bad5 ]

During regcache initialization, if an error occurs in the cache_ops->populate callback, and if cache operations include an exit callback, the error code from populate() is overwritten with the return value from exit(). This hides the error condition from the caller of regcache_init(), and can cause NULL pointer dereferences when the regcache is later accessed.

Fixes 94a3a95f0315 ("regcache: Add ->populate() callback to separate from ->init()")

Stable-dep-of: abff0a48b110 ("regmap: Drop map lock during cache init and exit")

---
 drivers/base/regmap/regcache.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/base/regmap/regcache.c +1 -1
index d2630a55bb50b..0e929e7ac68aa 100644
@@ -216,7 +216,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
 	if (map->cache_ops->exit) {
 		dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name);
 		map->lock(map->lock_arg);
-		ret = map->cache_ops->exit(map);
+		map->cache_ops->exit(map);
 		map->unlock(map->lock_arg);
 	}
 err_free:
Signature · 1 line
2.53.0
author maintainer
#39 lore raw

PATCH 39/247 regmap: Drop map lock during cache init and exit

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Michael Reeves <[email protected]>

[ Upstream commit abff0a48b110fe52d43999b4cb7c793a510a6bf1 ]

On systems with CONFIG_PROVE_RAW_LOCK_NESTING enabled, Apple GPIO probe triggers an "Invalid wait context" lockdep warning.

The driver uses a regmap with a raw spinlock and REGCACHE_FLAT. During regcache_init(), regcache_flat_init() allocates memory while holding the raw spinlock. The memory allocator can acquire a non-raw spinlock, which causes an invalid raw-to-non-raw lock nesting error. The abbreviated call chain is:

apple_gpio_pinctrl_probe
regcache_init
regcache_flat_init
__kmalloc_noprof
__pcs_replace_empty_main
refill_objects
_raw_spin_lock_irqsave

Cache allocation occurs before the regmap is published, cache destruction occurs after users have stopped accessing it, and regmap_reinit_cache() requires callers to prevent concurrent access. Remove the outer map lock from cache initialization and destruction. Keep it for cache population and normal register access.

The Maple cache still uses its internal spinlock during teardown. That lock can also be acquired from hard IRQ context during register access. Add Maple-state irqsave helpers and use them during teardown to preserve the lock's IRQ-safe lockdep state.

Fixes fd4ebc07b4df ("regmap: Hold the regmap lock when allocating and freeing the cache")
---
 drivers/base/regmap/regcache-maple.c | 9 +++++++--
 drivers/base/regmap/regcache.c       | 6 ------
 include/linux/maple_tree.h           | 4 ++++
 3 files changed, 11 insertions(+), 8 deletions(-)
drivers/base/regmap/regcache-maple.c +7 -2
index 34440e188f925..82d9410591654 100644
@@ -296,16 +296,21 @@ static int regcache_maple_exit(struct regmap *map)
 	struct maple_tree *mt = map->cache;
 	MA_STATE(mas, mt, 0, UINT_MAX);
 	unsigned int *entry;
+	unsigned long flags;
 
 	/* if we've already been called then just return */
 	if (!mt)
 		return 0;
 
-	mas_lock(&mas);
+	/*
+	 * Register access may take the Maple lock from hard IRQ context, so
+	 * preserve its IRQ-safe lockdep state during teardown.
+	 */
+	mas_lock_irqsave(&mas, flags);
 	mas_for_each(&mas, entry, UINT_MAX)
 		kfree(entry);
 	__mt_destroy(mt);
-	mas_unlock(&mas);
+	mas_unlock_irqrestore(&mas, flags);
 
 	kfree(mt);
 	map->cache = NULL;
drivers/base/regmap/regcache.c +0 -6
index 0e929e7ac68aa..5592cd704cf04 100644
@@ -195,9 +195,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
 	if (map->cache_ops->init) {
 		dev_dbg(map->dev, "Initializing %s cache\n",
 			map->cache_ops->name);
-		map->lock(map->lock_arg);
 		ret = map->cache_ops->init(map);
-		map->unlock(map->lock_arg);
 		if (ret)
 			goto err_free;
 	}
@@ -215,9 +213,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
 err_exit:
 	if (map->cache_ops->exit) {
 		dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name);
-		map->lock(map->lock_arg);
 		map->cache_ops->exit(map);
-		map->unlock(map->lock_arg);
 	}
 err_free:
 	kfree(map->reg_defaults);
@@ -241,9 +237,7 @@ void regcache_exit(struct regmap *map)
 	if (map->cache_ops->exit) {
 		dev_dbg(map->dev, "Destroying %s cache\n",
 			map->cache_ops->name);
-		map->lock(map->lock_arg);
 		map->cache_ops->exit(map);
-		map->unlock(map->lock_arg);
 	}
 }
 
include/linux/maple_tree.h +4 -0
index ecbcc2b3e59a6..4300f7746f2dc 100644
@@ -466,9 +466,13 @@ struct ma_wr_state {
 };
 
 #define mas_lock(mas)           spin_lock(&((mas)->tree->ma_lock))
+#define mas_lock_irqsave(mas, flags) \
+		spin_lock_irqsave(&((mas)->tree->ma_lock), flags)
 #define mas_lock_nested(mas, subclass) \
 		spin_lock_nested(&((mas)->tree->ma_lock), subclass)
 #define mas_unlock(mas)         spin_unlock(&((mas)->tree->ma_lock))
+#define mas_unlock_irqrestore(mas, flags) \
+		spin_unlock_irqrestore(&((mas)->tree->ma_lock), flags)
 
 /*
  * Special values for ma_state.node.
Signature · 1 line
2.53.0
author maintainer
#40 lore raw

PATCH 40/247 netlink: reset skb headers before unicast and multicast filtering

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kyle Zeng <[email protected]>

[ Upstream commit 8bf659bd54e1f00e34cf5244b1d289c506fa47c0 ]

Socket filters can use SKF_NET_OFF and SKF_LL_OFF to read relative to an skb's network and MAC headers. Netlink producers may reserve headroom without resetting those headers, allowing a receiving socket's filter to read data before the initialized message.

For example, IWPM HELLO replies are allocated with dev_alloc_skb(), which reserves NET_SKB_PAD bytes. An unprivileged NETLINK_RDMA user can attach a classic socket filter and disclose this headroom through the length of the delivered reply. The resets in netlink_dump() do not help because IWPM sends a separately allocated skb through netlink_unicast().

Reset both headers before running the receiver's filter in unicast and broadcast delivery, as is already done for dump skbs. This also covers other netlink producers that reserve headroom without changing their allocation or message-building code.

Fixes b1153f29ee07 ("netlink: make socket filters work on netlink")
Assisted-by LLM
---
 net/netlink/af_netlink.c | 4 ++++
 1 file changed, 4 insertions(+)
net/netlink/af_netlink.c +4 -0
index e250d4a3d0309..a0a8f185ccdeb 100644
@@ -1338,6 +1338,8 @@ int netlink_unicast(struct sock *ssk, struct sk_buff *skb,
 	if (netlink_is_kernel(sk))
 		return netlink_unicast_kernel(sk, skb, ssk);
 
+	skb_reset_network_header(skb);
+	skb_reset_mac_header(skb);
 	if (sk_filter(sk, skb)) {
 		err = skb->len;
 		kfree_skb(skb);
@@ -1472,6 +1474,8 @@ static void do_one_broadcast(struct sock *sk,
 		goto out;
 	}
 
+	skb_reset_network_header(p->skb2);
+	skb_reset_mac_header(p->skb2);
 	if (sk_filter(sk, p->skb2)) {
 		kfree_skb(p->skb2);
 		p->skb2 = NULL;
Signature · 1 line
2.53.0
author maintainer
#41 lore raw

PATCH 41/247 spi: tegra114: assert CS GPIO for every transfer in a message

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Carlos Jones Jr <[email protected]>

[ Upstream commit 8980bc573ae2d2b9d86d76aeb86c76e1597070bf ]

The driver provides its own transfer_one_message() and therefore drives the chip-select GPIO itself rather than through the SPI core. The GPIO is asserted only in the is_first_of_msg path of tegra_spi_setup_transfer_one(), but tegra_spi_transfer_end() deasserts it whenever a transfer has cs_change set. For a multi-transfer message whose earlier transfers request cs_change -- for example a register read that sends the address in one transfer and clocks the data back in the next -- the CS GPIO is released after the first transfer and never re-asserted, so the remaining transfers are clocked with chip-select inactive and the peripheral never drives the bus.

This was observed with an ADIS16470 IMU (adis16475 driver), whose register read is a two-transfer, cs_change-framed exchange: a logic analyzer showed the data frame clocked with CS high, and the driver read 0 while the device drove 0x4056 on the wire.

Re-assert the CS GPIO for every transfer so that multi-transfer messages keep chip-select active across the whole message. gpiod_set_value() is idempotent and polarity-aware, so this is a no-op for the first transfer and for messages that keep CS asserted throughout.

Fixes 63c1440596ff ("spi: tegra114: add support for gpio based CS")
---
 drivers/spi/spi-tegra114.c | 7 +++++++
 1 file changed, 7 insertions(+)
drivers/spi/spi-tegra114.c +7 -0
index 5b8bd34e6bfd9..622bcaa0fe6e9 100644
@@ -859,6 +859,13 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
 		command1 |= SPI_BIT_LENGTH(bits_per_word - 1);
 	}
 
+	/*
+	 * Re-assert the CS GPIO for every transfer: transfer_end() drops it
+	 * on cs_change, and the assert above only covers is_first_of_msg.
+	 */
+	if (spi_get_csgpiod(spi, 0))
+		gpiod_set_value(spi_get_csgpiod(spi, 0), 1);
+
 	return command1;
 }
 
Signature · 1 line
2.53.0
author maintainer
#42 lore raw

PATCH 42/247 nios2: fix __wsum type degradation in csum_fold()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Keith Busch <[email protected]>

[ Upstream commit b963f2345fa20417401c51a69dedb6a58eea33a9 ]

csum_fold() shifts a bitwise __wsum type, which sparse reports as "restricted __wsum degrades to integer". Sparse only checks the body of a static inline once something instantiates it, so this went unnoticed until a recent block layer change added the first csum_fold() caller to a file built on this architecture.

Force cast to a __u32 for the shift, as the other architectures do.

---
 arch/nios2/include/asm/checksum.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/nios2/include/asm/checksum.h +1 -1
index 69004e07a1ba3..b6b415ae17a08 100644
@@ -27,7 +27,7 @@ static inline __sum16 csum_fold(__wsum sum)
 		"add	%0, %0, r8\n"
 		"nor	%0, %0, %0\n"
 		: "=r" (sum)
-		: "r" (sum << 16), "0" (sum)
+		: "r" ((__force u32)sum << 16), "0" (sum)
 		: "r8");
 	return (__force __sum16) sum;
 }
Signature · 1 line
2.53.0
author maintainer
#43 lore raw

PATCH 43/247 scsi: st: Save the visited block for the partition the drive is in

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Laurence Oberman <[email protected]>

[ Upstream commit c9e9f024dd4f61f0f6636c9f2ed0830f87060e11 ]

set_location() saves the current tape location before switching partitions, so that switch_partition() can return to it later. It reads the location with READ POSITION but stores it in the st_partstat of STp->partition, ignoring the partition the drive reports.

The two differ after a device reset. When the reset is recovered with MTREW, MTSEEK or MTEOM, st_ioctl() sets STp->partition = 0 and calls switch_partition() to return to STp->new_partition. A drive that was in partition 1 is still there, so set_location() files the partition 1 position under partition 0. The next switch to partition 0 locates to that block: if partition 0 holds less data, MTSETPART fails with EIO (BLANK CHECK), otherwise the tape is silently positioned at the wrong block.

IBM ULTRIUM-TD5 (LTO-5), LU reset in partition 1 at block 1025:

st 12:0:0:0: [st0] Visited block 1025 for partition 0 saved.
...
st 12:0:0:0: [st0] Setting block to 1025 and partition to 0. st 12:0:0:0: [st0] Error: 402, cmd: 2b 2 0 0 0 4 st 12:0:0:0: [st0] Sense Key : Blank Check [current] st 12:0:0:0: [st0] Add. Sense: End-of-data detected

Store the location in the st_partstat of the partition reported by READ POSITION. With the fix:

st 12:0:0:0: [st0] Visited block 1025 for partition 1 saved.

This is a pre-existing issue. It was not caught when testing the recent st reset series because the partition test was only run on scsi_debug:
on the LTO drive it was skipped, since can-partitions is off in the test harness's stinit.conf, as is normal for LTO. scsi_debug hides the problem because its emulated tape returns to the beginning of partition 0 on an LU reset. It was found while validating a backport of that series to RHEL 9.8, when the partition tests were run on real hardware with can-partitions enabled. The same failure then reproduced on unmodified RHEL 9.8 and on upstream, and the fix makes the test pass on v7.3-rc3 and RHEL 9.8 with an LTO-5.

The harness (https://gitlab.com/loberman/tapetest, test X03) now refuses to silently skip the partition tests on hardware.

Fixes 7081dc75df79 ("scsi: st: Restore some drive settings after reset")
Assisted-by Claude
---
 drivers/scsi/st.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)
drivers/scsi/st.c +10 -4
index 3e982c166baf9..95da0d15e0490 100644
@@ -3194,14 +3194,20 @@ static int set_location(struct scsi_tape *STp, unsigned int block, int partition
 	    partition >= ST_NBR_PARTITIONS)
 		return (-EINVAL);
 	if (partition != STp->partition) {
-		if (get_location(STp, &blk, &p, 1))
+		/*
+		 * Save the location for the partition the drive reports,
+		 * not for STp->partition: after a reset STp->partition may
+		 * not match the partition the drive is in.
+		 */
+		if (get_location(STp, &blk, &p, 1) ||
+		    p < 0 || p >= ST_NBR_PARTITIONS)
 			STps->last_block_valid = 0;
 		else {
-			STps->last_block_valid = 1;
-			STps->last_block_visited = blk;
+			STp->ps[p].last_block_valid = 1;
+			STp->ps[p].last_block_visited = blk;
 			DEBC_printk(STp, "Visited block %d for "
 				    "partition %d saved.\n",
-				    blk, STp->partition);
+				    blk, p);
 		}
 	}
 
Signature · 1 line
2.53.0
author maintainer
#44 lore raw

PATCH 44/247 scsi: target: iscsi: Fix race in discovery auth updates

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Linfeng Sun <[email protected]>

[ Upstream commit e15b81f602edbdee39c335546f25486a1747f3f0 ]

Separate configfs opens can concurrently update the discovery AuthMethod parameter. Both callers may free the same value in iscsi_update_param_value(), causing a double-free.

Protect the lookup and replacement with the discovery TPG access lock, as used by the other TPG configfs parameter updates.

Fixes e48354ce078c ("iscsi-target: Add iSCSI fabric support for target v4.1")
---
 drivers/target/iscsi/iscsi_target_configfs.c | 26 ++++++++++++++------
 1 file changed, 19 insertions(+), 7 deletions(-)
drivers/target/iscsi/iscsi_target_configfs.c +19 -7
index 88db94f382bb9..5e01ab65cec5f 100644
@@ -1306,17 +1306,24 @@ static ssize_t iscsi_disc_enforce_discovery_auth_store(struct config_item *item,
 		return -EINVAL;
 	}
 
+	if (iscsit_get_tpg(discovery_tpg) < 0)
+		return -EINVAL;
+
 	param = iscsi_find_param_from_key(AUTHMETHOD,
 				discovery_tpg->param_list);
-	if (!param)
-		return -EINVAL;
+	if (!param) {
+		err = -EINVAL;
+		goto out;
+	}
 
 	if (op) {
 		/*
 		 * Reset the AuthMethod key to CHAP.
 		 */
-		if (iscsi_update_param_value(param, CHAP) < 0)
-			return -EINVAL;
+		if (iscsi_update_param_value(param, CHAP) < 0) {
+			err = -EINVAL;
+			goto out;
+		}
 
 		discovery_tpg->tpg_attrib.authentication = 1;
 		iscsit_global->discovery_acl.node_auth.enforce_discovery_auth = 1;
@@ -1327,8 +1334,10 @@ static ssize_t iscsi_disc_enforce_discovery_auth_store(struct config_item *item,
 		/*
 		 * Reset the AuthMethod key to CHAP,None
 		 */
-		if (iscsi_update_param_value(param, "CHAP,None") < 0)
-			return -EINVAL;
+		if (iscsi_update_param_value(param, "CHAP,None") < 0) {
+			err = -EINVAL;
+			goto out;
+		}
 
 		discovery_tpg->tpg_attrib.authentication = 0;
 		iscsit_global->discovery_acl.node_auth.enforce_discovery_auth = 0;
@@ -1337,7 +1346,10 @@ static ssize_t iscsi_disc_enforce_discovery_auth_store(struct config_item *item,
 			" Discovery TPG\n");
 	}
 
-	return count;
+	err = count;
+out:
+	iscsit_put_tpg(discovery_tpg);
+	return err;
 }
 
 CONFIGFS_ATTR(iscsi_disc_, enforce_discovery_auth);
Signature · 1 line
2.53.0
author maintainer
#45 lore raw

PATCH 45/247 ocfs2/dlm: Protect the recovery list during debugfs reads

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Cen Zhang <[email protected]>

[ Upstream commit 4c435828932bf46f29860e4a0375bcae101b778a ]

Recovery participant records must remain alive while debug_state_print() walks reco.node_data and formats their fields. The formatter holds dlm->spinlock, but the recovery participant list is protected by dlm_reco_state_lock. In particular, dlm_destroy_recovery_area() detaches the list under dlm_reco_state_lock and frees the detached records after releasing it. A concurrent debugfs open can therefore read freed memory.

Protect the participant traversal and field reads in debug_state_print() with dlm_reco_state_lock, matching the existing list writers. This also serializes the traversal against list_add_tail() during recovery-area initialization. Make the existing lock available to the debug code through dlmcommon.h; the lock remains internal to the OCFS2 DLM module.

Release dlm->spinlock after formatting the other domain state and before acquiring dlm_reco_state_lock, so the debug reader does not nest the two locks. Leave recovery list insertion, detachment and freeing unchanged.

KASAN report as below:

    BUG: KASAN: slab-use-after-free in debug_state_open+0x1169/0x12d0
    Read of size 4 at addr ffff88810662ef80 by task dlm-state-stres/896

    CPU: 1 UID: 0 PID: 896 Comm: dlm-state-stres Not tainted 7.3.0-rc4-next-20260921-pmb-ocfs2-functional-v1+ #1 PREEMPT(lazy)
    Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
    Call Trace:
     <TASK>
     dump_stack_lvl+0x93/0xd0
     print_report+0xce/0x630
     ? debug_state_open+0x1169/0x12d0
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? __virt_addr_valid+0x20e/0x420
     ? debug_state_open+0x1169/0x12d0
     kasan_report+0xe0/0x110
     ? debug_state_open+0x1169/0x12d0
     debug_state_open+0x1169/0x12d0
     ? __pfx_debug_state_open+0x10/0x10
     full_proxy_open_regular+0x193/0x310
     do_dentry_open+0x595/0x12d0
     ? __pfx_full_proxy_open_regular+0x10/0x10
     vfs_open_consume+0xd1/0x400
     ? srso_alias_return_thunk+0x5/0xfbef5
     path_openat+0x18d0/0x2020
     ? __pfx_path_openat+0x10/0x10
     do_file_open+0x21e/0x470
     ? __pfx_do_file_open+0x10/0x10
     ? _raw_spin_unlock+0x23/0x40
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? alloc_fd+0x3a6/0x6b0
     do_sys_openat2+0xf4/0x1b0
     ? __pfx_do_sys_openat2+0x10/0x10
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? __fput+0x5b5/0xa60
     __x64_sys_openat+0x136/0x1e0
     ? __pfx___x64_sys_openat+0x10/0x10
     do_syscall_64+0x114/0x620
     entry_SYSCALL_64_after_hwframe+0x77/0x7f
    [Register dump omitted.]
     </TASK>

    Allocated by task 880:
     kasan_save_stack+0x33/0x60
     kasan_save_track+0x14/0x30
     __kasan_kmalloc+0xaa/0xb0
     __kmalloc_cache_noprof+0x28d/0x610
     dlm_remaster_locks+0x147/0x1d90
     dlm_do_recovery+0xde1/0x1580
     dlm_recovery_thread+0x109/0x300
     kthread+0x351/0x460
     ret_from_fork+0x659/0x940
     ret_from_fork_asm+0x1a/0x30

    Freed by task 880:
     kasan_save_stack+0x33/0x60
     kasan_save_track+0x14/0x30
     kasan_save_free_info+0x3b/0x60
     __kasan_slab_free+0x5f/0x80
     kfree+0x308/0x580
     dlm_destroy_recovery_area+0x285/0x460
     dlm_remaster_locks+0x17c4/0x1d90
     dlm_do_recovery+0xde1/0x1580
     dlm_recovery_thread+0x109/0x300
     kthread+0x351/0x460
     ret_from_fork+0x659/0x940
     ret_from_fork_asm+0x1a/0x30

    The buggy address belongs to the object at ffff88810662ef80
     which belongs to the cache kmalloc-32 of size 32
    The buggy address is located 0 bytes inside of
     freed 32-byte region [ffff88810662ef80, ffff88810662efa0)

[Page and memory-state dumps omitted.]

---
 fs/ocfs2/dlm/dlmcommon.h   | 2 ++
 fs/ocfs2/dlm/dlmdebug.c    | 5 ++++-
 fs/ocfs2/dlm/dlmrecovery.c | 2 +-
 3 files changed, 7 insertions(+), 2 deletions(-)
fs/ocfs2/dlm/dlmcommon.h +2 -0
index 20f790a47484a..58b28dfa8d34e 100644
@@ -435,6 +435,8 @@ enum {
 	DLM_DEREF_LOCKRES_DONE		= 522,
 };
 
+extern spinlock_t dlm_reco_state_lock;
+
 struct dlm_reco_node_data
 {
 	int state;
fs/ocfs2/dlm/dlmdebug.c +4 -1
index e62ed98df7de4..afaea6396b6f9 100644
@@ -792,8 +792,11 @@ static int debug_state_print(struct dlm_ctxt *dlm, char *buf, int len)
 				 buf + out, len - out);
 	out += scnprintf(buf + out, len - out, "\n");
 
+	spin_unlock(&dlm->spinlock);
+
 	/* Recovery Node State: */
 	out += scnprintf(buf + out, len - out, "Recovery Node State:\n");
+	spin_lock(&dlm_reco_state_lock);
 	list_for_each_entry(node, &dlm->reco.node_data, list) {
 		switch (node->state) {
 		case DLM_RECO_NODE_DATA_INIT:
@@ -825,7 +828,7 @@ static int debug_state_print(struct dlm_ctxt *dlm, char *buf, int len)
 				node->node_num, state);
 	}
 
-	spin_unlock(&dlm->spinlock);
+	spin_unlock(&dlm_reco_state_lock);
 
 	return out;
 }
fs/ocfs2/dlm/dlmrecovery.c +1 -1
index 739da8bd7dbf0..591dd17ef4f5e 100644
@@ -80,7 +80,7 @@ static int dlm_lockres_master_requery(struct dlm_ctxt *dlm,
 
 static u64 dlm_get_next_mig_cookie(void);
 
-static DEFINE_SPINLOCK(dlm_reco_state_lock);
+DEFINE_SPINLOCK(dlm_reco_state_lock);
 static DEFINE_SPINLOCK(dlm_mig_cookie_lock);
 static u64 dlm_mig_cookie = 1;
 
Signature · 1 line
2.53.0
author maintainer
#46 lore raw

PATCH 46/247 samples: pidfd: fix userprogs typo

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

[ Upstream commit f1365f97fb7498d805981102eee22d362ae643f2 ]

The Makefile adds pidfd-metadata to "usertprogs-always-y", which kbuild does not know, so the sample has not been built since the conversion to userprogs, even with CONFIG_SAMPLE_PIDFD=y.

Fix the variable name. pidfd-metadata.c builds without warnings with gcc and clang for x86_64 and arm64.

Fixes 60fb0b1239719 ("samples: pidfd: build sample program for target architecture")
---
 samples/pidfd/Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
samples/pidfd/Makefile +1 -1
index 9754e2d81f70d..d82409f7061f3 100644
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
-usertprogs-always-y += pidfd-metadata
+userprogs-always-y += pidfd-metadata
 
 userccflags += -I usr/include
Signature · 1 line
2.53.0
author maintainer
#47 lore raw

PATCH 47/247 pipe: preserve existing fasync readers on writer setup failure

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kyle Zeng <[email protected]>

[ Upstream commit 40bb03d3ea6f6a915218508819ecc4d7a7fa260d ]

F_SETFL and FIOASYNC decide whether to invoke ->fasync() before pipe_fasync() takes pipe->mutex. Two enables of one O_RDWR FIFO can therefore both enter the callback.

fasync_helper() allocates before checking for an existing entry. If the first enable installs both entries, a second enable can update the reader entry and then fail the allocation for the writer entry. The error path removes the pre-existing reader entry, clearing FASYNC while the writer entry remains registered. __fput() then skips ->fasync(), so a later pipe notification can dereference the freed file or its f_owner. An unprivileged memory-pressure reproducer triggers a KASAN slab-use-after-free in send_sigio() through this path.

Keep the reader helper's result and undo the registration only if this call added it. The pipe mutex serializes changes to both lists, so a failed new registration still removes its reader, while a failed duplicate preserves the existing registrations and FASYNC. Successful duplicate enables continue to update fa_fd as before.

Fixes 76398425bb06 ("Move FASYNC bit handling to f_op->fasync()")
---
 fs/pipe.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
fs/pipe.c +4 -2
index 12b22c2723b7e..d2e75e726e63b 100644
@@ -753,9 +753,11 @@ pipe_fasync(int fd, struct file *filp, int on)
 	if (filp->f_mode & FMODE_READ)
 		retval = fasync_helper(fd, filp, on, &pipe->fasync_readers);
 	if ((filp->f_mode & FMODE_WRITE) && retval >= 0) {
+		int read_ret = retval;
+
 		retval = fasync_helper(fd, filp, on, &pipe->fasync_writers);
-		if (retval < 0 && (filp->f_mode & FMODE_READ))
-			/* this can happen only if on == T */
+		/* Undo only a reader entry added by this call. */
+		if (retval < 0 && read_ret > 0)
 			fasync_helper(-1, filp, 0, &pipe->fasync_readers);
 	}
 	mutex_unlock(&pipe->mutex);
Signature · 1 line
2.53.0
author maintainer
#48 lore raw

PATCH 48/247 tracing: probes: error if BTF is required but unavailable

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Martin Kaiser <[email protected]>

[ Upstream commit 66c658033d842bb75f81f20aaa4a378fe47be1a1 ]

traceprobe_expand_meta_args expands probe arguments based on BTF info.

Return an error if we have arguments that need expanding but BTF is not supported.

The params variable is now unused, it can be removed.

Fixes d157d7694460 ("tracing/probes: Support BTF field access from $retval")
---
 kernel/trace/trace_probe.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
kernel/trace/trace_probe.c +1 -2
index 95201c0daa8f0..c7e4758505f26 100644
@@ -1709,7 +1709,6 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[],
 					 int *new_argc, char *buf, int bufsize,
 					 struct traceprobe_parse_context *ctx)
 {
-	const struct btf_param *params = NULL;
 	int i, j, n, used, ret, args_idx = -1;
 	const char **new_argv = NULL;
 
@@ -1727,7 +1726,7 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[],
 		if (args_idx != -1) {
 			/* $arg* requires BTF info */
 			trace_probe_log_err(0, NOSUP_BTFARG);
-			return (const char **)params;
+			return ERR_PTR(-EOPNOTSUPP);
 		}
 		*new_argc = argc;
 		return NULL;
Signature · 1 line
2.53.0
author maintainer
#49 lore raw

PATCH 49/247 scsi: libsas: Fix SMP IO deadlock during HA resume

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xingui Yang <[email protected]>

[ Upstream commit 81e097a064b28b4a1f6ca1a8b433e659c1bd64c0 ]

smp_execute_task_sg() calls pm_runtime_get_sync() on the host before issuing an SMP command. When that command is itself issued from the HA resume path, the get_sync() deadlocks: it waits for the ongoing resume (the device is RPM_RESUMING), while the resume is blocked in sas_drain_work() waiting for that same SMP IO to complete.

The deadlock needs an expander-attached SATA disk. During sas_resume_ha() -> sas_drain_work(), DISCE_RESUME -> sas_resume_sata() -> ata_sas_port_resume() requests ATA_EH_RESET, and the hard reset for such a disk is done via SMP PHY CONTROL (sas_ata_hard_reset() -> sas_phy_reset() -> sas_smp_phy_control() -> smp_execute_task_sg()). Direct-attached SATA resets through lldd_control_phy() and SSP devices use TMFs, so neither hits this.

Replace the get_sync()/put_sync() pair with pm_runtime_get_noresume()/pm_runtime_put(). smp_execute_task_sg() only needs to hold off autosuspend while the SMP is in flight, and it must not try to resume the host: a sync resume issued from the HA resume path itself is what deadlocks, and by the time sas_resume_ha() runs, hw_init has already reinitialized the hardware, so the device is accessible without one.

The usage reference is still required. Discovery work normally runs inside an event worker's PM reference, taken at sas_notify_port_event() notify time and held until the handler has flushed the disco queue. sas_rediscover_ex_phy() however requeues DISCE_REVALIDATE_DOMAIN from within the revalidation worker itself, and flush_workqueue() does not wait for work items queued during execution, so that chained revalidation runs with no outer PM reference - without the get_noresume(), its SMP could race autosuspend.

For the BSG path, sas_smp_handler() is the only caller which may find the host autosuspended: expander SMP requests do not go through any SCSI device request queue, so nothing else in that path holds the host awake. Resume it there with pm_runtime_resume_and_get() and check the result.

Fixes 3dbbbf656b850 ("scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race")
---
 drivers/scsi/libsas/sas_expander.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)
drivers/scsi/libsas/sas_expander.c +12 -2
index 2b8004eb6f1b3..165c0c588c68b 100644
@@ -62,7 +62,11 @@ static int smp_execute_task_sg(struct domain_device *dev,
 		to_sas_internal(dev->port->ha->shost->transportt);
 	struct sas_ha_struct *ha = dev->port->ha;
 
-	pm_runtime_get_sync(ha->dev);
+	/*
+	 * Non-blocking: a sync resume here would deadlock against
+	 * sas_drain_work() during HA resume.
+	 */
+	pm_runtime_get_noresume(ha->dev);
 	mutex_lock(&dev->ex_dev.cmd_mutex);
 	for (retry = 0; retry < 3; retry++) {
 		if (test_bit(SAS_DEV_GONE, &dev->state)) {
@@ -135,7 +139,7 @@ static int smp_execute_task_sg(struct domain_device *dev,
 		}
 	}
 	mutex_unlock(&dev->ex_dev.cmd_mutex);
-	pm_runtime_put_sync(ha->dev);
+	pm_runtime_put(ha->dev);
 
 	BUG_ON(retry == 3 && task != NULL);
 	sas_free_task(task);
@@ -2165,6 +2169,11 @@ void sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
 		goto out;
 	}
 
+	/* The host may have autosuspended, resume it here. */
+	ret = pm_runtime_resume_and_get(dev->port->ha->dev);
+	if (ret)
+		goto out;
+
 	ret = smp_execute_task_sg(dev, job->request_payload.sg_list,
 			job->reply_payload.sg_list);
 	if (ret >= 0) {
@@ -2172,6 +2181,7 @@ void sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
 		rcvlen = job->reply_payload.payload_len - ret;
 		ret = 0;
 	}
+	pm_runtime_put(dev->port->ha->dev);
 
 out:
 	bsg_job_done(job, ret, rcvlen);
Signature · 1 line
2.53.0
author maintainer
#50 lore raw

PATCH 50/247 netlink: avoid hashing the network namespace pointer

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kyle Zeng <[email protected]>

[ Upstream commit f88bb3606e4bd11ee035b682cddc2999e604609a ]

The netlink rhashtable key includes a raw struct net pointer and a user-controlled port ID. Both /proc/net/netlink and socket diagnostics expose the table's bucket order. By binding and rebinding chosen NETLINK_USERSOCK port IDs, an unprivileged reader can distinguish equal buckets and recover the low bits of the Jenkins hash. Its 32-bit seed and the limited set of kernel-image slides can then be searched offline to recover the address of init_net.

Use the namespace's unique, non-address net_cookie in the comparison key instead. It is assigned before the per-net initializers run and remains unchanged for the namespace's lifetime. The lookup key and object hash are still built by netlink_compare_arg_init(), keeping lookup, insertion, removal and rehashing consistent while preserving namespace separation. Neither public table walker needs to change.

Reading the socket's namespace cookie in netlink_compare() is safe under RCU, including during namespace teardown. netlink_release() removes the socket from the hash table and defers its final put with call_rcu(). cleanup_net() runs the per-net exit methods and waits for outstanding RCU callbacks with rcu_barrier() before freeing namespace storage.

Unlike ns.ns_id, net_cookie is also available and initialized during setup_net() in older stable kernels, making the change straightforward to backport to v5.15 and later.

Fixes c428ecd1a21f ("netlink: Move namespace into hash key")
Assisted-by LLM
---
 net/netlink/af_netlink.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
net/netlink/af_netlink.c +3 -3
index a0a8f185ccdeb..7e1c087a98647 100644
@@ -463,7 +463,7 @@ netlink_unlock_table(void)
 
 struct netlink_compare_arg
 {
-	possible_net_t pnet;
+	u64 net_cookie;
 	u32 portid;
 };
 
@@ -478,14 +478,14 @@ static inline int netlink_compare(struct rhashtable_compare_arg *arg,
 	const struct netlink_sock *nlk = ptr;
 
 	return nlk->portid != x->portid ||
-	       !net_eq(sock_net(&nlk->sk), read_pnet(&x->pnet));
+	       sock_net(&nlk->sk)->net_cookie != x->net_cookie;
 }
 
 static void netlink_compare_arg_init(struct netlink_compare_arg *arg,
 				     struct net *net, u32 portid)
 {
 	memset(arg, 0, sizeof(*arg));
-	write_pnet(&arg->pnet, net);
+	arg->net_cookie = net->net_cookie;
 	arg->portid = portid;
 }
 
Signature · 1 line
2.53.0
author maintainer
#51 lore raw

PATCH 51/247 net: bcmgenet: unshare the skb before writing the control block

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nicolai Buchwitz <[email protected]>

[ Upstream commit 89771ce4f8f67dcffae561b2ccac95c0f6e3aa01 ]

bcmgenet stores first_cb, last_cb and bytes_sent in the skb control block and reads them back on completion to free the skb and credit BQL. ether_setup() leaves IFF_TX_SKB_SHARING set, so the stack can resubmit a shared skb that is still in the ring. The next transmit overwrites last_cb, so the earlier slots complete without crediting their bytes and BQL stalls the queue until the watchdog resets it.

Unshare the skb before writing the control block.

Reproduce with pktgen in clone_skb mode, which hands the driver the same skb while earlier copies are still in the ring:

modprobe pktgen
pg=/proc/net/pktgen
echo "add_device eth0" > $pg/kpktgend_0
echo "dst_mac AA:BB:CC:DD:EE:FF" > $pg/eth0
echo "dst 192.0.2.1" > $pg/eth0
echo "clone_skb 8" > $pg/eth0
echo "count 0" > $pg/eth0
echo start > $pg/pgctrl &
sleep 5
echo stop > $pg/pgctrl
wait
cat /sys/class/net/eth0/queues/tx-0/byte_queue_limits/inflight

inflight stays nonzero on the now idle queue. clone_skb 0 leaves it at zero.

---
 drivers/net/ethernet/broadcom/genet/bcmgenet.c | 10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/net/ethernet/broadcom/genet/bcmgenet.c +10 -0
index 7da4a2114a771..0402e8f69c6fa 100644
@@ -2095,6 +2095,16 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev)
 		goto out;
 	}
 
+	/* We store Tx state in the control block, so the skb must not be
+	 * shared, but ether_setup() leaves IFF_TX_SKB_SHARING set.
+	 */
+	skb = skb_share_check(skb, GFP_ATOMIC);
+	if (!skb) {
+		BCMGENET_STATS64_INC((&ring->stats64), dropped);
+		ret = NETDEV_TX_OK;
+		goto out;
+	}
+
 	/* Retain how many bytes will be sent on the wire, without TSB inserted
 	 * by transmit checksum offload
 	 */
Signature · 1 line
2.53.0
author maintainer
#52 lore raw

PATCH 52/247 net/ipv6: Introduce payload_len helpers

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Alice Mikityanska <[email protected]>

[ Upstream commit b2936b4fd56294e49d6c8e9152ea6c4982757c7d ]

The next commits will transition away from using the hop-by-hop extension header to encode packet length for BIG TCP. Add wrappers around ip6->payload_len that return the actual value if it's non-zero, and calculate it from skb->len if payload_len is set to zero (and a symmetrical setter).

The new helpers are used wherever the surrounding code supports the hop-by-hop jumbo header for BIG TCP IPv6, or the corresponding IPv4 code uses skb_ip_totlen (e.g., in include/net/netfilter/nf_tables_ipv6.h).

No behavioral change in this commit.

Stable-dep-of: 8070ed578b6b ("calipso: keep BIG TCP payload_len in calipso_skbuff_setattr()")

---
 include/linux/ipv6.h                       | 22 ++++++++++++++++++++++
 include/net/ipv6.h                         |  2 --
 include/net/netfilter/nf_tables_ipv6.h     |  4 ++--
 net/bridge/br_netfilter_ipv6.c             |  2 +-
 net/bridge/netfilter/nf_conntrack_bridge.c |  4 ++--
 net/ipv6/ip6_input.c                       |  2 +-
 net/ipv6/ip6_offload.c                     |  7 +++----
 net/ipv6/output_core.c                     |  7 +------
 net/netfilter/ipvs/ip_vs_xmit.c            |  2 +-
 net/netfilter/nf_conntrack_ovs.c           |  2 +-
 net/netfilter/nf_log_syslog.c              |  2 +-
 net/sched/sch_cake.c                       |  2 +-
 12 files changed, 36 insertions(+), 22 deletions(-)
include/linux/ipv6.h +22 -0
index ad5f09765e0c7..6f3c5d4049e24 100644
@@ -125,6 +125,28 @@ static inline unsigned int ipv6_transport_len(const struct sk_buff *skb)
 	       skb_network_header_len(skb);
 }
 
+static inline unsigned int
+ipv6_payload_len(const struct sk_buff *skb, const struct ipv6hdr *ip6)
+{
+	u32 len = ntohs(ip6->payload_len);
+
+	return (len || !skb_is_gso(skb) || !skb_is_gso_tcp(skb)) ?
+		len :
+		skb->len - skb_network_offset(skb) - sizeof(struct ipv6hdr);
+}
+
+static inline unsigned int skb_ipv6_payload_len(const struct sk_buff *skb)
+{
+	return ipv6_payload_len(skb, ipv6_hdr(skb));
+}
+
+#define IPV6_MAXPLEN		65535
+
+static inline void ipv6_set_payload_len(struct ipv6hdr *ip6, unsigned int len)
+{
+	ip6->payload_len = len <= IPV6_MAXPLEN ? htons(len) : 0;
+}
+
 /* 
    This structure contains results of exthdrs parsing
    as offsets from skb->nh.
include/net/ipv6.h +0 -2
index 3f3e312bced4f..e87eb9769516a 100644
@@ -25,8 +25,6 @@ struct ip_tunnel_info;
 
 #define SIN6_LEN_RFC2133	24
 
-#define IPV6_MAXPLEN		65535
-
 /*
  *	NextHeader field of IPv6 header
  */
include/net/netfilter/nf_tables_ipv6.h +2 -2
index a0633eeaec977..c53ac00bb974d 100644
@@ -42,7 +42,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)
 	if (ip6h->version != 6)
 		return -1;
 
-	pkt_len = ntohs(ip6h->payload_len);
+	pkt_len = ipv6_payload_len(pkt->skb, ip6h);
 	skb_len = pkt->skb->len - skb_network_offset(pkt->skb);
 	if (pkt_len + sizeof(*ip6h) > skb_len)
 		return -1;
@@ -86,7 +86,7 @@ static inline int nft_set_pktinfo_ipv6_ingress(struct nft_pktinfo *pkt)
 	if (ip6h->version != 6)
 		goto inhdr_error;
 
-	pkt_len = ntohs(ip6h->payload_len);
+	pkt_len = ipv6_payload_len(pkt->skb, ip6h);
 	if (pkt_len + sizeof(*ip6h) > pkt->skb->len) {
 		idev = __in6_dev_get(nft_in(pkt));
 		__IP6_INC_STATS(nft_net(pkt), idev, IPSTATS_MIB_INTRUNCATEDPKTS);
net/bridge/br_netfilter_ipv6.c +1 -1
index e0421eaa3abc7..76ce70b4e7f3b 100644
@@ -58,7 +58,7 @@ int br_validate_ipv6(struct net *net, struct sk_buff *skb)
 	if (hdr->version != 6)
 		goto inhdr_error;
 
-	pkt_len = ntohs(hdr->payload_len);
+	pkt_len = ipv6_payload_len(skb, hdr);
 	if (hdr->nexthdr == NEXTHDR_HOP && nf_ip6_check_hbh_len(skb, &pkt_len))
 		goto drop;
 
net/bridge/netfilter/nf_conntrack_bridge.c +2 -2
index cfa8091102426..b17962672ebfc 100644
@@ -234,7 +234,7 @@ static int nf_ct_br_ipv6_check(const struct sk_buff *skb)
 	if (hdr->version != 6)
 		return -1;
 
-	len = ntohs(hdr->payload_len) + sizeof(struct ipv6hdr) + nhoff;
+	len = ipv6_payload_len(skb, hdr) + sizeof(struct ipv6hdr) + nhoff;
 	if (skb->len < len)
 		return -1;
 
@@ -274,7 +274,7 @@ static unsigned int nf_ct_bridge_pre(void *priv, struct sk_buff *skb,
 		if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
 			return NF_ACCEPT;
 
-		len = sizeof(struct ipv6hdr) + ntohs(ipv6_hdr(skb)->payload_len);
+		len = sizeof(struct ipv6hdr) + skb_ipv6_payload_len(skb);
 		if (pskb_trim_rcsum(skb, len))
 			return NF_ACCEPT;
 
net/ipv6/ip6_input.c +1 -1
index f0a98c61a087a..54fc4b0eb57ba 100644
@@ -262,7 +262,7 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev,
 	skb->transport_header = skb->network_header + sizeof(*hdr);
 	IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
 
-	pkt_len = ntohs(hdr->payload_len);
+	pkt_len = ipv6_payload_len(skb, hdr);
 
 	/* pkt_len may be zero if Jumbo payload option is present */
 	if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
net/ipv6/ip6_offload.c +3 -4
index fce91183797a6..6762ce7909c8a 100644
@@ -372,12 +372,11 @@ INDIRECT_CALLABLE_SCOPE int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
 		hop_jumbo->jumbo_payload_len = htonl(payload_len + hoplen);
 
 		iph->nexthdr = NEXTHDR_HOP;
-		iph->payload_len = 0;
-	} else {
-		iph = (struct ipv6hdr *)(skb->data + nhoff);
-		iph->payload_len = htons(payload_len);
 	}
 
+	iph = (struct ipv6hdr *)(skb->data + nhoff);
+	ipv6_set_payload_len(iph, payload_len);
+
 	nhoff += sizeof(*iph) + ipv6_exthdrs_len(iph, &ops);
 	if (WARN_ON(!ops || !ops->callbacks.gro_complete))
 		goto out;
net/ipv6/output_core.c +1 -6
index d21fe27fe21e3..4396a6b58b607 100644
@@ -123,12 +123,7 @@ EXPORT_SYMBOL(ip6_dst_hoplimit);
 
 int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
-	int len;
-
-	len = skb->len - sizeof(struct ipv6hdr);
-	if (len > IPV6_MAXPLEN)
-		len = 0;
-	ipv6_hdr(skb)->payload_len = htons(len);
+	ipv6_set_payload_len(ipv6_hdr(skb), skb->len - sizeof(struct ipv6hdr));
 	IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
 
 	/* if egress device is enslaved to an L3 master device pass the
net/netfilter/ipvs/ip_vs_xmit.c +1 -1
index e0c2820be57ee..9589abea2f034 100644
@@ -985,7 +985,7 @@ ip_vs_prepare_tunneled_skb(struct sk_buff *skb, int skb_af,
 		*next_protocol = IPPROTO_IPV6;
 		if (payload_len)
 			*payload_len =
-				ntohs(old_ipv6h->payload_len) +
+				ipv6_payload_len(skb, old_ipv6h) +
 				sizeof(*old_ipv6h);
 		old_dsfield = ipv6_get_dsfield(old_ipv6h);
 		*ttl = old_ipv6h->hop_limit;
net/netfilter/nf_conntrack_ovs.c +1 -1
index 068e9489e1c27..a6988eeb15797 100644
@@ -121,7 +121,7 @@ int nf_ct_skb_network_trim(struct sk_buff *skb, int family)
 		len = skb_ip_totlen(skb);
 		break;
 	case NFPROTO_IPV6:
-		len = ntohs(ipv6_hdr(skb)->payload_len);
+		len = skb_ipv6_payload_len(skb);
 		if (ipv6_hdr(skb)->nexthdr == NEXTHDR_HOP) {
 			int err = nf_ip6_check_hbh_len(skb, &len);
 
net/netfilter/nf_log_syslog.c +1 -1
index de51b15cf53d9..e2c109d5f069c 100644
@@ -562,7 +562,7 @@ dump_ipv6_packet(struct net *net, struct nf_log_buf *m,
 
 	/* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */
 	nf_log_buf_add(m, "LEN=%zu TC=%u HOPLIMIT=%u FLOWLBL=%u ",
-		       ntohs(ih->payload_len) + sizeof(struct ipv6hdr),
+		       ipv6_payload_len(skb, ih) + sizeof(struct ipv6hdr),
 		       (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20,
 		       ih->hop_limit,
 		       (ntohl(*(__be32 *)ih) & 0x000fffff));
net/sched/sch_cake.c +1 -1
index a60aae42398a3..62390153abccc 100644
@@ -1263,7 +1263,7 @@ static struct sk_buff *cake_ack_filter(struct cake_sched_data *q,
 			    ipv6_addr_cmp(&ipv6h_check->daddr, &ipv6h->daddr))
 				continue;
 
-			seglen = ntohs(ipv6h_check->payload_len);
+			seglen = ipv6_payload_len(skb, ipv6h_check);
 		} else {
 			continue;
 		}
Signature · 1 line
2.53.0
author maintainer
#53 lore raw

PATCH 53/247 calipso: keep BIG TCP payload_len in calipso_skbuff_setattr()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Joas Antonio dos Santos <[email protected]>

[ Upstream commit 8070ed578b6b9134e045627fb745c887a806d46e ]

calipso_skbuff_setattr() adjusts payload_len by the length of the option it inserts or resizes:

	payload = ntohs(ip6_hdr->payload_len);
	ip6_hdr->payload_len = htons(payload + len_delta);

GRO can build BIG TCP packets larger than IPV6_MAXPLEN, and stores payload_len == 0 for them; ipv6_payload_len() then falls back to skb->len. For such a packet forwarded to a destination mapped to CALIPSO, the code above writes len_delta (or 65536 + len_delta when shrinking) into payload_len, and ipv6_payload_len() no longer falls back to skb->len. nf_tables, conntrack and sch_cake rely on it.

The same arithmetic also wraps when a packet grows past IPV6_MAXPLEN, and a BIG TCP packet that shrinks below IPV6_MAXPLEN should get its real length back.

Set payload_len from the skb length with ipv6_set_payload_len(), which stores 0 above IPV6_MAXPLEN and the real length otherwise, as ipv6_gro_complete() does. The network header was just reset to skb->data, so skb->len - sizeof(*ip6_hdr) is the payload length.

Tested on one kernel (arm64, QEMU) with SELinux permissive, a minimal mdp-generated policy (network_peer_controls=1), "netlabelctl calipso add pass doi:16" and a NetLabel mapping of fd00:2::/64 to CALIPSO DOI 16. calipso_skbuff_setattr() was called through netlbl_skbuff_setattr() from a test-only debugfs hook (not part of this patch) adding a level-only label; payload_len after the call:

case before fix after fix real length
1000, unlabeled 1016 1016 1016

  70000 GSO, payload_len 0             16           0         70016
  65530 GSO, grows past MAXPLEN        10           0         65546

65540 GSO with a 24 byte hop
header, shrinks below MAXPLEN 65528 65532 65532
1000 with a 24 byte hop header 992 992 992

(0 means ipv6_payload_len() returns the real length.) On the forward path, with a client injecting unlabeled UDP through a router to a server, the router adds the CALIPSO option and the server delivers all datagrams (Udp6InDatagrams 5, Ip6InTruncatedPkts 0) with this patch.

Fixes 81fbc812132c ("ipv6/gro: insert temporary HBH/jumbo header")
Assisted-by Claude:claude-opus-5-5
---
 net/ipv6/calipso.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
net/ipv6/calipso.c +3 -3
index f5cc02ea3092d..3ab22f9b416ec 100644
@@ -1319,7 +1319,7 @@ static int calipso_skbuff_setattr(struct sk_buff *skb,
 	struct ipv6hdr *ip6_hdr;
 	struct ipv6_opt_hdr *hop;
 	unsigned char buf[CALIPSO_MAX_BUFFER];
-	int len_delta, new_end, pad, payload;
+	int len_delta, new_end, pad;
 	unsigned int start, end;
 
 	ip6_hdr = ipv6_hdr(skb);
@@ -1358,8 +1358,8 @@ static int calipso_skbuff_setattr(struct sk_buff *skb,
 			sizeof(*ip6_hdr) + start);
 		skb_reset_network_header(skb);
 		ip6_hdr = ipv6_hdr(skb);
-		payload = ntohs(ip6_hdr->payload_len);
-		ip6_hdr->payload_len = htons(payload + len_delta);
+		/* 0 above IPV6_MAXPLEN (BIG TCP), the real length otherwise */
+		ipv6_set_payload_len(ip6_hdr, skb->len - sizeof(*ip6_hdr));
 	}
 
 	hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
Signature · 1 line
2.53.0
author maintainer
#54 lore raw

PATCH 54/247 gpiolib: fix memory leak in gpiod_set_array_value_complex() on error paths

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kent Gibson <[email protected]>

[ Upstream commit c28846ca1ac6134881114d272314623c8433e372 ]

A drive-by review by Sashiko reports that, after a failed call to gpiochip_set_multiple(), gpiod_set_array_value() returns abruptly, bypassing the freeing of possibly alloced bitmaps, potentially leaking memory. It is an unlikely error path, but is possible.

Defer the return code check until after the bitmap_free() block.

---
 drivers/gpio/gpiolib.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/gpio/gpiolib.c +5 -4
index 144f1e174c545..bffdaa91d86c5 100644
@@ -3578,16 +3578,17 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
 		} while ((i < array_size) &&
 			 gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc));
 		/* push collected bits to outputs */
-		if (count != 0) {
+		ret = 0;
+		if (count != 0)
 			ret = gpiochip_set_multiple(guard.gc, mask, bits);
-			if (ret)
-				return ret;
-		}
 
 		if (mask != fastpath_mask)
 			bitmap_free(mask);
 		if (bits != fastpath_bits)
 			bitmap_free(bits);
+
+		if (ret)
+			return ret;
 	}
 	return 0;
 }
Signature · 1 line
2.53.0
author maintainer
#55 lore raw

PATCH 55/247 gpio: mxc: use lock guards for the generic GPIO chip lock

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bartosz Golaszewski <[email protected]>

[ Upstream commit ba441322c7aac4735d78fc6781e497a01fb8eac7 ]

Simplify the code by using lock guards for the bgpio_lock.

Stable-dep-of: b7719c575dec ("gpio: mxc: fix both_edges bit operations")

---
 drivers/gpio/gpio-mxc.c | 48 ++++++++++++++++++-----------------------
 1 file changed, 21 insertions(+), 27 deletions(-)
drivers/gpio/gpio-mxc.c +21 -27
index a61bc17b27081..c404d3d56a9dc 100644
@@ -7,6 +7,7 @@
 // Authors: Daniel Mack, Juergen Beisert.
 // Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
 
+#include <linux/cleanup.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/init.h>
@@ -161,7 +162,6 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
 {
 	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 	struct mxc_gpio_port *port = gc->private;
-	unsigned long flags;
 	u32 bit, val;
 	u32 gpio_idx = d->hwirq;
 	int edge;
@@ -200,41 +200,38 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
 		return -EINVAL;
 	}
 
-	raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags);
+	scoped_guard(raw_spinlock_irqsave, &port->gc.bgpio_lock) {
+		if (GPIO_EDGE_SEL >= 0) {
+			val = readl(port->base + GPIO_EDGE_SEL);
+			if (edge == GPIO_INT_BOTH_EDGES)
+				writel(val | (1 << gpio_idx),
+				       port->base + GPIO_EDGE_SEL);
+			else
+				writel(val & ~(1 << gpio_idx),
+				       port->base + GPIO_EDGE_SEL);
+		}
 
-	if (GPIO_EDGE_SEL >= 0) {
-		val = readl(port->base + GPIO_EDGE_SEL);
-		if (edge == GPIO_INT_BOTH_EDGES)
-			writel(val | (1 << gpio_idx),
-				port->base + GPIO_EDGE_SEL);
-		else
-			writel(val & ~(1 << gpio_idx),
-				port->base + GPIO_EDGE_SEL);
-	}
+		if (edge != GPIO_INT_BOTH_EDGES) {
+			reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */
+			bit = gpio_idx & 0xf;
+			val = readl(reg) & ~(0x3 << (bit << 1));
+			writel(val | (edge << (bit << 1)), reg);
+		}
 
-	if (edge != GPIO_INT_BOTH_EDGES) {
-		reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */
-		bit = gpio_idx & 0xf;
-		val = readl(reg) & ~(0x3 << (bit << 1));
-		writel(val | (edge << (bit << 1)), reg);
+		writel(1 << gpio_idx, port->base + GPIO_ISR);
+		port->pad_type[gpio_idx] = type;
 	}
 
-	writel(1 << gpio_idx, port->base + GPIO_ISR);
-	port->pad_type[gpio_idx] = type;
-
-	raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags);
-
 	return port->gc.direction_input(&port->gc, gpio_idx);
 }
 
 static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
 {
 	void __iomem *reg = port->base;
-	unsigned long flags;
 	u32 bit, val;
 	int edge;
 
-	raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags);
+	guard(raw_spinlock_irqsave)(&port->gc.bgpio_lock);
 
 	reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
 	bit = gpio & 0xf;
@@ -250,12 +247,9 @@ static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
 	} else {
 		pr_err("mxc: invalid configuration for GPIO %d: %x\n",
 		       gpio, edge);
-		goto unlock;
+		return;
 	}
 	writel(val | (edge << (bit << 1)), reg);
-
-unlock:
-	raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags);
 }
 
 /* handle 32 interrupts in one status register */
Signature · 1 line
2.53.0
author maintainer
#56 lore raw

PATCH 56/247 gpio: mxc: fix both_edges bit operations

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Peng Fan <[email protected]>

[ Upstream commit b7719c575dec69146f6d537fd616b27083c0cf45 ]

gpio_set_irq_type() can be called concurrently for different pins on the same port. The IRQ core serializes per irq descriptor, but different pins have different descriptors, so on SMP systems two concurrent gpio_set_irq_type() calls for pins on the same port run without mutual exclusion.

The read-modify-write of port->both_edges is performed outside the port-wide scoped_guard(gpio_generic_lock_irqsave) section, so concurrent updates for different pins can overwrite each other, causing missed interrupts or stuck IRQ lines on dual-edge triggered pins.

The original uniprocessor MXC SoCs were not affected; the race became reachable on multi-core i.MX7 and i.MX8 SoCs that reuse this driver.

Convert both_edges from u32 to unsigned long and use set_bit(), clear_bit(), and test_bit() to make the modifications atomic.

---
 drivers/gpio/gpio-mxc.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/gpio/gpio-mxc.c +5 -4
index c404d3d56a9dc..e9120bb67db3e 100644
@@ -7,6 +7,7 @@
 // Authors: Daniel Mack, Juergen Beisert.
 // Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
 
+#include <linux/bitops.h>
 #include <linux/cleanup.h>
 #include <linux/clk.h>
 #include <linux/err.h>
@@ -67,7 +68,7 @@ struct mxc_gpio_port {
 	struct irq_domain *domain;
 	struct gpio_chip gc;
 	struct device *dev;
-	u32 both_edges;
+	unsigned long both_edges;
 	struct mxc_gpio_reg_saved gpio_saved_reg;
 	bool power_off;
 	u32 wakeup_pads;
@@ -167,7 +168,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
 	int edge;
 	void __iomem *reg = port->base;
 
-	port->both_edges &= ~(1 << gpio_idx);
+	clear_bit(gpio_idx, &port->both_edges);
 	switch (type) {
 	case IRQ_TYPE_EDGE_RISING:
 		edge = GPIO_INT_RISE_EDGE;
@@ -187,7 +188,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
 				edge = GPIO_INT_HIGH_LEV;
 				pr_debug("mxc: set GPIO %d to high trigger\n", gpio_idx);
 			}
-			port->both_edges |= 1 << gpio_idx;
+			set_bit(gpio_idx, &port->both_edges);
 		}
 		break;
 	case IRQ_TYPE_LEVEL_LOW:
@@ -258,7 +259,7 @@ static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
 	while (irq_stat != 0) {
 		int irqoffset = fls(irq_stat) - 1;
 
-		if (port->both_edges & (1 << irqoffset))
+		if (test_bit(irqoffset, &port->both_edges))
 			mxc_flip_edge(port, irqoffset);
 
 		generic_handle_domain_irq(port->domain, irqoffset);
Signature · 1 line
2.53.0
author maintainer
#57 lore raw

PATCH 57/247 gpio: mxc: use BIT() macro

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Alexander Stein <[email protected]>

[ Upstream commit 9de94681ee48770ec7e2062451a572b557bf9298 ]

Replace the open-code with the BIT() macro.

Stable-dep-of: 170aeff46fb5 ("gpio: mxc: fix wakeup_pads bit operations")

---
 drivers/gpio/gpio-mxc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/gpio/gpio-mxc.c +2 -2
index e9120bb67db3e..1131934e7880e 100644
@@ -330,13 +330,13 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
 			ret = enable_irq_wake(port->irq_high);
 		else
 			ret = enable_irq_wake(port->irq);
-		port->wakeup_pads |= (1 << gpio_idx);
+		port->wakeup_pads |= BIT(gpio_idx);
 	} else {
 		if (port->irq_high && (gpio_idx >= 16))
 			ret = disable_irq_wake(port->irq_high);
 		else
 			ret = disable_irq_wake(port->irq);
-		port->wakeup_pads &= ~(1 << gpio_idx);
+		port->wakeup_pads &= ~BIT(gpio_idx);
 	}
 
 	return ret;
Signature · 1 line
2.53.0
author maintainer
#58 lore raw

PATCH 58/247 gpio: mxc: fix wakeup_pads bit operations

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Peng Fan <[email protected]>

[ Upstream commit 170aeff46fb54f41545c85a72aec525aebc79d44 ]

gpio_set_wake_irq() can be called concurrently for different pins on the same port. The writer-vs-writer race is the concern: two concurrent gpio_set_wake_irq() calls for different pins on the same port both do a non-atomic read-modify-write on the same wakeup_pads word. The old |= and &= compile to separate load/modify/store instructions that can interleave, losing one CPU's update.

assign_bit() uses an atomic RMW instruction, so concurrent writers to different bits in the same word are safe.

The reader side (for_each_set_bit in noirq_suspend, wakeup_pads > 0) runs in noirq context where no concurrent writer can execute, so reader atomicity is not needed.

wakeup_pads is a u32, while assign_bit() operates on unsigned long pointers. On 64-bit platforms, this causes an 8-byte read-modify-write on a 4-byte field, corrupting the adjacent is_pad_wakeup. So change wakeup_pads to unsigned long and reorder to avoid wasting words.

And fix the enable/disable path unconditionally sets/clears the wakeup_pads bit even when enable_irq_wake()/disable_irq_wake() fails. Only update the bit on success.

Fixes f60c9eac54af ("gpio: mxc: enable pad wakeup on i.MX8x platforms")
Assisted-by LLM
---
 drivers/gpio/gpio-mxc.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)
drivers/gpio/gpio-mxc.c +6 -4
index 1131934e7880e..30e03acbe8596 100644
@@ -71,8 +71,8 @@ struct mxc_gpio_port {
 	unsigned long both_edges;
 	struct mxc_gpio_reg_saved gpio_saved_reg;
 	bool power_off;
-	u32 wakeup_pads;
 	bool is_pad_wakeup;
+	unsigned long wakeup_pads;
 	u32 pad_type[32];
 	const struct mxc_gpio_hwdata *hwdata;
 };
@@ -330,16 +330,18 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
 			ret = enable_irq_wake(port->irq_high);
 		else
 			ret = enable_irq_wake(port->irq);
-		port->wakeup_pads |= BIT(gpio_idx);
 	} else {
 		if (port->irq_high && (gpio_idx >= 16))
 			ret = disable_irq_wake(port->irq_high);
 		else
 			ret = disable_irq_wake(port->irq);
-		port->wakeup_pads &= ~BIT(gpio_idx);
 	}
+	if (ret)
+		return ret;
 
-	return ret;
+	assign_bit(gpio_idx, &port->wakeup_pads, enable);
+
+	return 0;
 }
 
 static int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
Signature · 1 line
2.53.0
author maintainer
#59 lore raw

PATCH 59/247 perf: Fix perf_pmu_migrate_context() leaving events behind

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Haris Okanovic <[email protected]>

[ Upstream commit e76b36b4be7160eaa14b416681e548b125702395 ]

__perf_pmu_remove() walks the group leaders of a CPU and PMU with perf_event_groups_for_cpu_pmu(), removing each one as it goes. perf_remove_from_context() takes the leader out of the groups rbtree, after which rb_next() on that node returns NULL, prematurely ending the walk. Other groups stay behind in the source context.

On CPU hotplug, they are then torn down with the dying CPU. They keep running until teardown, while the PMU's interrupt and migrated events are on a new CPU, which drivers rely on never happening.

Walk ctx->event_list instead and pick out the PMU's events by their pmu_ctx.

---
 kernel/events/core.c | 20 +++++++-------------
 1 file changed, 7 insertions(+), 13 deletions(-)
kernel/events/core.c +7 -13
index 6c9c3bfc0da27..a076236b3a49b 100644
@@ -13326,23 +13326,18 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
 }
 EXPORT_SYMBOL_GPL(perf_event_create_kernel_counter);
 
-static void __perf_pmu_remove(struct perf_event_context *ctx,
-			      int cpu, struct pmu *pmu,
-			      struct perf_event_groups *groups,
+static void __perf_pmu_remove(struct perf_event_context *ctx, struct pmu *pmu,
 			      struct list_head *events)
 {
-	struct perf_event *event, *sibling;
+	struct perf_event *event, *tmp;
+
+	list_for_each_entry_safe(event, tmp, &ctx->event_list, event_entry) {
+		if (event->pmu_ctx->pmu != pmu)
+			continue;
 
-	perf_event_groups_for_cpu_pmu(event, groups, cpu, pmu) {
 		perf_remove_from_context(event, 0);
 		put_pmu_ctx(event->pmu_ctx);
 		list_add(&event->migrate_entry, events);
-
-		for_each_sibling_event(sibling, event) {
-			perf_remove_from_context(sibling, 0);
-			put_pmu_ctx(sibling->pmu_ctx);
-			list_add(&sibling->migrate_entry, events);
-		}
 	}
 }
 
@@ -13418,8 +13413,7 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu)
 	 */
 	mutex_lock_double(&src_ctx->mutex, &dst_ctx->mutex);
 
-	__perf_pmu_remove(src_ctx, src_cpu, pmu, &src_ctx->pinned_groups, &events);
-	__perf_pmu_remove(src_ctx, src_cpu, pmu, &src_ctx->flexible_groups, &events);
+	__perf_pmu_remove(src_ctx, pmu, &events);
 
 	if (!list_empty(&events)) {
 		/*
Signature · 1 line
2.53.0
author maintainer
#60 lore raw

PATCH 60/247 printk: Postpone kthread startup

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: John Ogness <[email protected]>

[ Upstream commit 5f339c46f153184570f16c8e789c9709b14cbe8b ]

There have been various bug reports related to SMP systems hanging on boot. The reports vary but seem to have some overlap with regard to missed IPI's during SMP setup. The reports also show that reverting NBCON console usage allows the bugs to disappear.

The NBCON kthreads for console printing are started very early during boot using early_initcall(). Since these kthreads are asynchronously enabling/disabling interrupts during early boot, they may be interfering with early SMP setup on some hardware platforms.

Starting the console printing kthreads later has shown to avoid the hanging on systems that could previously reproduce the issue.

Postpone kthread starting until after all arch initcalls have completed.

---
 kernel/printk/printk.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/printk/printk.c +1 -1
index d5c1dff1617c4..72845f87089b6 100644
@@ -3767,7 +3767,7 @@ static int __init printk_set_kthreads_ready(void)
 
 	return 0;
 }
-early_initcall(printk_set_kthreads_ready);
+subsys_initcall(printk_set_kthreads_ready);
 #endif /* CONFIG_PRINTK */
 
 static int __read_mostly keep_bootcon;
Signature · 1 line
2.53.0
author maintainer
#61 lore raw

PATCH 61/247 PCI: Dont treat ~0 as ready in pci_dev_wait() with RRS SV

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Alexander Gruhlke <[email protected]>

[ Upstream commit e8df1d194c39b7a5159d570798941851f70a8668 ]

With RRS Software Visibility, pci_dev_wait() considers a device ready as soon as reading the Vendor ID doesn't return the RRS value. Devices aren't required to respond with RRS while they aren't ready, so the read may return ~0 and pci_dev_wait() stops waiting too early. This was seen with Intel [8086:0a54] and Samsung [144d:a80c] NVMe SSDs.

Also wait while the Vendor ID reads ~0. A VF's Vendor ID always reads ~0, so check its Command register instead, as is done without RRS SV.

Fixes d591f6804e7e ("PCI: Wait for device readiness with Configuration RRS")
Assisted-by Claude:claude-opus-5-5
---
 drivers/pci/pci.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)
drivers/pci/pci.c +11 -4
index 0d33b30ec2f67..f96801d1c833f 100644
@@ -1312,9 +1312,11 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
 	 * initialize.
 	 *
 	 * If the device is below a Root Port with Configuration RRS
-	 * Software Visibility enabled, reading the Vendor ID returns a
+	 * Software Visibility enabled, reading the Vendor ID may return a
 	 * special data value if the device responded with RRS.  Read the
-	 * Vendor ID until we get non-RRS status.
+	 * Vendor ID until we get non-RRS status and something other than
+	 * ~0.  The Vendor ID of VFs reads ~0, so check their Command
+	 * register instead.
 	 *
 	 * If there's no Root Port or Configuration RRS Software Visibility
 	 * is not enabled, the device may still respond with RRS, but
@@ -1334,8 +1336,13 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
 
 		if (root && root->config_rrs_sv) {
 			pci_read_config_dword(dev, PCI_VENDOR_ID, &id);
-			if (!pci_bus_rrs_vendor_id(id))
-				break;
+			if (!pci_bus_rrs_vendor_id(id)) {
+				if (PCI_POSSIBLE_ERROR(id) && dev->is_virtfn)
+					pci_read_config_dword(dev, PCI_COMMAND,
+							      &id);
+				if (!PCI_POSSIBLE_ERROR(id))
+					break;
+			}
 		} else {
 			pci_read_config_dword(dev, PCI_COMMAND, &id);
 			if (!PCI_POSSIBLE_ERROR(id))
Signature · 1 line
2.53.0
author maintainer
#62 lore raw

PATCH 62/247 afs: free the super info after a grace period

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Christian Brauner <[email protected]>

commit 59e483739f2fd14327ece3f60c4995d060ca46b7 upstream.

afs_atcell_get_link() resolves the @cell and .@cell symlinks of a dynamic root in rcu pathwalk mode. Before it looks at the dentry it gets the afs_net through afs_i2net(), which reads ->net_ns from the afs_super_info in sb->s_fs_info. The @cell dentries are kept around when unused and the dynamic root has no ->d_revalidate(), so a lookup that finds @cell in the dcache gets to ->get_link() in rcu mode.

afs_kill_super() frees the afs_super_info with a plain kfree() in afs_destroy_sbi(). Right now a pathwalk can't still be in afs_atcell_get_link() at that point, but only by accident. umount(2) waits for a grace period in namespace_unlock() before it drops the mount. And a mount whose last reference goes away elsewhere, like the chdir out of a lazily unmounted directory, still gets a grace period from bdi_unregister(). afs_fill_super() sets up a private bdi, so generic_shutdown_super() unregisters it from kill_anon_super() and bdi_remove_from_list() waits in synchronize_rcu_expedited() before afs_destroy_sbi() runs. That grace period protects the rcu readers of bdi_list in the writeback code. It has nothing to do with pathwalk.

Don't rely on it and free the afs_super_info after a grace period.

---
 fs/afs/internal.h |    2 ++
 fs/afs/super.c    |    2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)
fs/afs/internal.h +2 -0
@@ -264,6 +264,8 @@ struct afs_super_info {
 	struct afs_volume	*volume;	/* volume record */
 	enum afs_flock_mode	flock_mode:8;	/* File locking emulation mode */
 	bool			dyn_root;	/* True if dynamic root */
+	/* afs_atcell_get_link() reads ->net_ns in rcu pathwalk */
+	struct rcu_head		rcu;
 };
 
 static inline struct afs_super_info *AFS_FS_S(struct super_block *sb)
fs/afs/super.c +1 -1
@@ -523,7 +523,7 @@ static void afs_destroy_sbi(struct afs_s
 		afs_put_volume(as->volume, afs_volume_trace_put_destroy_sbi);
 		afs_unuse_cell(as->cell, afs_cell_trace_unuse_sbi);
 		put_net(as->net_ns);
-		kfree(as);
+		kfree_rcu(as, rcu);
 	}
 }
 
author maintainer
#63 lore raw

PATCH 63/247 autofs: ignore notification errors from a replaced pipe

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Sung Byeongchan <[email protected]>

commit 65676cd4203c0e41a94caf54080a7f0a95434bda upstream.

autofs_notify_daemon() takes a reference to the current notification pipe under wq_mutex, then drops the mutex before writing the request. The write can block while the daemon enters catatonic mode and installs a replacement pipe.

If the old pipe's reader is then closed, the delayed write returns -EPIPE and the generic error path enters catatonic mode again. That transition acts on the current superblock state, closes the replacement pipe, and releases the new daemon generation's wait queues.

Only enter catatonic mode when the pipe which failed is still the current pipe. Factor the already-locked transition so the identity check and state change are atomic with respect to another replacement.

This was reproduced on v7.3-rc5-337-gff47652a4b66c in a local QEMU guest. An unprivileged UID 65534 lookup filled the old packet pipe before a normal CATATONIC/SETPIPEFD restart. On the unmodified kernel, the new pipe received HUP and both a pending victim lookup and a later lookup failed with ENOENT in two out of two fresh-mount runs.

With this change, the new pipe remained active, received an intact 304-byte request, and both victim lookups completed in two out of two runs. Normal lookup and restart-without-a-blocked-writer controls passed. No KASAN, oops, refcount, or lock diagnostic was observed. A source reproducer and complete local logs are available privately on request.

The demonstrated impact is limited to denial of a shared autofs service during a legitimate daemon restart. No memory corruption, information leak, privilege escalation, or code execution primitive was observed.

---
 fs/autofs/waitq.c |   25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)
fs/autofs/waitq.c +19 -6
@@ -12,15 +12,13 @@
  */
 static autofs_wqt_t autofs_next_wait_queue = 1;
 
-void autofs_catatonic_mode(struct autofs_sb_info *sbi)
+static void autofs_catatonic_mode_locked(struct autofs_sb_info *sbi)
 {
 	struct autofs_wait_queue *wq, *nwq;
 
-	mutex_lock(&sbi->wq_mutex);
-	if (sbi->flags & AUTOFS_SBI_CATATONIC) {
-		mutex_unlock(&sbi->wq_mutex);
+	lockdep_assert_held(&sbi->wq_mutex);
+	if (sbi->flags & AUTOFS_SBI_CATATONIC)
 		return;
-	}
 
 	pr_debug("entering catatonic mode\n");
 
@@ -40,6 +38,21 @@ void autofs_catatonic_mode(struct autofs
 	fput(sbi->pipe);	/* Close the pipe */
 	sbi->pipe = NULL;
 	sbi->pipefd = -1;
+}
+
+void autofs_catatonic_mode(struct autofs_sb_info *sbi)
+{
+	mutex_lock(&sbi->wq_mutex);
+	autofs_catatonic_mode_locked(sbi);
+	mutex_unlock(&sbi->wq_mutex);
+}
+
+static void autofs_catatonic_mode_for_pipe(struct autofs_sb_info *sbi,
+					   struct file *pipe)
+{
+	mutex_lock(&sbi->wq_mutex);
+	if (sbi->pipe == pipe)
+		autofs_catatonic_mode_locked(sbi);
 	mutex_unlock(&sbi->wq_mutex);
 }
 
@@ -170,7 +183,7 @@ static void autofs_notify_daemon(struct
 		autofs_wait_release(sbi, wq->wait_queue_token, ret);
 		break;
 	default:
-		autofs_catatonic_mode(sbi);
+		autofs_catatonic_mode_for_pipe(sbi, pipe);
 		break;
 	}
 	fput(pipe);
author maintainer
#64 lore raw

PATCH 64/247 unicode: free the map after a grace period

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Christian Brauner <[email protected]>

commit a96c5299bd7305eb984377681eb3222a57376405 upstream.

generic_ci_d_hash() and generic_ci_d_compare() read sb->s_encoding and dereference the unicode map while a pathwalk in rcu mode holds nothing but rcu_read_lock(). The filesystems that use them, ext4, f2fs and tmpfs, drop the map with utf8_unload() from ->put_super(), which runs when the last reference to the mount goes away. A lookup that is still inside such a filesystem at that point reads a freed map.

With a pathwalk held up in utf8_casefold_hash() while the last reference to a casefolded tmpfs goes away:

  BUG: KASAN: slab-use-after-free in utf8nlookup+0x74c/0x950
  Read of size 8 at addr ff110001030fe658 by task walker/98
  Call Trace:
   <TASK>
   utf8nlookup+0x74c/0x950
   utf8byte+0x160/0x1560
   utf8_casefold_hash+0x167/0x230
   generic_ci_d_hash+0xdd/0x1c0
   link_path_walk+0x11ad/0x1d30
   path_lookupat+0x63/0xb00
   filename_lookup+0x1f1/0x5b0
   vfs_statx+0xd7/0x3b0
  [...]
  Allocated by task 94:
   __kmalloc_cache_noprof+0x249/0x560
   utf8_load+0x4e/0x570
   shmem_parse_opt_casefold.isra.0+0x92/0x270
  [...]
  Freed by task 97:
   kfree+0x1db/0x5b0
   shmem_put_super+0x72/0x170
   generic_shutdown_super+0x14c/0x530
   kill_anon_super+0x3a/0x60
   deactivate_locked_super+0x9c/0xf0
   cleanup_mnt+0x9f/0x130

So far that has been hard to hit. umount(2) waits for a grace period in namespace_unlock() before it drops the mount, so only a mount whose last reference goes away elsewhere, like the close of a file or a chdir out of a lazily unmounted directory, can race a walker. So it is reachable.

Delay the free until a grace period has passed. The map and the data table it holds a reference to then outlive any pathwalk that found the superblock before the mount went away. The reference on the data table module is dropped from the rcu callback as well. utf8-core is built in whenever CONFIG_UNICODE is set, so there is no module exit that would have to wait for the callbacks.

Overlayfs points the s_encoding of an overlay at the map of its first layer without taking a reference of its own. That stays correct. The layer is held by the overlay, so its map is dropped only after the overlay is gone.

Fixes b886ee3e778e ("ext4: Support case-insensitive file name lookups")
---
 fs/unicode/utf8-core.c  |   21 +++++++++++++++++----
 include/linux/unicode.h |    1 +
 2 files changed, 18 insertions(+), 4 deletions(-)
fs/unicode/utf8-core.c +17 -4
@@ -3,6 +3,7 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/slab.h>
+#include <linux/rcupdate.h>
 #include <linux/parser.h>
 #include <linux/errno.h>
 #include <linux/stringhash.h>
@@ -205,12 +206,24 @@ out_free_um:
 }
 EXPORT_SYMBOL(utf8_load);
 
+static void utf8_unload_rcu(struct rcu_head *head)
+{
+	struct unicode_map *um = container_of(head, struct unicode_map, rcu);
+
+	symbol_put(utf8_data_table);
+	kfree(um);
+}
+
+/*
+ * A pathwalk in rcu mode may still be in ->d_hash() or ->d_compare() of a
+ * filesystem that is being torn down and read the map through its
+ * superblock, so the map and the tables it points to have to outlive the
+ * grace period.
+ */
 void utf8_unload(struct unicode_map *um)
 {
-	if (um) {
-		symbol_put(utf8_data_table);
-		kfree(um);
-	}
+	if (um)
+		call_rcu(&um->rcu, utf8_unload_rcu);
 }
 EXPORT_SYMBOL(utf8_unload);
 
include/linux/unicode.h +1 -0
@@ -51,6 +51,7 @@ struct unicode_map {
 	unsigned int version;
 	const struct utf8data *ntab[UTF8_NMAX];
 	const struct utf8data_table *tables;
+	struct rcu_head rcu;
 };
 
 int utf8_validate(const struct unicode_map *um, const struct qstr *str);
author maintainer
#65 lore raw

PATCH 65/247 x86/kvm: Dont consume async #PF flags on a kernel #PF in NMI context

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: EJ Campbell <[email protected]>

commit 81d99abe2bd81379abfb595e403bbbee6fbfc624 upstream.

An async #PF "page not present" event arrives through the #PF vector, and its reason sits in the per-CPU apf_reason.flags until __kvm_handle_async_pf() reads and clears it. If an NMI arrives after the event is injected and before the #PF handler reads the flags, and the NMI handler itself takes a #PF, that fault reads and clears the flags belonging to the #PF it interrupted. It then finds interrupts disabled in the NMI context it interrupted, and panics:

  Kernel panic - not syncing: Host injected async #PF in interrupt
disabled region
  Call Trace:
   <NMI>
   panic+0x52/0x60
   __kvm_handle_async_pf+0xb0/0xb0
   exc_page_fault+0x71/0xb0
   asm_exc_page_fault+0x27/0x30
  RIP: 0010:__get_user_nocheck_8+0x6/0x20
   perf_callchain_user+0x147/0x1a0
   get_perf_callchain+0x12d/0x1c0
   ...
   perf_event_nmi_handler+0x26/0x50
   exc_nmi+0xe8/0x110
  RIP: 0010:asm_exc_page_fault+0x0/0x30
   </NMI>

The last RIP line shows the NMI landing on the first instruction of the #PF entry. perf with user call chains takes such a fault: the sampling NMI walks the user stack with __get_user(), which faults when a saved frame pointer is not a valid user address or the walk runs off the top of the stack. The same window exists when KVM, running as an L1 hypervisor with DELIVERY_AS_PF_VMEXIT, reads the flags itself after a #PF VM-exit.

The NMI entry code saves and restores CR2 for this nesting, but not the async #PF reason. Andy Lutomirski described the race, with perf as the example, when async #PF delivery was reworked in 2020 [1]. Saving and clearing the flags across the NMI, next to CR2, was discussed there but not merged.

Since that rework, Linux has stopped setting KVM_ASYNC_PF_SEND_ALWAYS, so the host delivers "page not present" only for user mode faults and a kernel-mode fault in NMI context (including kernel-mode #MC and #DB) cannot be an async page fault. In that case, return without touching the flags so that the interrupted handler finds them intact.

Tested on v7.3-rc5 in a 4-vCPU, 2 GiB guest restored from a snapshot whose memory is served on demand through userfaultfd, so that reading it back takes async #PFs, while every thread samples hardware cycles with user call chains and points its frame pointer at an unmapped page. Without this patch the guest panicked as above in 3 of 3 runs. With it, the guest survived 3 of 3 runs of 60 seconds, and the host counted 148,441 to 153,334 kvm_async_pf_not_present events in the first 20 seconds of each. On 6.18.50 the results were the same: the guest panicked without the patch and survived with it.

---
 arch/x86/kernel/kvm.c |   32 +++++++++++++++++++++++++++++---
 1 file changed, 29 insertions(+), 3 deletions(-)
arch/x86/kernel/kvm.c +29 -3
@@ -255,11 +255,39 @@ noinstr u32 kvm_read_and_reset_apf_flags
 }
 EXPORT_SYMBOL_GPL(kvm_read_and_reset_apf_flags);
 
+static __always_inline bool kvm_apf_reason_pending(void)
+{
+	return __this_cpu_read(async_pf_enabled) &&
+	       __this_cpu_read(apf_reason.flags);
+}
+
 noinstr bool __kvm_handle_async_pf(struct pt_regs *regs, u32 token)
 {
-	u32 flags = kvm_read_and_reset_apf_flags();
 	irqentry_state_t state;
+	u32 flags;
+
+	if (!kvm_apf_reason_pending())
+		return false;
+
+	/*
+	 * The guest does not enable KVM_ASYNC_PF_SEND_ALWAYS, so the host
+	 * delivers async #PF only to user mode (or as a #PF VM-exit, which KVM
+	 * reads itself). A #PF from kernel mode can still arrive between that
+	 * delivery and the read of the reason flags, inside an NMI that
+	 * interrupted it, for instance perf reading user memory for a call
+	 * chain. Such a fault is not async, so leave the async PF flags
+	 * untouched for the outer handler.  Outside NMI context, on the other
+	 * hand, async PF flags on a kernel-mode fault mean the host is broken.
+	 */
+	if (!user_mode(regs)) {
+		if (likely(in_nmi()))
+			return false;
+		state = irqentry_enter(regs);
+		instrumentation_begin();
+		panic("Host injected async #PF in kernel mode\n");
+	}
 
+	flags = kvm_read_and_reset_apf_flags();
 	if (!flags)
 		return false;
 
@@ -275,8 +303,6 @@ noinstr bool __kvm_handle_async_pf(struc
 		panic("Host injected async #PF in interrupt disabled region\n");
 
 	if (flags & KVM_PV_REASON_PAGE_NOT_PRESENT) {
-		if (unlikely(!(user_mode(regs))))
-			panic("Host injected async #PF in kernel mode\n");
 		/* Page is swapped out by the host. */
 		kvm_async_pf_task_wait_schedule(token);
 	} else {
author maintainer
#66 lore raw

PATCH 66/247 KVM: nVMX: Reset MMU back to the root MMU if loading L2 state fails for !VM-Entry

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Amirmohammad Eftekhar <[email protected]>

commit c8f4cfe1cf93bce924e27f2abead7a4c4a487505 upstream.

Reset the vCPU's MMU pointer back to the root MMU if loading L2 state fails in the "not from VM-Entry" case, i.e. when emulating RSM or handling KVM_SET_NESTED_STATE. Failure to unwind the MMU pointer results in KVM trying to run L1 with a nested MMU, and is especially problematic if the vCPU manages to free its nested state before the VM crashes. Constructing the root for a nested EPT MMU goes through vmcs12, which is dynamically allocated/freed when nVMX is enabled/disabled. And so if the nested state is freed with the MMU still pointing at the nested MMU, KVM will hit a NULL pointer deref when attempting to get L1=>L2 EPTP.

  BUG: kernel NULL pointer dereference, address: 0000000000000078
  #PF: supervisor read access in kernel mode
  #PF: error_code(0x0000) - not-present page
  PGD 114a2d067 P4D 111743067 PUD 11141f067 PMD 0
  Oops: Oops: 0000 [#1] SMP
  CPU: 20 UID: 1000 PID: 1272 Comm: nested_tdp_faul Not tainted 7.3.0-rc4-8c89f11d1a99b-sink-vm #1914 PREEMPTLAZY
  Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015
  RIP: 0010:nested_ept_get_eptp+0xb/0x10 [kvm_intel]
  Call Trace:
   <TASK>
   kvm_mmu_load+0xcd/0x870 [kvm]
   kvm_arch_vcpu_ioctl_run+0x18f5/0x1f80 [kvm]
   kvm_vcpu_ioctl+0x2c7/0x970 [kvm]
   __x64_sys_ioctl+0x90/0xd0
   do_syscall_64+0x9c/0x4d0
   entry_SYSCALL_64_after_hwframe+0x4b/0x53
  RIP: 0033:0x47491b
   </TASK>
  Modules linked in: kvm_intel kvm irqbypass
  CR2: 0000000000000078
  ---[ end trace 0000000000000000 ]---
  RIP: 0010:nested_ept_get_eptp+0xb/0x10 [kvm_intel]
Cc David Woodhouse <[email protected]>
Fixes 72e9cbdb4338 ("KVM: nVMX: fix SMI injection in guest mode")
Fixes 8fcc4b5923af ("kvm: nVMX: Introduce KVM_CAP_NESTED_STATE")
[sean: write changelog]
---
 arch/x86/kvm/vmx/nested.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
arch/x86/kvm/vmx/nested.c +3 -1
@@ -3689,8 +3689,10 @@ vmentry_fail_vmexit_guest_mode:
 vmentry_fail_vmexit:
 	vmx_switch_vmcs(vcpu, &vmx->vmcs01);
 
-	if (!from_vmentry)
+	if (!from_vmentry) {
+		nested_ept_uninit_mmu_context(vcpu);
 		return NVMX_VMENTRY_VMEXIT;
+	}
 
 	kvm_vcpu_unmap(vcpu, &vmx->nested.apic_access_page_map, false);
 	kvm_vcpu_unmap(vcpu, &vmx->nested.virtual_apic_map, true);
author maintainer
#67 lore raw

PATCH 67/247 fsnotify: prevent mark additions during connector teardown

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kyle Zeng <[email protected]>

commit 36ebddca56b9d8993d6dc984fdecc748ee1f5e06 upstream.

fsnotify_destroy_marks() keeps a reference to its current mark while it drops conn->lock to destroy the mark. This stabilizes the forward-walk cursor, but does not prevent a new mark from being inserted before it. For example, an inotify watch re-added after IN_IGNORED sorts before the old, detached mark for the same group. The walk can miss the replacement and then detach the entire connector from the object.

The replacement is still on its group's marks_list and, for inotify, in the watch IDR. The group mutex no longer protects the connector's object from disappearing. An unprivileged fdinfo read racing the final detach can consequently call igrab(NULL):

  BUG: KASAN: null-ptr-deref in igrab+0x24/0x100
  Write of size 4 at addr 0000000000000148
  Call Trace:
   igrab+0x24/0x100
   inotify_show_fdinfo+0x1dc/0x5e0
   seq_show+0x397/0x690

Set a DESTROYING flag under conn->lock before starting the walk, and reject new marks on that connector with -ENOENT. The existing add-mark error paths undo the temporary group-list entry and the backend's watch allocation. Do not wait for teardown while holding the group mutex, which the destroyer needs. Once the old connector is detached, an add can install a fresh connector as before.

This closes the insertion race for all object types using the common connector teardown, without changing the mark reference or SRCU lifetime rules.

Fixes 6b3f05d24d35 ("fsnotify: Detach mark from object list when last reference is dropped")
---
 fs/notify/mark.c                 |   11 +++++++++++
 include/linux/fsnotify_backend.h |    1 +
 2 files changed, 12 insertions(+)
fs/notify/mark.c +11 -0
@@ -779,6 +779,11 @@ restart:
 		goto restart;
 	}
 
+	if (conn->flags & FSNOTIFY_CONN_FLAG_DESTROYING) {
+		err = -ENOENT;
+		goto out_err;
+	}
+
 	/* is mark the first mark? */
 	if (hlist_empty(&conn->list)) {
 		hlist_add_head_rcu(&mark->obj_list, &conn->list);
@@ -966,6 +971,12 @@ void fsnotify_destroy_marks(fsnotify_con
 	if (!conn)
 		return;
 	/*
+	 * New marks could be inserted before our cursor and survive the final
+	 * detachment of the connector from the object. Prevent additions to
+	 * this connector before dropping its lock.
+	 */
+	conn->flags |= FSNOTIFY_CONN_FLAG_DESTROYING;
+	/*
 	 * We have to be careful since we can race with e.g.
 	 * fsnotify_clear_marks_by_group() and once we drop the conn->lock, the
 	 * list can get modified. However we are holding mark reference and
include/linux/fsnotify_backend.h +1 -0
@@ -468,6 +468,7 @@ struct fsnotify_mark_connector {
 	unsigned char prio;	/* Highest priority group */
 #define FSNOTIFY_CONN_FLAG_IS_WATCHED	0x01
 #define FSNOTIFY_CONN_FLAG_HAS_IREF	0x02
+#define FSNOTIFY_CONN_FLAG_DESTROYING	0x04
 	unsigned short flags;	/* flags [lock] */
 	union {
 		/* Object pointer [lock] */
author maintainer
#68 lore raw

PATCH 68/247 pid_namespace: make zap_pid_ns_processes() freezable

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aviv Vaknin <[email protected]>

commit 81933038f5e273d17ed22792f906889393b204c5 upstream.

When the init of a pid namespace exits, zap_pid_ns_processes() waits until all other pids in the namespace are freed. A tracer outside the namespace that does not reap its traced zombies keeps those pids allocated, so this wait has no upper bound, and while it lasts every suspend attempt fails:

Freezing user space processes failed after 20.006 seconds (2 tasks refusing to freeze, wq_busy=0):

Neither wait is freezable. The first loop sleeps in do_wait(), which returns -ERESTARTSYS on the freezer's fake signal; the loop then clears TIF_SIGPENDING and waits again without ever freezing. The final loop sleeps in TASK_INTERRUPTIBLE, and since nothing clears TIF_SIGPENDING there, schedule() returns immediately after the first freeze attempt and the task spins until the wait ends.

Sleep in TASK_INTERRUPTIBLE | TASK_FREEZABLE in do_wait(), so that the freezer can freeze tasks waiting there directly, and in TASK_IDLE | TASK_FREEZABLE in the final loop, like coredump_task_exit() does.

This was hit with Chromium-based browsers: crashpad ptrace-attaches the threads of a crashed renderer, which is a pid namespace init, and does not reap those killed during the dump.

Fixes 3eb07c8c8adb ("pid namespaces: destroy pid namespace on init's death")
Fixes b9a985db9896 ("pid_ns: Sleep in TASK_INTERRUPTIBLE in zap_pid_ns_processes")
---
 kernel/exit.c          |    2 +-
 kernel/pid_namespace.c |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
kernel/exit.c +1 -1
@@ -1696,7 +1696,7 @@ static long do_wait(struct wait_opts *wo
 	add_wait_queue(&current->signal->wait_chldexit, &wo->child_wait);
 
 	do {
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_INTERRUPTIBLE | TASK_FREEZABLE);
 		retval = __do_wait(wo);
 		if (retval != -ERESTARTSYS)
 			break;
kernel/pid_namespace.c +1 -1
@@ -246,7 +246,7 @@ void zap_pid_ns_processes(struct pid_nam
 	 * free_pid() will awaken this task.
 	 */
 	for (;;) {
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_IDLE | TASK_FREEZABLE);
 		if (pid_ns->pid_allocated == init_pids)
 			break;
 		schedule();
author maintainer
#69 lore raw

PATCH 69/247 eventfs: Free the eventfs_inode after an rcu grace period too

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Christian Brauner <[email protected]>

commit 2cd88c498ecff8e22bf4c397b8eb226db70ac343 upstream.

tracefs_d_revalidate() reads ->is_freed of the eventfs_inode that dentry->d_fsdata points to, and ->d_revalidate() is called in rcu pathwalk mode with nothing but rcu_read_lock() held. The dentry itself stays around for such a walker, but tracefs_d_release() drops its reference to the eventfs_inode when the dentry is killed, and release_ei() then frees it after a grace period of eventfs_srcu. An SRCU grace period doesn't wait for rcu_read_lock() sections, so the walker can read an eventfs_inode that is already gone.

With a pathwalk held up in tracefs_d_revalidate() while the dentry goes away:

  BUG: KASAN: slab-use-after-free in tracefs_d_revalidate+0x76/0x90
  Read of size 1 at addr ff1100010152d8d0 by task walker/97
  Call Trace:
   <TASK>
   tracefs_d_revalidate+0x76/0x90
   lookup_fast+0x320/0x640
   path_lookupat+0x160/0xb00
   filename_lookup+0x1f1/0x5b0
   vfs_statx+0xd7/0x3b0
   vfs_fstatat+0x4d/0x90
   __do_sys_newfstatat+0xa0/0x100
   do_syscall_64+0x10e/0x5e0
   entry_SYSCALL_64_after_hwframe+0x77/0x7f
  [...]
  Allocated by task 1:
   __kmalloc_cache_noprof+0x249/0x560
   eventfs_create_dir+0x7a/0x590
   event_create_dir+0x2fb/0xcf0
   trace_add_event_call+0xbf/0x150
  [...]
  Freed by task 11:
   kfree+0x1db/0x5b0
   srcu_invoke_callbacks+0x3a3/0x620
   process_one_work+0x937/0x1810
   worker_thread+0x4f1/0xd60

Before commit 8e556432477e ("eventfs: Use SRCU for freeing eventfs_inodes") release_ei() used call_rcu(), which covered the walker, while eventfs_iterate() needs the SRCU grace period. Wait for both and chain call_rcu() from the SRCU callback.

---
 fs/tracefs/event_inode.c |   10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
fs/tracefs/event_inode.c +9 -1
@@ -91,6 +91,14 @@ static void free_ei_rcu(struct rcu_head
 	}
 }
 
+/* read under eventfs_srcu and, by tracefs_d_revalidate(), in rcu pathwalk */
+static void free_ei_srcu(struct rcu_head *rcu)
+{
+	struct eventfs_inode *ei = container_of(rcu, struct eventfs_inode, rcu);
+
+	call_rcu(&ei->rcu, free_ei_rcu);
+}
+
 /*
  * eventfs_inode reference count management.
  *
@@ -112,7 +120,7 @@ static void release_ei(struct kref *ref)
 			entry->release(entry->name, ei->data);
 	}
 
-	call_srcu(&eventfs_srcu, &ei->rcu, free_ei_rcu);
+	call_srcu(&eventfs_srcu, &ei->rcu, free_ei_srcu);
 }
 
 static inline void put_ei(struct eventfs_inode *ei)
author maintainer
#70 lore raw

PATCH 70/247 namei: test for a negative dentry the way traverse_mounts() does

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Christian Brauner <[email protected]>

commit 98b6cf102b35b2bb48d1fd74d5f664eb66fe83eb upstream.

lookup_fast_for_open() only hands an O_CREAT open a cached dentry if it's positive. Otherwise the open goes on to lookup_open(), which takes the directory lock and creates the file. It tests for that with !dentry->d_inode, but traverse_mounts() later decides with the type in d_flags. And when a dentry is instantiated __d_set_inode_and_type() stores ->d_inode first and only then releases the type into ->d_flags.

So a refwalk that took its reference to a negative dentry right before another task instantiates it can find ->d_inode already set in lookup_fast_for_open(), go on to step_into(), and still find the negative type in traverse_mounts(). The open then fails with -ENOENT, which an O_CREAT open must never do. rcuwalk doesn't get that far, the d_seq check in step_into() catches the instantiation.

Use d_is_negative() so both look at the type. While the type is still negative the open takes lookup_open() and waits for the directory lock like any other O_CREAT open of a negative dentry.

On its own the window is a couple of instructions in the creating task, so it takes an interrupt or a preempted vcpu in exactly the right spot. I haven't caught it without help in about 600 million racing O_CREAT opens.

The debug patch below stalls the creating task between the two stores for names starting with "ocrace", the way an interrupt landing there would. The reproducer below opens "ocrace" with O_CREAT through symlinks on a strictatime tmpfs: following them updates their atime, which drops the walk out of rcuwalk, so the name is always looked up in refwalk. With

echo 20 > /proc/sys/fs/ocrace_instantiate_us
./ocreat_enoent -t 60

on a KASAN kernel open() failed with -ENOENT 43 times in 4.9 million calls. With this patch it never failed in 4.8 million calls. Tracing step_into_slowpath() during such a run, each of the 98 -ENOENTs it returned came right after the same task had entered it with a dentry that was still typed negative.

  // SPDX-License-Identifier: GPL-2.0
  /*
   * ocreat_enoent: open(O_CREAT) racing the creation of the same name.
   *
   * Writers unlink "ocrace" and create it again. Readers open it with
   * O_CREAT through a symlink of their own on a strictatime tmpfs.
   * Following the symlink updates its atime, which drops the walk out of
   * rcu mode, so the name is always looked up in refwalk. An O_CREAT open
   * must never fail with ENOENT.
   *
   * -p arms a cpu-clock perf timer with that period in ns on every writer,
   * so that interrupts land at random points in it.
   *
   *   usage: ocreat_enoent [-t secs] [-w writers] [-r readers] [-p ns]
   */
  #define _GNU_SOURCE
  #include <errno.h>
  #include <fcntl.h>
  #include <pthread.h>
  #include <stdatomic.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/mount.h>
  #include <sys/stat.h>
  #include <sys/syscall.h>
  #include <unistd.h>
  #include <linux/perf_event.h>

  #define DIR "/tmp/oc"

static atomic_int stop;
static atomic_long opens, creates, enoent, other; static long period;

  static void die(const char *what)
  {
  	fprintf(stderr, "FATAL %s: %s\n", what, strerror(errno));
  	exit(2);
  }

  static void *writer(void *arg)
  {
  	(void)arg;
  	if (period) {
  		struct perf_event_attr attr = {
  			.type = PERF_TYPE_SOFTWARE,
  			.size = sizeof(attr),
  			.config = PERF_COUNT_SW_CPU_CLOCK,
  			.sample_period = period,
  		};

  		if (syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0) < 0)
  			die("perf_event_open");
  	}
  	while (!atomic_load(&stop)) {
  		int fd;

  		unlink(DIR "/ocrace");
  		fd = open(DIR "/ocrace", O_CREAT | O_RDWR | O_CLOEXEC, 0644);
  		if (fd >= 0)
  			close(fd);
  		atomic_fetch_add(&creates, 1);
  	}
  	return NULL;
  }

  static void *reader(void *arg)
  {
  	char lnk[64];

  	snprintf(lnk, sizeof(lnk), DIR "/lnk.%ld", (long)arg);
  	while (!atomic_load(&stop)) {
  		int fd = open(lnk, O_CREAT | O_RDWR | O_CLOEXEC, 0644);

  		if (fd >= 0)
  			close(fd);
  		else if (errno == ENOENT)
  			atomic_fetch_add(&enoent, 1);
  		else
  			atomic_fetch_add(&other, 1);
  		atomic_fetch_add(&opens, 1);
  	}
  	return NULL;
  }

  int main(int argc, char **argv)
  {
  	int secs = 60, nw = 4, nr = 12, c, n = 0;
  	pthread_t t[256];

  	while ((c = getopt(argc, argv, "t:w:r:p:")) != -1) {
  		switch (c) {
  		case 't': secs = atoi(optarg); break;
  		case 'w': nw = atoi(optarg); break;
  		case 'r': nr = atoi(optarg); break;
  		case 'p': period = atol(optarg); break;
  		default: return 2;
  		}
  	}
  	if (nw + nr > 256)
  		return 2;
  	mkdir(DIR, 0755);
  	if (mount("oc", DIR, "tmpfs", MS_STRICTATIME, "mode=0755"))
  		die("tmpfs");
  	for (long i = 0; i < nr; i++) {
  		char lnk[64];

  		snprintf(lnk, sizeof(lnk), DIR "/lnk.%ld", i);
  		if (symlink("ocrace", lnk))
  			die("symlink");
  	}
  	for (long i = 0; i < nw; i++)
  		if (pthread_create(&t[n++], NULL, writer, NULL))
  			die("writer");
  	for (long i = 0; i < nr; i++)
  		if (pthread_create(&t[n++], NULL, reader, (void *)i))
  			die("reader");
  	sleep(secs);
  	atomic_store(&stop, 1);
  	for (int i = 0; i < n; i++)
  		pthread_join(t[i], NULL);
  	printf("%ds: %ld opens, %ld creates, ENOENT %ld, other errors %ld\n",
  	       secs, atomic_load(&opens), atomic_load(&creates),
  	       atomic_load(&enoent), atomic_load(&other));
  	umount2(DIR, MNT_DETACH);
  	return atomic_load(&enoent) ? 1 : 0;
  }
#  diff --git a/fs/dcache.c b/fs/dcache.c
#  --- a/fs/dcache.c
#  +++ b/fs/dcache.c
#  @@ -32,9 +32,13 @@
#   #include <linux/bit_spinlock.h>
#   #include <linux/rculist_bl.h>
#   #include <linux/list_lru.h>

# +#include <linux/delay.h>
# #include "internal.h"
# #include "mount.h"
#

#  +/* debug: stall "ocrace*" names between ->d_inode and the type */
#  +static int sysctl_ocrace_instantiate_us;
#  +

# #include <asm/runtime-const.h>
#
# /*
# @@ -200,6 +204,13 @@
# }
#
# static const struct ctl_table fs_dcache_sysctls[] = {

#  +	{
#  +		.procname	= "ocrace_instantiate_us",
#  +		.data		= &sysctl_ocrace_instantiate_us,
#  +		.maxlen		= sizeof(int),
#  +		.mode		= 0644,
#  +		.proc_handler	= proc_dointvec,
#  +	},
#   	{
#   		.procname	= "dentry-state",
#   		.data		= &dentry_stat,

# @@ -405,6 +416,10 @@
# unsigned flags;
#
# dentry->d_inode = inode;

#  +	/* debug: an interrupt landing right here */
#  +	if (unlikely(READ_ONCE(sysctl_ocrace_instantiate_us)) &&
#  +	    !strncmp(dentry->d_name.name, "ocrace", 6))
#  +		udelay(READ_ONCE(sysctl_ocrace_instantiate_us));
#   	flags = READ_ONCE(dentry->d_flags);
#   	flags &= ~DCACHE_ENTRY_TYPE;
#   	flags |= type_flags;
Fixes e747e15156b7 ("fs: try an opportunistic lookup for O_CREAT opens too")
Cc [email protected] # v6.12+
---
 fs/namei.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
fs/namei.c +1 -2
@@ -3726,8 +3726,7 @@ static struct dentry *lookup_fast_for_op
 		return dentry;
 
 	if (open_flag & O_CREAT) {
-		/* Discard negative dentries. Need inode_lock to do the create */
-		if (!dentry->d_inode) {
+		if (d_is_negative(dentry)) {
 			if (!(nd->flags & LOOKUP_RCU))
 				dput(dentry);
 			dentry = NULL;
author maintainer
#71 lore raw

PATCH 71/247 net/sched: fix use-after-free in __tcf_action_put()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jérémy Jean <[email protected]>

commit 7b16eb911091aae237a8673da94e4dfb9cd21d54 upstream.

__tcf_action_put() decrements the reference count before decrementing the binding count. Another thread can free the action between these operations, causing the binding-count update to access freed memory. KASAN reports it as:

  BUG: KASAN: slab-use-after-free in __tcf_action_put+0x327/0x340
  Write of size 4 at addr ff11000000d46420 by task tc-put-pause/64

Hold the action IDR mutex across both counter updates and IDR removal, keeping cleanup outside the lock.

Fixes 16af6067392c ("net: sched: implement reference counted action release")
Assisted-by LLM
---
 net/sched/act_api.c |   11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)
net/sched/act_api.c +5 -6
@@ -372,9 +372,10 @@ static int __tcf_action_put(struct tc_ac
 {
 	struct tcf_idrinfo *idrinfo = p->idrinfo;
 
-	if (refcount_dec_and_mutex_lock(&p->tcfa_refcnt, &idrinfo->lock)) {
-		if (bind)
-			atomic_dec(&p->tcfa_bindcnt);
+	mutex_lock(&idrinfo->lock);
+	if (bind)
+		atomic_dec(&p->tcfa_bindcnt);
+	if (refcount_dec_and_test(&p->tcfa_refcnt)) {
 		idr_remove(&idrinfo->action_idr, p->tcfa_index);
 		mutex_unlock(&idrinfo->lock);
 
@@ -382,9 +383,7 @@ static int __tcf_action_put(struct tc_ac
 		return 1;
 	}
 
-	if (bind)
-		atomic_dec(&p->tcfa_bindcnt);
-
+	mutex_unlock(&idrinfo->lock);
 	return 0;
 }
 
author maintainer
#72 lore raw

PATCH 72/247 net: rds: fix uninitialized trans dereference in CM event handler

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aohan Mei <[email protected]>

commit 492ba6ac1b1ffb8a06ff52db0825c1af2cdb28c8 upstream.

rds_rdma_cm_event_handler_cmn() assigns trans only when the RDMA device is an InfiniBand CA (RDMA_NODE_IB_CA). On any other device type, e.g. an iWARP RNIC such as siw, trans stays uninitialized, but the event switch dereferences it: unconditionally in the RDMA_CM_EVENT_CONNECT_REQUEST case via trans->cm_handle_connect(), and (with a connection context) in the ROUTE_RESOLVED and ESTABLISHED cases.

An RDS listener on an iWARP device therefore crashes the kernel as soon as a connect request arrives: with CONFIG_INIT_STACK_ALL_ZERO the wild load becomes a NULL dereference at offset 0xa0 (&trans->cm_handle_connect) in the iw_cm_wq workqueue.

GCC masks the bug in default builds by folding the uninitialized load into &rds_ib_transport; Clang-built kernels take the real uninitialized path and oops.

The active side can land on a non-IB device too: the id that rds_ib_conn_path_connect() creates is not restricted to a node type, so rdma_resolve_addr() binds it to whichever device serves the local address, and an iWARP RNIC on the same netdev qualifies. The crash there is different: resolving a route on an iWARP id never fills in cm_id->route.path_rec, and the ROUTE_RESOLVED case writes path_rec[0].sl before it calls into the transport.

The iWARP transport was dropped long ago and IB is the only transport left, so make that explicit: initialize trans to &rds_ib_transport at declaration, drop the conditional assignment, reject a connect request that arrives on a non-IB device, and drop a connection whose address resolved to one instead of resolving a route on it. The rejection is limited to RDMA_CM_EVENT_CONNECT_REQUEST on purpose: a non-zero return from the handler makes rdma_cm destroy the id the event was delivered on, which is right for the request's freshly created id but would free a connection id that RDS still owns for any other event. On the active side the id stays ic->i_cm_id and the connection's shutdown destroys it, so that case returns 0.

[achender: reject only on RDMA_CM_EVENT_CONNECT_REQUEST instead of
bailing out for every event on a non-IB device, so that rdma_cm does not destroy connection ids RDS still tracks; drop a connection whose address resolved to a non-IB device before a route is resolved on it; changelog adjusted]

---
 net/rds/rdma_transport.c |   26 ++++++++++++++++++++++----
 1 file changed, 22 insertions(+), 4 deletions(-)
net/rds/rdma_transport.c +22 -4
@@ -52,7 +52,7 @@ static int rds_rdma_cm_event_handler_cmn
 {
 	/* this can be null in the listening path */
 	struct rds_connection *conn = cm_id->context;
-	struct rds_transport *trans;
+	struct rds_transport *trans = &rds_ib_transport;
 	int ret = 0;
 	int *err;
 	u8 len;
@@ -60,9 +60,6 @@ static int rds_rdma_cm_event_handler_cmn
 	rdsdebug("conn %p id %p handling event %u (%s)\n", conn, cm_id,
 		 event->event, rdma_event_msg(event->event));
 
-	if (cm_id->device->node_type == RDMA_NODE_IB_CA)
-		trans = &rds_ib_transport;
-
 	/* Prevent shutdown from tearing down the connection
 	 * while we're executing. */
 	if (conn) {
@@ -82,11 +79,32 @@ static int rds_rdma_cm_event_handler_cmn
 
 	switch (event->event) {
 	case RDMA_CM_EVENT_CONNECT_REQUEST:
+		/* Only the IB transport is supported, but RDS listens on
+		 * every RDMA device: reject a request that arrived on any
+		 * other kind.  A non-zero return has rdma_cm destroy the
+		 * request's id, which is what we want here and only here.
+		 */
+		if (cm_id->device->node_type != RDMA_NODE_IB_CA) {
+			ret = 1;
+			break;
+		}
 		ret = trans->cm_handle_connect(cm_id, event, isv6);
 		break;
 
 	case RDMA_CM_EVENT_ADDR_RESOLVED:
 		if (conn) {
+			/* The address resolved to a device RDS has no
+			 * transport for.  Do not go on to resolve a route:
+			 * an iWARP route leaves cm_id->route.path_rec
+			 * unset, which the ROUTE_RESOLVED case below
+			 * dereferences.  Drop the connection instead; the
+			 * id is still ic->i_cm_id, so return 0 and let the
+			 * shutdown destroy it.
+			 */
+			if (cm_id->device->node_type != RDMA_NODE_IB_CA) {
+				rds_conn_drop(conn);
+				break;
+			}
 			rdma_set_service_type(cm_id, conn->c_tos);
 			rdma_set_min_rnr_timer(cm_id, IB_RNR_TIMER_000_32);
 			ret = rdma_resolve_route(cm_id,
author maintainer
#73 lore raw

PATCH 73/247 pinctrl: amd: Clear S4 wake bits when firmware has _AEI

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Mario Limonciello <[email protected]>

commit 1cca81a683da7f0ef2fbaf34442376044cbbad22 upstream.

Commit ffe8a0c6b552 ("pinctrl-amd: Don't clear S4 wake bits at probe") preserves firmware-programmed S4 wake sources because some PCIe devices rely on them for Wake-on-LAN. The system that motivated that change does not expose ACPI GPIO events through _AEI.

On a Chuwi CoreBook Plus, firmware exposes _AEI but also leaves the S4/S5 wake bit set for GPIO 24. This causes the machine to power back on immediately after shutdown even though Wake-on-LAN is not enabled.

GPIO register dumps show that the only relevant difference is bit 15 on GPIO 24. Clearing that bit makes the machine remain off. GPIO 24 is not itself listed in _AEI, so _AEI is not a per-pin indication of which pins the OS manages; it is a platform level signal of which wake model the firmware follows.

Use that signal to decide what to clear. Continue clearing S0i3/S3 bits on every system. Clear S4 bits when firmware exposes _AEI, indicating that the platform hands GPIO events to the OS, but preserve S4 bits when _AEI is absent so that firmware-programmed sources such as PCIe PME that bypass the GPIO IRQ wake API keep working.

A platform that exposes _AEI for some pins while routing a wake capable device through a raw GPIO not listed in _AEI would lose that wake source unless firmware re-arms it during the sleep transition. No such system has been reported.

---
 drivers/pinctrl/pinctrl-amd.c |    9 +++++++++
 1 file changed, 9 insertions(+)
drivers/pinctrl/pinctrl-amd.c +9 -0
@@ -871,6 +871,15 @@ static void amd_gpio_irq_init(struct amd
 
 	mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3);
 
+#ifdef CONFIG_ACPI
+	/*
+	 * Platforms without _AEI leave GPIO wake sources to firmware, so
+	 * preserve their S4 wake bits.
+	 */
+	if (acpi_has_method(ACPI_HANDLE(&gpio_dev->pdev->dev), "_AEI"))
+		mask |= BIT(WAKE_CNTRL_OFF_S4);
+#endif
+
 	for (i = 0; i < desc->npins; i++) {
 		int pin = desc->pins[i].number;
 		const struct pin_desc *pd = pin_desc_get(gpio_dev->pctrl, pin);
author maintainer
#74 lore raw

PATCH 74/247 sctp: revalidate output stream after association connect wait

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Daehyeon Ko <[email protected]>

commit b7ad72414c673ce1fa1f463e807cd4f34f8be9bf upstream.

When message interleaving is enabled, the first send waits for association establishment before building its data chunks. The wait drops the socket lock, and handshake processing can reduce the output stream count to the peer-advertised inbound stream count. sctp_stream_init() then frees the extension of every removed stream.

The sender resumes with the stream that it checked before the wait. If the peer removed that stream, sctp_outq_tail() later dereferences its NULL extension. Fatal-oops policies then panic the host.

KASAN: null-ptr-deref in range [0x38-0x3f]
RIP: sctp_outq_tail+0x49e/0xaa0
Call Trace:
 sctp_primitive_SEND
 sctp_sendmsg_to_asoc
 sctp_sendmsg

A range check alone is insufficient. Stream reconfiguration can grow the output count again while the lock is released without recreating extensions freed by the earlier shrink. The stream can then be in range while its extension remains NULL. The send-buffer wait has the same issue.

Factor the range and extension checks into a helper and repeat both after each send-path wait that drops the socket lock. Once the lock is reacquired, the validation remains stable through data creation and queueing.

If validation fails after the connect wait, the auto-created association is already established. Return ESRCH so the existing caller path keeps it under state-machine ownership instead of freeing it directly, which would leave protocol, counter and socket state inconsistent.

Fixes 668c9beb9020 ("sctp: implement assign_number for sctp_stream_interleave")
Assisted-by LLM
---
 net/sctp/socket.c |   33 +++++++++++++++++++++------------
 1 file changed, 21 insertions(+), 12 deletions(-)
net/sctp/socket.c +21 -12
@@ -1786,6 +1786,18 @@ static int sctp_sendmsg_check_sflags(str
 	return 1;
 }
 
+static int sctp_sendmsg_check_stream(struct sctp_association *asoc,
+				     struct sctp_sndrcvinfo *sinfo)
+{
+	if (unlikely(sinfo->sinfo_stream >= asoc->stream.outcnt))
+		return -EINVAL;
+
+	if (unlikely(!SCTP_SO(&asoc->stream, sinfo->sinfo_stream)->ext))
+		return sctp_stream_init_ext(&asoc->stream, sinfo->sinfo_stream);
+
+	return 0;
+}
+
 static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
 				struct msghdr *msg, size_t msg_len,
 				struct sctp_transport *transport,
@@ -1800,16 +1812,9 @@ static int sctp_sendmsg_to_asoc(struct s
 	long timeo;
 	int err;
 
-	if (sinfo->sinfo_stream >= asoc->stream.outcnt) {
-		err = -EINVAL;
+	err = sctp_sendmsg_check_stream(asoc, sinfo);
+	if (err)
 		goto err;
-	}
-
-	if (unlikely(!SCTP_SO(&asoc->stream, sinfo->sinfo_stream)->ext)) {
-		err = sctp_stream_init_ext(&asoc->stream, sinfo->sinfo_stream);
-		if (err)
-			goto err;
-	}
 
 	if (sp->disable_fragments && msg_len > asoc->frag_point) {
 		err = -EMSGSIZE;
@@ -1830,10 +1835,9 @@ static int sctp_sendmsg_to_asoc(struct s
 		err = sctp_wait_for_sndbuf(asoc, transport, &timeo, msg_len);
 		if (err)
 			goto err;
-		if (unlikely(sinfo->sinfo_stream >= asoc->stream.outcnt)) {
-			err = -EINVAL;
+		err = sctp_sendmsg_check_stream(asoc, sinfo);
+		if (err)
 			goto err;
-		}
 	}
 
 	if (sctp_state(asoc, CLOSED)) {
@@ -1848,6 +1852,11 @@ static int sctp_sendmsg_to_asoc(struct s
 				err = -ESRCH;
 				goto err;
 			}
+			err = sctp_sendmsg_check_stream(asoc, sinfo);
+			if (err) {
+				err = -ESRCH;
+				goto err;
+			}
 		} else {
 			wait_connect = true;
 		}
author maintainer
#75 lore raw

PATCH 75/247 tools: gpio: fix building from the top level with O=

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Sasha Levin <[email protected]>

commit 52336337f1a01ad803c7f3ddb23f40222e9ad58d upstream.

"make O=<dir> tools/gpio" fails:

Makefile:27: /tools/build/Makefile.include: No such file or directory

tools/Makefile clears srctree, and the gpio Makefile only works it out again when building_out_of_srctree is not set, which the top-level Makefile sets for O= builds.

Also work out srctree when it is empty, as tools/bpf/Makefile does since commit a89b2cbf71d64 ("tools, bpf: Fix build for 'make -s tools/bpf O=<dir>'").

Fixes 4a6a6f5c4aeed ("tools: gpio: Use !building_out_of_srctree to determine srctree")
Assisted-by LLM
---
 tools/gpio/Makefile |    6 ++++++
 1 file changed, 6 insertions(+)
tools/gpio/Makefile +6 -0
@@ -7,7 +7,13 @@ bindir ?= /usr/bin
 # isn't set and when invoked from selftests build, where srctree
 # is set to ".". building_out_of_srctree is undefined for in srctree
 # builds
+ifeq ($(srctree),)
+update_srctree := 1
+endif
 ifndef building_out_of_srctree
+update_srctree := 1
+endif
+ifeq ($(update_srctree),1)
 srctree := $(patsubst %/,%,$(dir $(CURDIR)))
 srctree := $(patsubst %/,%,$(dir $(srctree)))
 endif
author maintainer
#76 lore raw

PATCH 76/247 tracing/histograms: Reject non-stacktrace fields for synthetic stacktrace fields

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Lee Jones <[email protected]>

commit a244c241593b5b38a08f0df2cb43c26574efbdcb upstream.

In check_synth_field(), when a synthetic event field is a stacktrace (field->is_stack) and the histogram field is not a "long[" stacktrace array, validation falls through to the generic size and signedness comparison.

Because parse_synth_field() sets a stacktrace field's type to "__data_loc unsigned long[]", its size is sizeof(u64) and synth_field_signed() marks it as signed. Consequently, any 8-byte signed scalar histogram field passes the fallback check, and its scalar value is subsequently dereferenced as a kernel stacktrace pointer in get_field_size() and trace_stack().

Furthermore, checking strstr(hist_field->type, "long[") is insufficient because tracepoint fields defined with __bitmask() or __rel_bitmask() also have type "__data_loc unsigned long[]" or "__rel_loc unsigned long[]", whilst create_hist_field() treats them as 4-byte scalar values without HIST_FIELD_FL_STACKTRACE.

Propagate HIST_FIELD_FL_STACKTRACE to variable references and field variables (whilst excluding variable aliases from per-element stacktrace capture), and reject any mismatch between HIST_FIELD_FL_STACKTRACE and field->is_stack in check_synth_field().

---
 kernel/trace/trace_events_hist.c |   15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)
kernel/trace/trace_events_hist.c +11 -4
@@ -2098,7 +2098,8 @@ static int init_var_ref(struct hist_fiel
 	ref_field->size = var_field->size;
 	ref_field->is_signed = var_field->is_signed;
 	ref_field->flags |= var_field->flags &
-		(HIST_FIELD_FL_TIMESTAMP | HIST_FIELD_FL_TIMESTAMP_USECS);
+		(HIST_FIELD_FL_TIMESTAMP | HIST_FIELD_FL_TIMESTAMP_USECS |
+		 HIST_FIELD_FL_STACKTRACE);
 
 	if (system) {
 		ref_field->system = kstrdup(system, GFP_KERNEL);
@@ -3286,6 +3287,7 @@ static struct field_var *create_field_va
 		ret =  -ENOMEM;
 		goto err;
 	}
+	var->flags |= val->flags & HIST_FIELD_FL_STACKTRACE;
 
 	field_var->var = var;
 	field_var->val = val;
@@ -3904,7 +3906,10 @@ static int check_synth_field(struct synt
 	    && field->is_dynamic)
 		return 0;
 
-	if (strstr(hist_field->type, "long[") && field->is_stack)
+	if (!!(hist_field->flags & HIST_FIELD_FL_STACKTRACE) != field->is_stack)
+		return -EINVAL;
+
+	if (field->is_stack)
 		return 0;
 
 	if (strcmp(field->type, hist_field->type) != 0) {
@@ -4443,7 +4448,8 @@ static int create_var_field(struct hist_
 	if (!ret && hist_data->fields[val_idx]->flags & HIST_FIELD_FL_EXECNAME)
 		update_var_execname(hist_data->fields[val_idx]);
 
-	if (!ret && hist_data->fields[val_idx]->flags &
+	if (!ret && !(hist_data->fields[val_idx]->flags & HIST_FIELD_FL_ALIAS) &&
+	    hist_data->fields[val_idx]->flags &
 	    (HIST_FIELD_FL_STRING | HIST_FIELD_FL_STACKTRACE))
 		hist_data->fields[val_idx]->var_str_idx = hist_data->n_var_str++;
 
@@ -5184,7 +5190,8 @@ static void hist_trigger_elt_update(stru
 		if (hist_field->flags & HIST_FIELD_FL_VAR) {
 			var_idx = hist_field->var.idx;
 
-			if (hist_field->flags &
+			if (!(hist_field->flags & HIST_FIELD_FL_ALIAS) &&
+			    hist_field->flags &
 			    (HIST_FIELD_FL_STRING | HIST_FIELD_FL_STACKTRACE)) {
 				unsigned int str_start, var_str_idx, idx;
 				char *str, *val_str;
author maintainer
#77 lore raw

PATCH 77/247 i2c: qcom-cci: Fix HALT_ACK reset request being dropped

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Loic Poulain <[email protected]>

commit 195f3d74b5de98969dc4fad956393bb00787efd1 upstream.

A HALT_ACK interrupt indicates that the corresponding I2C master has entered the halted state and must be reset before further operation.

The M0 and M1 HALT_ACK status bits may be asserted simultaneously in a single interrupt. However, the interrupt handler currently overwrites the reset mask when processing each HALT_ACK condition, causing the first reset request to be lost if both events are reported together.

Accumulate reset requests instead of overwriting them so that both masters are properly reset when M0 and M1 HALT_ACK events occur in the same interrupt.

---
 drivers/i2c/busses/i2c-qcom-cci.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/i2c/busses/i2c-qcom-cci.c +2 -2
@@ -168,12 +168,12 @@ static irqreturn_t cci_isr(int irq, void
 	}
 
 	if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK)) {
-		reset = CCI_RESET_CMD_M0_MASK;
+		reset |= CCI_RESET_CMD_M0_MASK;
 		ret = IRQ_HANDLED;
 	}
 
 	if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK)) {
-		reset = CCI_RESET_CMD_M1_MASK;
+		reset |= CCI_RESET_CMD_M1_MASK;
 		ret = IRQ_HANDLED;
 	}
 
author maintainer
#78 lore raw

PATCH 78/247 perf: Update cgroup time when rotating multiplexed events

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Antoine Huret <[email protected]>

commit e4c7c401c6b9174c0c893084fdaa6a84bd2520d7 upstream.

Since commit bd2756811766 ("perf: Rewrite core context handling"), perf_rotate_context() only updates the context time before scheduling out the flexible events. It no longer updates the cgroup time, which ctx_sched_out() used to do.

Cgroup events take their times from the per-cpu cgroup clock, so between two cgroup switches the rotations see a frozen clock. The events rotated out lose the running time they accumulated, and the next update, usually a read, credits the whole interval to the events that happen to be on the PMU at that moment.

As a result, the time_running of multiplexed cgroup events is wrong, and so are their scaled counts, as long as no other cgroup runs on the CPU, e.g. a busy job alone on its CPUs. Counting cycles over one second of a busy loop in a cgroup gives from half to several times the max frequency of the CPU.

Update the cgroup time before rotating the CPU context, as was done before the rewrite.

---
 kernel/events/core.c |    1 +
 1 file changed, 1 insertion(+)
kernel/events/core.c +1 -0
@@ -4451,6 +4451,7 @@ static bool perf_rotate_context(struct p
 
 	if (cpu_event) {
 		update_context_time(&cpuctx->ctx);
+		update_cgrp_time_from_cpuctx(cpuctx, false);
 		__pmu_ctx_sched_out(cpu_epc, EVENT_FLEXIBLE);
 		rotate_ctx(&cpuctx->ctx, cpu_event);
 		__pmu_ctx_sched_in(cpu_epc, EVENT_FLEXIBLE);
author maintainer
#79 lore raw

PATCH 79/247 ring-buffer: Do not show buffer_meta for CPUs without a buffer

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: David Carlier <[email protected]>

commit 3f23cae09a4677a3f7c163a68388512cb231ff22 upstream.

The per_cpu/cpuN/buffer_meta files are created for every possible CPU, but buffer->buffers[cpu] is only allocated once that CPU comes online. Reading the file of a CPU that never came online dereferences a NULL cpu_buffer in rbm_start().

Fail the open with -ENODEV when the CPU has no buffer, as the other per CPU ring buffer interfaces do.

---
 kernel/trace/ring_buffer.c |    3 +++
 1 file changed, 3 insertions(+)
kernel/trace/ring_buffer.c +3 -0
@@ -2106,6 +2106,9 @@ int ring_buffer_meta_seq_init(struct fil
 	struct seq_file *m;
 	int ret;
 
+	if (!cpumask_test_cpu(cpu, buffer->cpumask))
+		return -ENODEV;
+
 	ret = seq_open(file, &rb_meta_seq_ops);
 	if (ret)
 		return ret;
author maintainer
#80 lore raw

PATCH 80/247 ring-buffer: Fix the sub-buffer array base in rb_meta_subbuf_idx()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Donggeun Yoo <[email protected]>

commit 25132363c723afc39aace8b098f547ab4a0ea759 upstream.

On a persistent ring buffer, rb_meta_subbuf_idx() can return an index one sub-buffer too high. rb_setup_ids_meta_page() then fills subbuf_ids[] shifted by one and leaves subbuf_ids[0] NULL, which __rb_map_vma() dereferences when userspace maps trace_pipe_raw. rb_update_meta_reader() writes two such indices into meta->buffers[], which rb_cpu_meta_valid() would reject on the next boot, discarding the previous boot's trace.

A CPU's sub-buffer array starts after its meta header and the nr_subbufs integers that follow, aligned up to a sub-buffer. rb_meta_subbuf_idx() inverts that calculation but skips only the integers, not the header, so its base is one sub-buffer low whenever the omitted header size crosses the alignment boundary.

Invert the calculation with rb_subbufs_from_meta() rather than repeating it. rb_range_buffer() maps an index back to an address through the same helper, so the two directions can no longer disagree.

---
 kernel/trace/ring_buffer.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
kernel/trace/ring_buffer.c +1 -3
@@ -3203,10 +3203,8 @@ static void rb_inc_iter(struct ring_buff
 /* Return the index into the sub-buffers for a given sub-buffer */
 static int rb_meta_subbuf_idx(struct ring_buffer_meta *meta, void *subbuf)
 {
-	void *subbuf_array;
+	void *subbuf_array = rb_subbufs_from_meta(meta);
 
-	subbuf_array = (void *)meta + sizeof(int) * meta->nr_subbufs;
-	subbuf_array = (void *)ALIGN((unsigned long)subbuf_array, meta->subbuf_size);
 	return (subbuf - subbuf_array) / meta->subbuf_size;
 }
 
author maintainer
#81 lore raw

PATCH 81/247 ring-buffer: Fix false warning in ring_buffer_map_get_reader()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Krystian Kaniewski <[email protected]>

commit 89bab1dba8a0167ef0d819e0f55b6bafa038e87f upstream.

The mmap reader can warn when it catches up with a writer that is still committing events. rb_get_reader_page() returns NULL in this case, but ring_buffer_map_get_reader() treats that result as an error.

The initial rb_per_cpu_empty() check filters out an empty buffer, but it does not guarantee that the next reader page has committed data. Writers do not take reader_lock and can advance commit_page before publishing the committed length on the new page. rb_get_reader_page() can swap to that page, catch up with the writer and return NULL.

Handle NULL through the existing no-data exit without warning. Remove the caller's reader_page == commit_page check as well, since the reader-page helper already handles that case. The existing metadata update and return path remain in place.

---
 kernel/trace/ring_buffer.c |    6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)
kernel/trace/ring_buffer.c +1 -5
@@ -7358,12 +7358,8 @@ consume:
 		goto out;
 	}
 
-	/* Did the reader catch up with the writer? */
-	if (cpu_buffer->reader_page == cpu_buffer->commit_page)
-		goto out;
-
 	reader = rb_get_reader_page(cpu_buffer);
-	if (WARN_ON(!reader))
+	if (!reader)
 		goto out;
 
 	/* Check if any events were dropped */
author maintainer
#82 lore raw

PATCH 82/247 scsi: bsg: Pass reply_len to bsg_setup_queue() to fit UFS ARPMB replies

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Stanley Jhu <[email protected]>

commit ec6b083915e8146e147314a5f4f1285d07b57bfc upstream.

bsg-lib allocates a 96-byte (SCSI_SENSE_BUFFERSIZE) reply buffer per job, but struct ufs_rpmb_reply is 104 bytes: a 40-byte struct ufs_bsg_reply followed by a 64-byte EHS. When an Advanced RPMB command returns an EHS, ufshcd_advanced_rpmb_req_handler() copies it right after the 40-byte header and writes 8 bytes past the end of the buffer.

Let each BSG transport pass its reply buffer size to bsg_setup_queue() and have ufs_bsg request sizeof(struct ufs_rpmb_reply), so the full reply reaches user space. Other transports keep SCSI_SENSE_BUFFERSIZE.

Fixes 6ff265fc5ef6 ("scsi: ufs: core: bsg: Add advanced RPMB support in ufs_bsg")
---
 block/bsg-lib.c                     |   13 ++++++++++---
 drivers/scsi/mpi3mr/mpi3mr_app.c    |    2 +-
 drivers/scsi/scsi_transport_fc.c    |    6 ++++--
 drivers/scsi/scsi_transport_iscsi.c |    2 +-
 drivers/scsi/scsi_transport_sas.c   |    6 ++++--
 drivers/ufs/core/ufs_bsg.c          |    2 +-
 include/linux/bsg-lib.h             |    3 ++-
 7 files changed, 23 insertions(+), 11 deletions(-)
block/bsg-lib.c +10 -3
@@ -23,11 +23,14 @@ struct bsg_set {
 	struct bsg_device	*bd;
 	bsg_job_fn		*job_fn;
 	bsg_timeout_fn		*timeout_fn;
+	unsigned int		reply_len;
 };
 
 static int bsg_transport_sg_io_fn(struct request_queue *q, struct sg_io_v4 *hdr,
 		bool open_for_write, unsigned int timeout)
 {
+	struct bsg_set *bset =
+		container_of(q->tag_set, struct bsg_set, tag_set);
 	struct bsg_job *job;
 	struct request *rq;
 	struct bio *bio;
@@ -50,7 +53,7 @@ static int bsg_transport_sg_io_fn(struct
 	reply = job->reply;
 	memset(job, 0, sizeof(*job));
 	job->reply = reply;
-	job->reply_len = SCSI_SENSE_BUFFERSIZE;
+	job->reply_len = bset->reply_len;
 	job->dd_data = job + 1;
 
 	job->request_len = hdr->request_len;
@@ -301,9 +304,10 @@ out:
 static int bsg_init_rq(struct blk_mq_tag_set *set, struct request *req,
 		       unsigned int hctx_idx, unsigned int numa_node)
 {
+	struct bsg_set *bset = container_of(set, struct bsg_set, tag_set);
 	struct bsg_job *job = blk_mq_rq_to_pdu(req);
 
-	job->reply = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL);
+	job->reply = kzalloc(bset->reply_len, GFP_KERNEL);
 	if (!job->reply)
 		return -ENOMEM;
 	return 0;
@@ -358,10 +362,12 @@ static const struct blk_mq_ops bsg_mq_op
  * @job_fn: bsg job handler
  * @timeout: timeout handler function pointer
  * @dd_job_size: size of LLD data needed for each job
+ * @reply_len: size of the reply buffer needed for each job
  */
 struct request_queue *bsg_setup_queue(struct device *dev, const char *name,
 		struct queue_limits *lim, bsg_job_fn *job_fn,
-		bsg_timeout_fn *timeout, int dd_job_size)
+		bsg_timeout_fn *timeout, int dd_job_size,
+		unsigned int reply_len)
 {
 	struct bsg_set *bset;
 	struct blk_mq_tag_set *set;
@@ -374,6 +380,7 @@ struct request_queue *bsg_setup_queue(st
 
 	bset->job_fn = job_fn;
 	bset->timeout_fn = timeout;
+	bset->reply_len = reply_len;
 
 	set = &bset->tag_set;
 	set->ops = &bsg_mq_ops;
drivers/scsi/mpi3mr/mpi3mr_app.c +1 -1
@@ -2968,7 +2968,7 @@ void mpi3mr_bsg_init(struct mpi3mr_ioc *
 	}
 
 	q = bsg_setup_queue(bsg_dev, dev_name(bsg_dev), &lim,
-			mpi3mr_bsg_request, NULL, 0);
+			mpi3mr_bsg_request, NULL, 0, SCSI_SENSE_BUFFERSIZE);
 	if (IS_ERR(q)) {
 		ioc_err(mrioc, "%s: bsg registration failed\n",
 		    dev_name(bsg_dev));
drivers/scsi/scsi_transport_fc.c +4 -2
@@ -4290,7 +4290,8 @@ fc_bsg_hostadd(struct Scsi_Host *shost,
 	scsi_init_limits(shost, &lim);
 	lim.max_segments = min_not_zero(lim.max_segments, i->f->max_bsg_segments);
 	q = bsg_setup_queue(dev, bsg_name, &lim, fc_bsg_dispatch,
-			fc_bsg_job_timeout, i->f->dd_bsg_size);
+			fc_bsg_job_timeout, i->f->dd_bsg_size,
+			SCSI_SENSE_BUFFERSIZE);
 	if (IS_ERR(q)) {
 		dev_err(dev,
 			"fc_host%d: bsg interface failed to initialize - setup queue\n",
@@ -4323,7 +4324,8 @@ fc_bsg_rportadd(struct Scsi_Host *shost,
 	scsi_init_limits(shost, &lim);
 	lim.max_segments = min_not_zero(lim.max_segments, i->f->max_bsg_segments);
 	q = bsg_setup_queue(dev, dev_name(dev), &lim, fc_bsg_dispatch_prep,
-				fc_bsg_job_timeout, i->f->dd_bsg_size);
+				fc_bsg_job_timeout, i->f->dd_bsg_size,
+				SCSI_SENSE_BUFFERSIZE);
 	if (IS_ERR(q)) {
 		dev_err(dev, "failed to setup bsg queue\n");
 		return PTR_ERR(q);
drivers/scsi/scsi_transport_iscsi.c +1 -1
@@ -1545,7 +1545,7 @@ iscsi_bsg_host_add(struct Scsi_Host *sho
 	snprintf(bsg_name, sizeof(bsg_name), "iscsi_host%d", shost->host_no);
 	scsi_init_limits(shost, &lim);
 	q = bsg_setup_queue(dev, bsg_name, &lim, iscsi_bsg_host_dispatch, NULL,
-			0);
+			0, SCSI_SENSE_BUFFERSIZE);
 	if (IS_ERR(q)) {
 		shost_printk(KERN_ERR, shost, "bsg interface failed to "
 			     "initialize - no request queue\n");
drivers/scsi/scsi_transport_sas.c +4 -2
@@ -199,7 +199,8 @@ static int sas_bsg_initialize(struct Scs
 
 	if (rphy) {
 		q = bsg_setup_queue(&rphy->dev, dev_name(&rphy->dev), NULL,
-				sas_smp_dispatch, NULL, 0);
+				sas_smp_dispatch, NULL, 0,
+				SCSI_SENSE_BUFFERSIZE);
 		if (IS_ERR(q))
 			return PTR_ERR(q);
 		rphy->q = q;
@@ -208,7 +209,8 @@ static int sas_bsg_initialize(struct Scs
 
 		snprintf(name, sizeof(name), "sas_host%d", shost->host_no);
 		q = bsg_setup_queue(&shost->shost_gendev, name, NULL,
-				sas_smp_dispatch, NULL, 0);
+				sas_smp_dispatch, NULL, 0,
+				SCSI_SENSE_BUFFERSIZE);
 		if (IS_ERR(q))
 			return PTR_ERR(q);
 		to_sas_host_attrs(shost)->q = q;
drivers/ufs/core/ufs_bsg.c +1 -1
@@ -257,7 +257,7 @@ int ufs_bsg_probe(struct ufs_hba *hba)
 		goto out;
 
 	q = bsg_setup_queue(bsg_dev, dev_name(bsg_dev), NULL, ufs_bsg_request,
-			NULL, 0);
+			NULL, 0, sizeof(struct ufs_rpmb_reply));
 	if (IS_ERR(q)) {
 		ret = PTR_ERR(q);
 		device_del(bsg_dev);
include/linux/bsg-lib.h +2 -1
@@ -66,7 +66,8 @@ void bsg_job_done(struct bsg_job *job, i
 		  unsigned int reply_payload_rcv_len);
 struct request_queue *bsg_setup_queue(struct device *dev, const char *name,
 		struct queue_limits *lim, bsg_job_fn *job_fn,
-		bsg_timeout_fn *timeout, int dd_job_size);
+		bsg_timeout_fn *timeout, int dd_job_size,
+		unsigned int reply_len);
 void bsg_remove_queue(struct request_queue *q);
 void bsg_job_put(struct bsg_job *job);
 int __must_check bsg_job_get(struct bsg_job *job);
author maintainer
#83 lore raw

PATCH 83/247 scsi: xen/scsifront: Check for a NULL shadow entry on a backend response

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yehyeong Lee <[email protected]>

commit 8c8222c255772713eb36cde695777a1fce224917 upstream.

scsifront_do_response() validates the rqid in a backend response against VSCSIIF_MAX_REQS before using it to index info->shadow[], but then dereferences the entry to test ->inflight without checking that the slot is populated. Shadow slots are NULL before a command is submitted (the host private area is zeroed at allocation) and are reset to NULL in _scsifront_put_rqid() once a request completes, so an in-range rqid that does not correspond to an outstanding request makes the frontend dereference a NULL pointer.

A malicious or buggy backend can thus crash the guest by returning a response whose rqid is in range but not in flight -- for example a spurious response before any command has been issued, or a duplicate of one already completed. The ring has only VSCSIIF_MAX_REQS (16) slots, so before the first command every in-range rqid selects a NULL slot.

Reject a response whose shadow slot is not populated.

---
 drivers/scsi/xen-scsifront.c |    1 +
 1 file changed, 1 insertion(+)
drivers/scsi/xen-scsifront.c +1 -0
@@ -367,6 +367,7 @@ static void scsifront_do_response(struct
 	struct vscsifrnt_shadow *shadow;
 
 	if (ring_rsp->rqid >= VSCSIIF_MAX_REQS ||
+	    !info->shadow[ring_rsp->rqid] ||
 	    !info->shadow[ring_rsp->rqid]->inflight) {
 		scsifront_set_error(info, "illegal rqid returned by backend!");
 		return;
author maintainer
#84 lore raw

PATCH 84/247 scsi: libiscsi: Reject a negative task index from parse_pdu_itt

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yehyeong Lee <[email protected]>

commit 8ee2a09a1dbb257c9e828ad2497f8e4832dfb142 upstream.

A transport that implements parse_pdu_itt hands libiscsi an index taken from the PDU, and both lookups bound it from above only:

      if (i >= session->cmds_max)

i and cmds_max are both int, so a negative index passes and session->cmds[i] is read from before the array.

be2iscsi produces one. beiscsi_parse_pdu() assigns the raw tag,

      *index = (int)itt;

and beiscsi_complete_pdu() forwards an unsolicited NOP-In from the hardware async ring without replacing its ITT, so the value is the target's. It also reports the session's own age rather than the one in the tag, which leaves the age comparison in iscsi_verify_itt() with nothing to reject.

Bound the index from below in both lookups.

---
 drivers/scsi/libiscsi.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/scsi/libiscsi.c +2 -2
@@ -1191,7 +1191,7 @@ struct iscsi_task *iscsi_itt_to_task(str
 		session->tt->parse_pdu_itt(conn, itt, &i, NULL);
 	else
 		i = get_itt(itt);
-	if (i >= session->cmds_max)
+	if (i < 0 || i >= session->cmds_max)
 		return NULL;
 
 	return session->cmds[i];
@@ -1384,7 +1384,7 @@ int iscsi_verify_itt(struct iscsi_conn *
 		return ISCSI_ERR_BAD_ITT;
 	}
 
-	if (i >= session->cmds_max) {
+	if (i < 0 || i >= session->cmds_max) {
 		iscsi_conn_printk(KERN_ERR, conn,
 				  "received invalid itt index %u (max cmds "
 				   "%u.\n", i, session->cmds_max);
author maintainer
#85 lore raw

PATCH 85/247 scsi: libiscsi: Validate the ITT reflected in a Reject PDU

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yehyeong Lee <[email protected]>

commit c5958c770b2a0ee492414e26b07da9164a0d1f5d upstream.

A Reject PDU carries a copy of the header it rejects, and iscsi_handle_reject() takes the ITT out of that copy to find the task to clean up:

      memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
      ...
      task = iscsi_itt_to_task(conn, rejected_pdu.itt);

That value is whatever the target put there. iscsi_itt_to_task() bounds the index against cmds_max and checks nothing else, so any task in the pool can be named, and iscsi_nop_out_rsp() then completes it.

An index that has never been used gives a NULL task->conn: the pool is zeroed at session setup and conn is assigned only when a task is allocated. If the task was used and returned, iscsi_complete_task() hits its WARN_ON_ONCE(task->state == ISCSI_TASK_FREE) and the refcount underflows. An in-flight SCSI command is completed as successful - a 1 MiB read returned 1048576 with none of its buffer written and no warning.

Validate the reflected ITT the way iscsi_itt_to_ctask() validates a command ITT, and require the task to be in flight and not a SCSI command.

[    6.248477] Oops: general protection fault, probably for non-canonical address 0xdffffc000000000c: 0000 [#1] SMP KASAN NOPTI
[    6.249357] KASAN: null-ptr-deref in range [0x0000000000000060-0x0000000000000067]
[    6.249951] CPU: 1 UID: 0 PID: 0 Comm: swapper/1 Not tainted 7.2.0-rc5-ISCSI1-gf5098b6bae76 #1 PREEMPT(lazy)
[    6.250718] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[    6.251616] RIP: 0010:iscsi_nop_out_rsp.constprop.0+0x46/0x160
[    6.252043] Code: c1 ea 03 48 83 ec 08 80 3c 02 00 0f 85 f5 00 00 00 48 b8 00 00 00 00 00 fc ff df 48 8b 6b 58 48 8d 7d 60 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 c0 00 00 00 48 8b 45 60 48 39 c3 74 50 48 b8 00
[    6.252813] RSP: 0018:ffff88806c907b80 EFLAGS: 00010206
[    6.253042] RAX: dffffc0000000000 RBX: ffff88800607e000 RCX: ffffffff8d571a25
[    6.253345] RDX: 000000000000000c RSI: ffff88806c907c38 RDI: 0000000000000060
[    6.253642] RBP: 0000000000000000 R08: 0000000000000000 R09: fffffbfff235a504
[    6.253947] R10: 0000000000000003 R11: 7463656e6e6f6320 R12: 0000000000000000
[    6.254251] R13: 0000000000000000 R14: ffff88806c907c38 R15: 0000000000000000
[    6.254549] FS:  0000000000000000(0000) GS:ffff8880d95bc000(0000) knlGS:0000000000000000
[    6.254887] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    6.255140] CR2: 00007fc393e8ffc0 CR3: 0000000005252006 CR4: 0000000000770ef0
[    6.255439] PKRU: 55555554
[    6.255564] Call Trace:
[    6.255674]  <IRQ>
[    6.255768]  __iscsi_complete_pdu+0x18bf/0x22a0
[    6.256733]  iscsi_complete_pdu+0x54/0xa0
[    6.256905]  iscsi_tcp_data_recv_done+0xf4/0x250
[    6.257103]  iscsi_tcp_recv_skb+0x31e/0xec0
[    6.257680]  iscsi_sw_tcp_recv+0x12f/0x390
[    6.258061]  __tcp_read_sock+0x1ab/0x810
[    6.258853]  iscsi_sw_tcp_data_ready+0x18b/0x510
[    6.259808]  tcp_data_queue+0x1f13/0x4cd0
[    6.260567]  tcp_rcv_established+0x8a5/0x3a00
[    6.261931]  tcp_v4_do_rcv+0x449/0x960
[    6.262269]  tcp_v4_rcv+0x2245/0x3bc0
---
 drivers/scsi/libiscsi.c |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/scsi/libiscsi.c +5 -3
@@ -1142,13 +1142,15 @@ static int iscsi_handle_reject(struct is
 			spin_unlock(&conn->session->frwd_lock);
 			spin_lock(&conn->session->back_lock);
 		} else {
-			struct iscsi_task *task;
+			struct iscsi_task *task = NULL;
 			/*
 			 * Our nop as ping got dropped. We know the target
 			 * and transport are ok so just clean up
 			 */
-			task = iscsi_itt_to_task(conn, rejected_pdu.itt);
-			if (!task) {
+			if (!iscsi_verify_itt(conn, rejected_pdu.itt))
+				task = iscsi_itt_to_task(conn, rejected_pdu.itt);
+			if (!task || task->state == ISCSI_TASK_FREE ||
+			    task->sc) {
 				iscsi_conn_printk(KERN_ERR, conn,
 						 "Invalid pdu reject. Could "
 						 "not lookup rejected task.\n");
author maintainer
#86 lore raw

PATCH 86/247 scsi: libiscsi: Validate the task named by a management response

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yehyeong Lee <[email protected]>

commit 11bffcd9cb346732eab2914ef44fe777a3f0b585 upstream.

__iscsi_complete_pdu() fetches the task for five response types from one place and checks only that the index resolved:

      case ISCSI_OP_LOGOUT_RSP:
      case ISCSI_OP_LOGIN_RSP:
      case ISCSI_OP_TEXT_RSP:
      case ISCSI_OP_SCSI_TMFUNC_RSP:
      case ISCSI_OP_NOOP_IN:
              task = iscsi_itt_to_task(conn, hdr->itt);
              if (!task)
                      return ISCSI_ERR_BAD_ITT;

iscsi_itt_to_task() bounds the index against cmds_max and looks at nothing else, so a target can name any slot in the pool, including one that has never been used. task->conn is then NULL, and both iscsi_nop_out_rsp() and iscsi_complete_task() dereference it. One unsolicited NOP-In is enough; a Text Response carrying the same ITT crashes in iscsi_complete_task() instead.

Require the task to be in flight and not a SCSI command, the way iscsi_itt_to_ctask() does for the command opcodes.

[    6.298634] Oops: general protection fault, probably for non-canonical address 0xdffffc000000000c: 0000 [#1] SMP KASAN NOPTI
[    6.298642] KASAN: null-ptr-deref in range [0x0000000000000060-0x0000000000000067]
[    6.298652] CPU: 1 UID: 0 PID: 111 Comm: iscsistart Not tainted 7.2.0-rc5-ISCSI1-gf5098b6bae76 #1 PREEMPT(lazy)
[    6.298654] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[    6.298656] RIP: 0010:iscsi_nop_out_rsp.constprop.0+0x46/0x160
[    6.298691] Code: c1 ea 03 48 83 ec 08 80 3c 02 00 0f 85 f5 00 00 00 48 b8 00 00 00 00 00 fc ff df 48 8b 6b 58 48 8d 7d 60 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 c0 00 00 00 48 8b 45 60 48 39 c3 74 50 48 b8 00
[    6.298698] RSP: 0018:ffff88806c907be8 EFLAGS: 00010206
[    6.298701] RAX: dffffc0000000000 RBX: ffff888006116800 RCX: 0000000000000020
[    6.298702] RDX: 000000000000000c RSI: ffff88800525d578 RDI: 0000000000000060
[    6.298703] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
[    6.298706] R10: 0000000000000020 R11: 0000000000000000 R12: 0000000000000000
[    6.298706] R13: 000000000000002e R14: ffff8880056eb440 R15: ffff888006116800
[    6.298707] FS:  00007fc02d1c2740(0000) GS:ffff8880b29bc000(0000) knlGS:0000000000000000
[    6.298710] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    6.298711] CR2: 000055fe3b56ddf8 CR3: 00000000053b9004 CR4: 0000000000770ef0
[    6.298712] PKRU: 55555554
[    6.298713] Call Trace:
[    6.298714]  <IRQ>
[    6.298714]  __iscsi_complete_pdu+0x13a8/0x22a0
[    6.298758]  iscsi_complete_pdu+0x54/0xa0
[    6.298759]  iscsi_tcp_hdr_recv_done+0x870/0x2c80
[    6.298778]  iscsi_tcp_recv_skb+0x31e/0xec0
[    6.298800]  iscsi_sw_tcp_recv+0x12f/0x390
[    6.298804]  __tcp_read_sock+0x1ab/0x810
[    6.298812]  iscsi_sw_tcp_data_ready+0x18b/0x510
[    6.298814]  tcp_rcv_established+0x1f56/0x3a00
[    6.298842]  tcp_v4_do_rcv+0x449/0x960
[    6.298847]  tcp_v4_rcv+0x2245/0x3bc0
---
 drivers/scsi/libiscsi.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/libiscsi.c +1 -1
@@ -1286,7 +1286,7 @@ int __iscsi_complete_pdu(struct iscsi_co
 	case ISCSI_OP_SCSI_TMFUNC_RSP:
 	case ISCSI_OP_NOOP_IN:
 		task = iscsi_itt_to_task(conn, hdr->itt);
-		if (!task)
+		if (!task || task->state == ISCSI_TASK_FREE || task->sc)
 			return ISCSI_ERR_BAD_ITT;
 		break;
 	default:
author maintainer
#87 lore raw

PATCH 87/247 drm/amdgpu/sdma3: Work around POLL_REGMEM / FENCE hang

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Timur Kristóf <[email protected]>

commit 752480ad62964edf0254e151110d3e53a7cb37de upstream.

SDMA can randomly hang on Polaris 10. Hard to reproduce, but has been occurring sporadically in the Mesa CI on the RADV Polaris 10 job even when Mesa isn't using the SDMA engine.

When Mesa actually uses the SDMA on Polaris, the hang becomes more frequent and the reproduction rate goes up significantly in the CI system.

We don't know why the hangs occur, but it seems that inserting a single NOP packet before each POLL_REGMEM and FENCE packet works around it. At the moment it's unclear which GPUs are affected, so let's apply this to SDMA 3.x for now.

Note that this issue is not documented or acknowledged by AMD.

(cherry picked from commit c343a27ed90823d08de125f7c447d96d056b75d9)
---
 drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c |   25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)
drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c +21 -4
@@ -455,6 +455,9 @@ static void sdma_v3_0_ring_emit_hdp_flus
 	else
 		ref_and_mask = REG_SET_FIELD(ref_and_mask, GPU_HDP_FLUSH_DONE, SDMA1, 1);
 
+	/* Hang workaround */
+	amdgpu_ring_write(ring, SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP));
+
 	amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) |
 			  SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(1) |
 			  SDMA_PKT_POLL_REGMEM_HEADER_FUNC(3)); /* == */
@@ -482,6 +485,10 @@ static void sdma_v3_0_ring_emit_fence(st
 				      unsigned flags)
 {
 	bool write64bit = flags & AMDGPU_FENCE_FLAG_64BIT;
+
+	/* Hang workaround */
+	amdgpu_ring_write(ring, SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP));
+
 	/* write the fence */
 	amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE));
 	amdgpu_ring_write(ring, lower_32_bits(addr));
@@ -490,7 +497,11 @@ static void sdma_v3_0_ring_emit_fence(st
 
 	/* optionally write high bits as well */
 	if (write64bit) {
+		/* Hang workaround */
+		amdgpu_ring_write(ring, SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP));
+
 		addr += 4;
+
 		amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE));
 		amdgpu_ring_write(ring, lower_32_bits(addr));
 		amdgpu_ring_write(ring, upper_32_bits(addr));
@@ -1031,6 +1042,9 @@ static void sdma_v3_0_ring_emit_pipeline
 	uint32_t seq = ring->fence_drv.sync_seq;
 	uint64_t addr = ring->fence_drv.gpu_addr;
 
+	/* Hang workaround */
+	amdgpu_ring_write(ring, SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP));
+
 	/* wait for idle */
 	amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) |
 			  SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(0) |
@@ -1059,6 +1073,9 @@ static void sdma_v3_0_ring_emit_vm_flush
 {
 	amdgpu_gmc_emit_flush_gpu_tlb(ring, vmid, pd_addr);
 
+	/* Hang workaround */
+	amdgpu_ring_write(ring, SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP));
+
 	/* wait for flush */
 	amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) |
 			  SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(0) |
@@ -1568,11 +1585,11 @@ static const struct amdgpu_ring_funcs sd
 	.get_wptr = sdma_v3_0_ring_get_wptr,
 	.set_wptr = sdma_v3_0_ring_set_wptr,
 	.emit_frame_size =
-		6 + /* sdma_v3_0_ring_emit_hdp_flush */
+		6 + 1 + /* sdma_v3_0_ring_emit_hdp_flush */
 		3 + /* hdp invalidate */
-		6 + /* sdma_v3_0_ring_emit_pipeline_sync */
-		VI_FLUSH_GPU_TLB_NUM_WREG * 3 + 6 + /* sdma_v3_0_ring_emit_vm_flush */
-		10 + 10 + 10, /* sdma_v3_0_ring_emit_fence x3 for user fence, vm fence */
+		6 + 1 + /* sdma_v3_0_ring_emit_pipeline_sync */
+		VI_FLUSH_GPU_TLB_NUM_WREG * 3 + 6 + 1 + /* sdma_v3_0_ring_emit_vm_flush */
+		(10 + 1) * 3, /* sdma_v3_0_ring_emit_fence x3 for user fence, vm fence */
 	.emit_ib_size = 7 + 6, /* sdma_v3_0_ring_emit_ib */
 	.emit_ib = sdma_v3_0_ring_emit_ib,
 	.emit_fence = sdma_v3_0_ring_emit_fence,
author maintainer
#88 lore raw

PATCH 88/247 drm/amdgpu: dont drop the runtime PM reference twice in amdgpu_gpr

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jesse Zhang <[email protected]>

commit de9f6bcdb44f0e410031002e2f103826ea6b6259 upstream.

amdgpu_debugfs_gpr_read() drops its runtime PM reference right after reading the wave registers. When copying them to userspace fails, it jumps to the err label, which drops the reference a second time. A read into a bad buffer on Navi31 either steals a reference somebody else holds (power/runtime_usage goes from 1 to 0) or underflows the count:

amdgpu 0000:03:00.0: Runtime PM usage count underflow!

The registers are already in the kernel buffer at that point, so finish the hardware access first and copy the buffer with a single copy_to_user() instead of the put_user() loop with its own error path.

Fixes 9eee152aab56 ("drm/amdgpu/debugfs: fix ref count leak when pm_runtime_get_sync fails")
(cherry picked from commit 31035bbd74f7c3e10c163db875b8e67f8b50ee6a)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c |   21 +++------------------
 1 file changed, 3 insertions(+), 18 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +3 -18
@@ -1164,7 +1164,6 @@ static ssize_t amdgpu_debugfs_gpr_read(s
 {
 	struct amdgpu_device *adev = f->f_inode->i_private;
 	int r;
-	ssize_t result = 0;
 	uint32_t offset, se, sh, cu, wave, simd, thread, bank, *data;
 
 	if (size > 4096 || size & 3 || *pos & 3)
@@ -1209,25 +1208,11 @@ static ssize_t amdgpu_debugfs_gpr_read(s
 
 	pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
 	pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
+	amdgpu_virt_disable_access_debugfs(adev);
 
-	while (size) {
-		uint32_t value;
-
-		value = data[result >> 2];
-		r = put_user(value, (uint32_t *)buf);
-		if (r) {
-			amdgpu_virt_disable_access_debugfs(adev);
-			goto err;
-		}
-
-		result += 4;
-		buf += 4;
-		size -= 4;
-	}
-
+	r = copy_to_user(buf, data, size) ? -EFAULT : size;
 	kfree(data);
-	amdgpu_virt_disable_access_debugfs(adev);
-	return result;
+	return r;
 
 err:
 	pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
author maintainer
#89 lore raw

PATCH 89/247 drm/exec: dont immediately add prelocked obj to array of locked objs

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Christian König <[email protected]>

commit 26401a4999f1596a515b2e704e7066ac09306488 upstream.

When a contention is detected the drm_exec object grabs a reference to the contended object, unlocks all other objects and pre-locks the contended one before anything else.

The problem is that it is possible that this pre-locked object was about to be destroyed and drm_exec is now holding the last reference to it.

So we never actually see this pre-locked object again to be explicitly locked and so also never reserve a slot for a dma_fence object on it.

Should the driver now use drm_exec_for_each_locked_object() to add a new fence to all locked objects we eventually see a warning or even a BUG_ON() to prevent random memory corruption.

Fix this by keeping the prelocked object out of the array of locked objs until it was explicitly mentioned by the driver again.

If the prelocked object is never mentioned again by the driver just unlock and drop the reference at the end of the locking loop.

---
 drivers/gpu/drm/drm_exec.c |   75 ++++++++++++++++++++++++++-------------------
 include/drm/drm_exec.h     |   15 +++++----
 2 files changed, 53 insertions(+), 37 deletions(-)
drivers/gpu/drm/drm_exec.c +44 -31
@@ -49,6 +49,16 @@
 /* Dummy value used to initially enter the retry loop */
 #define DRM_EXEC_DUMMY ((void *)~0)
 
+/* Unlock and put the prelocked obj */
+static void drm_exec_drop_prelocked(struct drm_exec *exec)
+{
+	if (exec->prelocked) {
+		dma_resv_unlock(exec->prelocked->resv);
+		drm_gem_object_put(exec->prelocked);
+		exec->prelocked = NULL;
+	}
+}
+
 /* Unlock all objects and drop references */
 static void drm_exec_unlock_all(struct drm_exec *exec)
 {
@@ -59,8 +69,7 @@ static void drm_exec_unlock_all(struct d
 		drm_gem_object_put(obj);
 	}
 
-	drm_gem_object_put(exec->prelocked);
-	exec->prelocked = NULL;
+	drm_exec_drop_prelocked(exec);
 }
 
 /**
@@ -119,6 +128,14 @@ EXPORT_SYMBOL(drm_exec_fini);
 bool drm_exec_cleanup(struct drm_exec *exec)
 {
 	if (likely(!exec->contended)) {
+
+		/*
+		 * The contended and prelocked object might never be passed to
+		 * drm_exec_lock_obj() again. This can happen when the object
+		 * is about to be cleaned up and we now hold the last reference
+		 * to it. Make sure that we don't unnecessarily keep it around.
+		 */
+		drm_exec_drop_prelocked(exec);
 		ww_acquire_done(&exec->ticket);
 		return false;
 	}
@@ -176,16 +193,16 @@ static int drm_exec_lock_contended(struc
 		dma_resv_lock_slow(obj->resv, &exec->ticket);
 	}
 
-	ret = drm_exec_obj_locked(exec, obj);
-	if (unlikely(ret))
-		goto error_unlock;
+	/*
+	 * It is perfectly possible that we see another contention before the
+	 * first one is fully handled.
+	 */
+	drm_exec_drop_prelocked(exec);
 
+	/* We move the reference from contended to prelocked here */
 	exec->prelocked = obj;
 	return 0;
 
-error_unlock:
-	dma_resv_unlock(obj->resv);
-
 error_dropref:
 	drm_gem_object_put(obj);
 	return ret;
@@ -213,35 +230,31 @@ int drm_exec_lock_obj(struct drm_exec *e
 	if (exec->prelocked == obj) {
 		drm_gem_object_put(exec->prelocked);
 		exec->prelocked = NULL;
-		return 0;
-	}
+	} else {
+		if (exec->flags & DRM_EXEC_INTERRUPTIBLE_WAIT)
+			ret = dma_resv_lock_interruptible(obj->resv,
+							  &exec->ticket);
+		else
+			ret = dma_resv_lock(obj->resv, &exec->ticket);
+
+		if (unlikely(ret == -EDEADLK)) {
+			drm_gem_object_get(obj);
+			exec->contended = obj;
+			return -EDEADLK;
+		}
+
+		if (unlikely(ret == -EALREADY) &&
+		    exec->flags & DRM_EXEC_IGNORE_DUPLICATES)
+			return 0;
 
-	if (exec->flags & DRM_EXEC_INTERRUPTIBLE_WAIT)
-		ret = dma_resv_lock_interruptible(obj->resv, &exec->ticket);
-	else
-		ret = dma_resv_lock(obj->resv, &exec->ticket);
-
-	if (unlikely(ret == -EDEADLK)) {
-		drm_gem_object_get(obj);
-		exec->contended = obj;
-		return -EDEADLK;
+		if (unlikely(ret))
+			return ret;
 	}
 
-	if (unlikely(ret == -EALREADY) &&
-	    exec->flags & DRM_EXEC_IGNORE_DUPLICATES)
-		return 0;
-
-	if (unlikely(ret))
-		return ret;
-
 	ret = drm_exec_obj_locked(exec, obj);
 	if (ret)
-		goto error_unlock;
-
-	return 0;
+		dma_resv_unlock(obj->resv);
 
-error_unlock:
-	dma_resv_unlock(obj->resv);
 	return ret;
 }
 EXPORT_SYMBOL(drm_exec_lock_obj);
include/drm/drm_exec.h +9 -6
@@ -67,13 +67,15 @@ drm_exec_obj(struct drm_exec *exec, unsi
 
 /* Helper for drm_exec_for_each_locked_object(). Internal use only. */
 #define __drm_exec_for_each_locked_object(exec, obj, __index)		\
-	for (unsigned long __index = 0; ((obj) = drm_exec_obj(exec, __index)); ++__index)
+	for (unsigned long __index = 0; ((obj) = drm_exec_obj(exec, __index)); \
+	     ++__index)
 /**
  * drm_exec_for_each_locked_object - iterate over all the locked objects
  * @exec: drm_exec object
  * @obj: the current GEM object
  *
- * Iterate over all the locked GEM objects inside the drm_exec object.
+ * Iterate over all the explicitly locked GEM objects inside the drm_exec
+ * container, except for the contended and implicit prelocked one.
  */
 #define drm_exec_for_each_locked_object(exec, obj)			\
 	__drm_exec_for_each_locked_object(exec, obj, __UNIQUE_ID(drm_exec))
@@ -88,12 +90,13 @@ drm_exec_obj(struct drm_exec *exec, unsi
  * @exec: drm_exec object
  * @obj: the current GEM object
  *
- * Iterate over all the locked GEM objects inside the drm_exec object in
- * reverse locking order. Note that the internal index may wrap around,
- * but that will be caught by drm_exec_obj(), returning a NULL object.
+ * Iterate over all the explicitly locked GEM objects inside the drm_exec
+ * container in reverse locking order. Note that the internal index may wrap
+ * around, but that will be caught by drm_exec_obj(), returning a NULL object.
  */
 #define drm_exec_for_each_locked_object_reverse(exec, obj)		\
-	__drm_exec_for_each_locked_object_reverse(exec, obj, __UNIQUE_ID(drm_exec))
+	__drm_exec_for_each_locked_object_reverse(exec, obj,		\
+						  __UNIQUE_ID(drm_exec))
 
 /**
  * drm_exec_until_all_locked - loop until all GEM objects are locked
author maintainer
#90 lore raw

PATCH 90/247 tracing: Fix the shift out of bounds in the log2 histogram modifier

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Donggeun Yoo <[email protected]>

commit 647c2f97fcb94bc642cefe6db004999974563f06 upstream.

The .log2 key modifier files a value under its base-2 order:

  # echo 'hist:keys=bytes_req.log2' > events/kmem/kmalloc/trigger

hist_field_log2() computes that as ilog2(roundup_pow_of_two(val)). The out-of-line form of roundup_pow_of_two() is

	return 1UL << fls_long(n - 1);

which shifts by 64 on a 64-bit kernel both when n is 0, where n - 1 wraps to ULONG_MAX, and when n is above 2^63. Both are ordinary keys, because the field fetch hands every value over as a u64: a syscall argument of 0 reaches the first, and one above 2^63 reaches the second.

Keying sys_enter_lseek on offset.log2 and passing 0, 1, 2, 3, 1000, 2^63+1 and U64_MAX gives

  { offset: ~ 2^1  } hitcount:          1
  { offset: ~ 2^10 } hitcount:          1
  { offset: ~ 2^2  } hitcount:          1
  { offset: ~ 2^0  } hitcount:          4

Keys above 2^63 land in the lowest bucket. With CONFIG_UBSAN_SHIFT=y the shift is also reported:

  UBSAN: shift-out-of-bounds in include/linux/log2.h:57:13
  shift exponent 64 is too large for 64-bit type 'long unsigned int'
   __ubsan_handle_shift_out_of_bounds.cold+0xdd/0x1cb
   hist_fn_call.cold+0x8b/0xd9
   event_hist_trigger+0x1cc/0x790
   ftrace_syscall_enter+0x197/0x360
   do_syscall_64+0x402/0x4b0

Use order_base_2(), which is ilog2(n - 1) + 1 for n > 1 and 0 below that. It returns what the old expression returned on every input the old one was defined for. Where it was not, 0 stays in bucket 0, which is where x86_64 had been putting it, and the keys above 2^63 move into bucket 64.

Fixes 4b94f5b7b4a5 ("tracing: Add hist trigger 'log2' modifier")
Assisted-by Claude:claude-fable-5
---
 kernel/trace/trace_events_hist.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_events_hist.c +1 -1
@@ -283,7 +283,7 @@ static u64 hist_field_log2(struct hist_f
 
 	u64 val = hist_fn_call(operand, elt, buffer, rbe, event);
 
-	return (u64) ilog2(roundup_pow_of_two(val));
+	return order_base_2(val);
 }
 
 static u64 hist_field_bucket(struct hist_field *hist_field,
author maintainer
#91 lore raw

PATCH 91/247 tracing: Take trace_types_lock when dropping the buffer_meta reference

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: David Carlier <[email protected]>

commit 2afe7575fb24b9d0476ae4a3a9862f7997216aa4 upstream.

tracing_buffer_meta_open() drops the trace_array reference with __trace_array_put() when ring_buffer_meta_seq_init() fails, without holding trace_types_lock. Use trace_array_put() like every other open error path.

Fixes 950032ffcee7 ("ring-buffer: Add output of ring buffer meta page")
---
 kernel/trace/trace.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace.c +1 -1
@@ -6839,7 +6839,7 @@ static int tracing_buffer_meta_open(stru
 
 	ret = ring_buffer_meta_seq_init(filp, tr->array_buffer.buffer, cpu);
 	if (ret < 0)
-		__trace_array_put(tr);
+		trace_array_put(tr);
 	return ret;
 }
 
author maintainer
#92 lore raw

PATCH 92/247 tracing: Check operator stack capacity before pushing

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Taeyang Lee <[email protected]>

commit c47a7b11bcb3d45e29723afcf6336866476b1d08 upstream.

predicate_parse() uses the first operator stack entry before processing any opening parentheses. A push then increments top before writing. The existing check still permits that write when top already points to the last allocated entry, which can access beyond the operator stack.

Reject the push when top reaches the last allocated entry.

Fixes 39f7c41c908b ("tracing/filters: Enable filtering a cpumask field by another cpumask")
---
 kernel/trace/trace_events_filter.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_events_filter.c +1 -1
@@ -512,7 +512,7 @@ predicate_parse(const char *str, int nr_
 
 		switch (*next) {
 		case '(':					/* #2 */
-			if (top - op_stack > nr_parens) {
+			if (top - op_stack >= nr_parens - 1) {
 				ret = -EINVAL;
 				goto out_free;
 			}
author maintainer
#93 lore raw

PATCH 93/247 tracing: Do not initialize tracefs work if tracing is disabled

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Amit Machhiwal <[email protected]>

commit b3b974008388ab59e44d764ba7a63c988fec2524 upstream.

If trace buffer allocation fails during early boot (e.g. in early_trace_init() -> allocate_trace_buffers()), tracing_disabled is left set to 1 and the global trace array events list remains uninitialized.

Later during boot, tracer_init_tracefs() (invoked via fs_initcall) queues tracerfs_init_work asynchronously without checking if tracing was disabled. When tracer_init_tracefs_work_func() runs, it calls event_trace_init() and init_tracer_tracefs(&global_trace, NULL) unconditionally. This leads to __find_event_file() dereferencing an uninitialized tr->events list, causing an early boot NULL pointer dereference Oops:

  BUG: Kernel NULL pointer dereference on read at 0x00000010
  Faulting instruction address: 0xc000000000485be0
  Oops: Kernel access of bad area, sig: 7 [#1]
  ...
  NIP [c000000000485be0] __find_event_file+0x70/0x3c0
  LR  [c000000000448874] init_tracer_tracefs+0x274/0xc80
  Call Trace:
   init_tracer_tracefs+0x274/0xc80
   tracer_init_tracefs_work_func+0x50/0x320
   process_one_work+0x1e8/0x5c0
   worker_thread+0x1dc/0x3d0
   kthread+0x194/0x1b0
   start_kernel_thread+0x14/0x18

Check tracing_disabled at the beginning of tracer_init_tracefs_work_func() and return immediately if tracing is disabled or failed to initialize, preventing the crash.

---
 kernel/trace/trace.c |    6 ++++++
 1 file changed, 6 insertions(+)
kernel/trace/trace.c +6 -0
@@ -10005,6 +10005,12 @@ static struct notifier_block trace_modul
 
 static __init void tracer_init_tracefs_work_func(struct work_struct *work)
 {
+	/*
+	 * Do not attempt to initialize tracefs if tracing was disabled or
+	 * failed to allocate
+	 */
+	if (tracing_disabled)
+		return;
 
 	event_trace_init();
 
author maintainer
#94 lore raw

PATCH 94/247 tracing: Fix hist trigger timestamps for buffered events

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Tengda Wu <[email protected]>

commit 9414ca3b99e5a7f1f3bf51c2a01b383e1b355dd0 upstream.

Syzkaller triggered a WARN_ON_ONCE(!nest) warning:

  WARNING: kernel/trace/ring_buffer.c:821 at ring_buffer_event_time_stamp
  Call trace:
    ring_buffer_event_time_stamp
    hist_field_timestamp
    hist_fn_call
    event_hist_trigger
    event_triggers_call
    __event_trigger_test_discard
    trace_event_buffer_commit
    do_trace_event_raw_event_sched_switch
    trace_event_raw_event_sched_switch

The warning is probabilistically reproducible and is related to syzkaller executing the following commands:

echo 'prev_pid == 999999' > events/sched/sched_switch/filter echo 'hist:keys=common_timestamp' > events/sched/sched_switch/trigger

The specific triggering process is as follows:

CPU 0 CPU 1
(context switching) (echo 'hist:keys=common_timestamp' > \
events/sched/sched_switch/trigger)
do_trace_event_raw_event_##call
trace_event_buffer_lock_reserve

    if (!tr->no_filter_buffering_ref &&
        trace_file->flags & EVENT_FILE_FL_FILTERED)
        return entry;  // return directly
                                                event_hist_trigger_parse
                                                  hist_register_trigger
                                                    tracing_set_filter_buffering(file->tr, true);
                                                      tr->no_filter_buffering_ref++;
    __trace_buffer_lock_reserve
      rb_start_commit
        local_inc(&cpu_buffer->committing);  // skipped and not executed

trace_event_buffer_commit
__event_trigger_test_discard
event_triggers_call
event_hist_trigger
hist_fn_call
hist_field_timestamp
ring_buffer_event_time_stamp
nest = local_read(&cpu_buffer->committing); WARN_ON_ONCE(!nest) // trigger warning

The root cause is that when an event file has a filter attached and the hist trigger has not yet been attached, trace_event_buffer_lock_reserve() writes the event into the per-CPU trace_buffered_event temp buffer instead of reserving it in the ring buffer, so cpu_buffer->committing is not incremented. When the event is later processed at commit time by a hist trigger, hist_field_timestamp() -> ring_buffer_event_time_stamp() sees a zero committing count, triggering WARN_ON_ONCE(!nest).

tracing_event_time_stamp() was added by commit d8279bfc5e959 ("tracing:
Add tracing_event_time_stamp() API") for exactly this case: if the event is the per-CPU trace_buffered_event, it returns the current ring buffer timestamp. But it never gained a caller. Use it in hist_field_timestamp(), so that a hist trigger that races onto a buffered event records the current time, which is only marginally later than when the event was recorded, instead of warning and recording a bogus timestamp.

Fixes b94bc80df648 ("tracing: Use a no_filter_buffering_ref to stop using the filter buffer")
---
 kernel/trace/trace_events_hist.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_events_hist.c +1 -1
@@ -866,7 +866,7 @@ static u64 hist_field_timestamp(struct h
 	struct hist_trigger_data *hist_data = hist_field->hist_data;
 	struct trace_array *tr = hist_data->event_file->tr;
 
-	u64 ts = ring_buffer_event_time_stamp(buffer, rbe);
+	u64 ts = tracing_event_time_stamp(buffer, rbe);
 
 	if (hist_data->attrs->ts_in_usecs && trace_clock_in_ns(tr))
 		ts = ns2usecs(ts);
author maintainer
#95 lore raw

PATCH 95/247 wifi: p54: validate firmware record lengths

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Heyang Tan <[email protected]>

commit db7b86bd97b380ece8fa39cfdd7502a9fd35e56f upstream.

The p54 firmware parser accesses component ID and component version records without checking that their lengths cover the fields being read. A malformed record can therefore make the parser read past the record.

Validate the component ID and component version record lengths before accessing their data. The descriptor record is also checked against the shortest descriptor length found in the supported firmware images.

Reject descriptor values that underflow the RX offset or produce an empty RX range before updating the driver state.

---
 drivers/net/wireless/intersil/p54/fwio.c |   42 +++++++++++++++++++++++++++----
 1 file changed, 37 insertions(+), 5 deletions(-)
drivers/net/wireless/intersil/p54/fwio.c +37 -5
@@ -52,6 +52,13 @@ int p54_parse_firmware(struct ieee80211_
 		u32 code = le32_to_cpu(bootrec->code);
 		switch (code) {
 		case BR_CODE_COMPONENT_ID:
+			if (len < sizeof(struct bootrec_comp_id) /
+				sizeof(*bootrec->data)) {
+				wiphy_err(priv->hw->wiphy,
+					  "firmware component ID is too short\n");
+				return -EINVAL;
+			}
+
 			priv->fw_interface = be32_to_cpup((__be32 *)
 					     bootrec->data);
 			switch (priv->fw_interface) {
@@ -72,16 +79,41 @@ int p54_parse_firmware(struct ieee80211_
 			}
 			break;
 		case BR_CODE_COMPONENT_VERSION:
+			if (len < DIV_ROUND_UP(sizeof(struct bootrec_comp_ver),
+					       sizeof(*bootrec->data))) {
+				wiphy_err(priv->hw->wiphy,
+					  "firmware component version is too short\n");
+				return -EINVAL;
+			}
+
 			/* 24 bytes should be enough for all firmwares */
-			if (strnlen((unsigned char *) bootrec->data, 24) < 24)
-				fw_version = (unsigned char *) bootrec->data;
+			if (strnlen((unsigned char *)bootrec->data,
+				    sizeof(struct bootrec_comp_ver)) <
+				    sizeof(struct bootrec_comp_ver))
+				fw_version = (unsigned char *)bootrec->data;
 			break;
 		case BR_CODE_DESCR: {
 			struct bootrec_desc *desc =
 				(struct bootrec_desc *)bootrec->data;
-			priv->rx_start = le32_to_cpu(desc->rx_start);
-			/* FIXME add sanity checking */
-			priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500;
+			u32 rx_start, rx_end;
+
+			/* 0xa is the shortest descriptor in supported firmware. */
+			if (len < 0xa) {
+				wiphy_err(priv->hw->wiphy,
+					  "firmware descriptor is too short\n");
+				return -EINVAL;
+			}
+
+			rx_start = le32_to_cpu(desc->rx_start);
+			rx_end = le32_to_cpu(desc->rx_end);
+			if (rx_end < 0x3500 || rx_end - 0x3500 <= rx_start) {
+				wiphy_err(priv->hw->wiphy,
+					  "firmware descriptor has invalid RX range\n");
+				return -EINVAL;
+			}
+
+			priv->rx_start = rx_start;
+			priv->rx_end = rx_end - 0x3500;
 			priv->headroom = desc->headroom;
 			priv->tailroom = desc->tailroom;
 			priv->privacy_caps = desc->privacy_caps;
author maintainer
#96 lore raw

PATCH 96/247 tcp: fix use-after-free in do_tcp_getsockopt(TCP_CONGESTION)

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Cen Zhang (Microsoft Security FORGE Labs) <[email protected]>

commit 5271b79b7ad68dcb222e893773f92bdabf7750f3 upstream.

do_tcp_getsockopt() reads icsk->icsk_ca_ops->name without holding rcu_read_lock(). Since commit 0baf26b0fcd7 ("bpf: tcp: Support tcp_congestion_ops in bpf"), icsk_ca_ops can point to dynamically allocated BPF struct_ops memory that may be freed concurrently via setsockopt(TCP_CONGESTION), leading to a use-after-free.

  BUG: KASAN: slab-use-after-free in _copy_to_user+0x37/0x60
  Read of size 16 at addr ffff888013505260 by task exploit/149
   _copy_to_user+0x37/0x60
   do_tcp_getsockopt+0x158a/0x2460 (net/ipv4/tcp.c:4585)
   tcp_getsockopt+0x91/0xf0
   __sys_getsockopt+0xf7/0x170

Fix this by holding rcu_read_lock() around the ca_ops->name access, using READ_ONCE() to load icsk_ca_ops, and copying the name to a stack buffer before releasing the lock. Also annotate the relevant icsk_ca_ops stores with WRITE_ONCE() to fix the accompanying KCSAN data-race issue.

---
 net/ipv4/tcp.c           |   14 +++++++++++---
 net/ipv4/tcp_cong.c      |    4 ++--
 net/ipv4/tcp_dctcp.c     |    2 +-
 net/ipv4/tcp_minisocks.c |    2 +-
 net/ipv4/tcp_output.c    |    2 +-
 5 files changed, 16 insertions(+), 8 deletions(-)
net/ipv4/tcp.c +11 -3
@@ -4418,16 +4418,24 @@ int do_tcp_getsockopt(struct sock *sk, i
 		val = !inet_csk_in_pingpong_mode(sk);
 		break;
 
-	case TCP_CONGESTION:
+	case TCP_CONGESTION: {
+		char ca_name[TCP_CA_NAME_MAX] = {};
+
 		if (copy_from_sockptr(&len, optlen, sizeof(int)))
 			return -EFAULT;
 		len = min_t(unsigned int, len, TCP_CA_NAME_MAX);
 		if (copy_to_sockptr(optlen, &len, sizeof(int)))
 			return -EFAULT;
-		if (copy_to_sockptr(optval, icsk->icsk_ca_ops->name, len))
+
+		rcu_read_lock();
+		memcpy(ca_name, READ_ONCE(icsk->icsk_ca_ops)->name,
+		       sizeof(ca_name));
+		rcu_read_unlock();
+
+		if (copy_to_sockptr(optval, ca_name, len))
 			return -EFAULT;
 		return 0;
-
+	}
 	case TCP_ULP:
 		if (copy_from_sockptr(&len, optlen, sizeof(int)))
 			return -EFAULT;
net/ipv4/tcp_cong.c +2 -2
@@ -222,7 +222,7 @@ void tcp_assign_congestion_control(struc
 	ca = rcu_dereference(net->ipv4.tcp_congestion_control);
 	if (unlikely(!bpf_try_module_get(ca, ca->owner)))
 		ca = &tcp_reno;
-	icsk->icsk_ca_ops = ca;
+	WRITE_ONCE(icsk->icsk_ca_ops, ca);
 	rcu_read_unlock();
 
 	memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
@@ -252,7 +252,7 @@ static void tcp_reinit_congestion_contro
 	struct inet_connection_sock *icsk = inet_csk(sk);
 
 	tcp_cleanup_congestion_control(sk);
-	icsk->icsk_ca_ops = ca;
+	WRITE_ONCE(icsk->icsk_ca_ops, ca);
 	icsk->icsk_ca_setsockopt = 1;
 	memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
 
net/ipv4/tcp_dctcp.c +1 -1
@@ -111,7 +111,7 @@ __bpf_kfunc static void dctcp_init(struc
 	/* No ECN support? Fall back to Reno. Also need to clear
 	 * ECT from sk since it is set during 3WHS for DCTCP.
 	 */
-	inet_csk(sk)->icsk_ca_ops = &dctcp_reno;
+	WRITE_ONCE(inet_csk(sk)->icsk_ca_ops, &dctcp_reno);
 	INET_ECN_dontxmit(sk);
 }
 
net/ipv4/tcp_minisocks.c +1 -1
@@ -468,7 +468,7 @@ void tcp_ca_openreq_child(struct sock *s
 		ca = tcp_ca_find_key(ca_key);
 		if (likely(ca && bpf_try_module_get(ca, ca->owner))) {
 			icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst);
-			icsk->icsk_ca_ops = ca;
+			WRITE_ONCE(icsk->icsk_ca_ops, ca);
 			ca_got_dst = true;
 		}
 		rcu_read_unlock();
net/ipv4/tcp_output.c +1 -1
@@ -3888,7 +3888,7 @@ static void tcp_ca_dst_init(struct sock
 	if (likely(ca && bpf_try_module_get(ca, ca->owner))) {
 		bpf_module_put(icsk->icsk_ca_ops, icsk->icsk_ca_ops->owner);
 		icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst);
-		icsk->icsk_ca_ops = ca;
+		WRITE_ONCE(icsk->icsk_ca_ops, ca);
 	}
 	rcu_read_unlock();
 }
author maintainer
#97 lore raw

PATCH 97/247 tcp: fix use-after-free in do_tcp_getsockopt(TCP_CC_INFO)

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Cen Zhang (Microsoft Security FORGE Labs) <[email protected]>

commit 385e474086c2e7e29e2dded690be40dc273e20ee upstream.

do_tcp_getsockopt() reads icsk->icsk_ca_ops and dereferences the get_info function pointer without rcu_read_lock(). With BPF struct_ops congestion control, ca_ops can point to dynamically allocated memory that is freed concurrently, resulting in a use-after-free when the kernel dereferences or calls through the stale pointer.

  BUG: KASAN: slab-use-after-free in do_tcp_getsockopt+0x2037/0x23e0
  Read of size 8 at addr ffff888013701258 by task exploit/149
   do_tcp_getsockopt+0x2037/0x23e0 (net/ipv4/tcp.c:4564)
   tcp_getsockopt+0x91/0xf0
   __sys_getsockopt+0xf7/0x170

Fix this by wrapping the ca_ops load and get_info call within rcu_read_lock()/rcu_read_unlock(), and using READ_ONCE() to load the icsk_ca_ops pointer.

---
 net/ipv4/tcp.c       |    4 +++-
 net/ipv4/tcp_dctcp.c |    2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)
net/ipv4/tcp.c +3 -1
@@ -4403,9 +4403,11 @@ int do_tcp_getsockopt(struct sock *sk, i
 		if (copy_from_sockptr(&len, optlen, sizeof(int)))
 			return -EFAULT;
 
-		ca_ops = icsk->icsk_ca_ops;
+		rcu_read_lock();
+		ca_ops = READ_ONCE(icsk->icsk_ca_ops);
 		if (ca_ops && ca_ops->get_info)
 			sz = ca_ops->get_info(sk, ~0U, &attr, &info);
+		rcu_read_unlock();
 
 		len = min_t(unsigned int, len, sz);
 		if (copy_to_sockptr(optlen, &len, sizeof(int)))
net/ipv4/tcp_dctcp.c +1 -1
@@ -224,7 +224,7 @@ static size_t dctcp_get_info(struct sock
 	if (ext & (1 << (INET_DIAG_DCTCPINFO - 1)) ||
 	    ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
 		memset(&info->dctcp, 0, sizeof(info->dctcp));
-		if (inet_csk(sk)->icsk_ca_ops != &dctcp_reno) {
+		if (READ_ONCE(inet_csk(sk)->icsk_ca_ops) != &dctcp_reno) {
 			info->dctcp.dctcp_enabled = 1;
 			info->dctcp.dctcp_ce_state = (u16) ca->ce_state;
 			info->dctcp.dctcp_alpha = ca->dctcp_alpha;
author maintainer
#98 lore raw

PATCH 98/247 cgroup: fix spurious SIGKILL of CLONE_INTO_CGROUP children

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Etienne Perot <[email protected]>

commit 8e359920216689b3b79e0fe8961a77fe312a511f upstream.

Since commit b69bb476dee9 ("cgroup: fix race between fork and cgroup.kill"), the fork path snapshots the kill_seq of the child's future cgroup into kargs->kill_seq, and cgroup_post_fork() SIGKILLs the child if that cgroup's kill_seq has changed in the meantime, to catch forks racing with a cgroup.kill sweep.

For CLONE_INTO_CGROUP, however, the snapshot in cgroup_css_set_fork() is taken before the target cgroup has been resolved: kargs->cgrp is always NULL at this point (it is only set at the end of the function). So the "if (kargs->cgrp)" branch is dead code and the snapshot always records the kill_seq of the parent's cgroup. cgroup_post_fork() then compares it with the kill_seq of the target cgroup, so the child gets SIGKILLed whenever the two cgroups have been killed a different number of times.

As a result, once cgroup.kill has been written to a cgroup, every child subsequently cloned into it with clone3(CLONE_INTO_CGROUP) is killed on the spot, for as long as the cgroup exists: kill_seq is not exposed to userspace and never resets.

Re-snapshot kill_seq from the target cgroup once it has been resolved, and drop the dead branch at the early snapshot site.

This does not reopen the race fixed by b69bb476dee9. For CLONE_INTO_CGROUP, everything from the snapshot to the check in cgroup_post_fork() runs with cgroup_mutex held, and kill_seq is only ever incremented under cgroup_mutex.

tj: Updated the comment above kill_seq to reflect the new serialization rules as suggested by Shakeel Butt.

Fixes b69bb476dee9 ("cgroup: fix race between fork and cgroup.kill")
Cc Shakeel Butt <[email protected]>
Assisted-by LLM
---
 include/linux/cgroup-defs.h |    5 ++++-
 kernel/cgroup/cgroup.c      |    6 ++----
 2 files changed, 6 insertions(+), 5 deletions(-)
include/linux/cgroup-defs.h +4 -1
@@ -458,7 +458,10 @@ struct cgroup {
 
 	int nr_threaded_children;	/* # of live threaded child cgroups */
 
-	/* sequence number for cgroup.kill, serialized by css_set_lock. */
+	/*
+	 * Sequence number for cgroup.kill. Incremented with both cgroup_mutex
+	 * and css_set_lock held. Readers hold either one.
+	 */
 	unsigned int kill_seq;
 
 	struct kernfs_node *kn;		/* cgroup kernfs entry */
kernel/cgroup/cgroup.c +2 -4
@@ -6602,10 +6602,7 @@ static int cgroup_css_set_fork(struct ke
 	spin_lock_irq(&css_set_lock);
 	cset = task_css_set(current);
 	get_css_set(cset);
-	if (kargs->cgrp)
-		kargs->kill_seq = kargs->cgrp->kill_seq;
-	else
-		kargs->kill_seq = cset->dfl_cgrp->kill_seq;
+	kargs->kill_seq = cset->dfl_cgrp->kill_seq;
 	spin_unlock_irq(&css_set_lock);
 
 	if (!(kargs->flags & CLONE_INTO_CGROUP)) {
@@ -6670,6 +6667,7 @@ static int cgroup_css_set_fork(struct ke
 	put_css_set(cset);
 	fput(f);
 	kargs->cgrp = dst_cgrp;
+	kargs->kill_seq = dst_cgrp->kill_seq;
 	return ret;
 
 err:
author maintainer
#99 lore raw

PATCH 99/247 dm cache: fix demotion stats in passthrough mode

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ming-Hung Tsai <[email protected]>

commit 2ccb8878c149443c6acf628b438c9c942c20abb2 upstream.

The demotion counter is incremented per incoming write bio before the invalidation begins, causing the demotion count to exceed the actual number of cached blocks when multiple bios target the same cached block. Additionally, the counter is incremented unconditionally regardless of invalidation failure.

Reproduce steps:

1. Create a cache device consisting of 512 cache entries

modprobe brd rd_size=262144
dmsetup create cmeta --table "0 8192 linear /dev/ram0 0" dmsetup create cdata --table "0 65536 linear /dev/ram0 8192" dmsetup create corig --table "0 65536 linear /dev/ram0 262144" dd if=/dev/zero of=/dev/mapper/cmeta bs=4k count=1 oflag=direct dmsetup create cache --table "0 65536 cache /dev/mapper/cmeta \
/dev/mapper/cdata /dev/mapper/corig 128 2 metadata2 writethrough smq 0"

2. Populate the cache, and record the number of cached blocks

fio --name=populate --filename=/dev/mapper/cache --rw=randwrite --bs=4k \
--direct=1 --ioengine=libaio --iodepth=32 --io_size=2048m nr_cached=$(dmsetup status cache | awk '{split($7, a, "/"); print a[1]}')

3. Reload the cache into passthrough mode

dmsetup suspend cache
dmsetup reload cache --table "0 65536 cache /dev/mapper/cmeta \
/dev/mapper/cdata /dev/mapper/corig 128 2 metadata2 passthrough smq 0" dmsetup resume cache

4. Write to the passthrough cache with multiple jobs to trigger multiple bios hitting the same cached block.

fio --filename=/dev/mapper/cache --name=test --rw=write --bs=4k \
--direct=1 --ioengine=libaio --iodepth=32 --numjobs=4

5. Check if demoted matches cached block count. These numbers should match but may differ due to overcounting per bio.

nr_demoted=$(dmsetup status cache | awk '{print $12}') echo "$nr_cached, $nr_demoted"

Fix by moving the demotion counter increment into invalidate_complete(), gated on the success flag.

Fixes b29d4986d0da ("dm cache: significant rework to leverage dm-bio-prison-v2")
---
 drivers/md/dm-cache-target.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/md/dm-cache-target.c +3 -1
@@ -1461,6 +1461,9 @@ static void invalidate_complete(struct d
 	struct bio_list bios;
 	struct cache *cache = mg->cache;
 
+	if (success)
+		atomic_inc(&cache->stats.demotion);
+
 	bio_list_init(&bios);
 	if (mg->cell) {
 		if (dm_cell_unlock_v2(cache->prison, mg->cell, &bios))
@@ -1732,7 +1735,6 @@ static int map_bio(struct cache *cache,
 		if (passthrough_mode(cache)) {
 			if (bio_data_dir(bio) == WRITE) {
 				bio_drop_shared_lock(cache, bio);
-				atomic_inc(&cache->stats.demotion);
 				invalidate_start(cache, cblock, block, bio);
 				return DM_MAPIO_SUBMITTED;
 			} else
author maintainer
#100 lore raw

PATCH 100/247 dm cache: fix issue with background work locking

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Benjamin Marzinski <[email protected]>

commit bc9781c0247de107876f32929f1637db93a42b34 upstream.

dm cache used a rw_semaphore for background_work_lock. Write locks on rw_semaphores have strict owner semantics, but there was no guarantee that the process that locked background_work_lock was the same process that unlocked it. This can be easily seen using a kernel compiled with CONFIG_DEBUG_RWSEMS. Given a dm cache device <cache>, run: 'dmsetup suspend <cache> && dmsetup resume <cache>'. This will trigger a kernel warning:

DEBUG_RWSEMS_WARN_ON((rwsem_owner(sem) != current) && !rwsem_test_oflags(sem, RWSEM_NONSPINNABLE))

triggered by cache_resume(). To fix this, switch from a rw_semaphore to a spinlock and a wait queue. dm cache already has a wait queue and associated counter, migration_wait and nr_allocated_migrations, that was getting woken up when background work was getting completed, but wasn't actually used by anything. This is replaced by the background_work queue and counter.

Fixes b29d4986d0da ("dm cache: significant rework to leverage dm-bio-prison-v2")
---
 drivers/md/dm-cache-target.c |   55 +++++++++++++++++++++----------------------
 1 file changed, 28 insertions(+), 27 deletions(-)
drivers/md/dm-cache-target.c +28 -27
@@ -339,8 +339,6 @@ struct cache {
 	struct list_head invalidation_requests;
 
 	sector_t migration_threshold;
-	wait_queue_head_t migration_wait;
-	atomic_t nr_allocated_migrations;
 
 	/*
 	 * The number of in flight migrations that are performing
@@ -396,7 +394,11 @@ struct cache {
 	bool loaded_mappings:1;
 	bool loaded_discards:1;
 
-	struct rw_semaphore background_work_lock;
+	/* background work management */
+	bool background_work_allowed;
+	unsigned background_work_nr;
+	spinlock_t background_work_lock;
+	wait_queue_head_t background_work_wait;
 
 	struct batcher committer;
 	struct work_struct commit_ws;
@@ -487,19 +489,13 @@ static struct dm_cache_migration *alloc_
 	memset(mg, 0, sizeof(*mg));
 
 	mg->cache = cache;
-	atomic_inc(&cache->nr_allocated_migrations);
 
 	return mg;
 }
 
 static void free_migration(struct dm_cache_migration *mg)
 {
-	struct cache *cache = mg->cache;
-
-	if (atomic_dec_and_test(&cache->nr_allocated_migrations))
-		wake_up(&cache->migration_wait);
-
-	mempool_free(mg, &cache->migration_pool);
+	mempool_free(mg, &mg->cache->migration_pool);
 }
 
 /*----------------------------------------------------------------*/
@@ -1029,34 +1025,39 @@ static void calc_discard_block_range(str
 
 static void prevent_background_work(struct cache *cache)
 {
-	lockdep_off();
-	down_write(&cache->background_work_lock);
-	lockdep_on();
+	spin_lock_irq(&cache->background_work_lock);
+	cache->background_work_allowed = false;
+	wait_event_lock_irq(cache->background_work_wait,
+			    cache->background_work_nr == 0,
+			    cache->background_work_lock);
+	spin_unlock_irq(&cache->background_work_lock);
 }
 
 static void allow_background_work(struct cache *cache)
 {
-	lockdep_off();
-	up_write(&cache->background_work_lock);
-	lockdep_on();
+	spin_lock_irq(&cache->background_work_lock);
+	cache->background_work_allowed = true;
+	spin_unlock_irq(&cache->background_work_lock);
 }
 
 static bool background_work_begin(struct cache *cache)
 {
 	bool r;
 
-	lockdep_off();
-	r = down_read_trylock(&cache->background_work_lock);
-	lockdep_on();
-
+	spin_lock_irq(&cache->background_work_lock);
+	r = cache->background_work_allowed;
+	if (r)
+		cache->background_work_nr++;
+	spin_unlock_irq(&cache->background_work_lock);
 	return r;
 }
 
 static void background_work_end(struct cache *cache)
 {
-	lockdep_off();
-	up_read(&cache->background_work_lock);
-	lockdep_on();
+	spin_lock_irq(&cache->background_work_lock);
+	if (--cache->background_work_nr == 0)
+		wake_up(&cache->background_work_wait);
+	spin_unlock_irq(&cache->background_work_lock);
 }
 
 /*----------------------------------------------------------------*/
@@ -2515,9 +2516,7 @@ static int cache_create(struct cache_arg
 
 	spin_lock_init(&cache->lock);
 	bio_list_init(&cache->deferred_bios);
-	atomic_set(&cache->nr_allocated_migrations, 0);
 	atomic_set(&cache->nr_io_migrations, 0);
-	init_waitqueue_head(&cache->migration_wait);
 
 	r = -ENOMEM;
 	atomic_set(&cache->nr_dirty, 0);
@@ -2599,8 +2598,10 @@ static int cache_create(struct cache_arg
 		     issue_op, cache, cache->wq);
 	dm_iot_init(&cache->tracker);
 
-	init_rwsem(&cache->background_work_lock);
-	prevent_background_work(cache);
+	init_waitqueue_head(&cache->background_work_wait);
+	spin_lock_init(&cache->background_work_lock);
+	cache->background_work_allowed = false;
+	cache->background_work_nr = 0;
 
 	*result = cache;
 	return 0;
author maintainer
#101 lore raw

PATCH 101/247 dm-crypt: fix a tiny race condition in crypt_dec_pending

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ben Cressey <[email protected]>

commit 148845aa1921d95ef5dc851c76e6a284f6657df6 upstream.

crypt_dec_pending reads io->error before calling atomic_dec_and_test. Another context, for example crypt_endio called from an interrupt, may set io->error and drop its reference between the read and the decrement. crypt_dec_pending then drops the last reference and completes the bio with the stale status - so a read that failed and was never decrypted, or a write that failed, is reported as successful.

The read was placed before the decrement by commit b35f8caa0890 ("dm
crypt: wait for endio to complete before destruction"), because that commit freed dm_crypt_io before calling bio_endio. This is no longer the case, dm_crypt_io lives in the per-bio data now.

Read io->error after atomic_dec_and_test instead. atomic_dec_and_test is fully ordered, so no additional barrier is needed.

Fixes b35f8caa0890 ("dm crypt: wait for endio to complete before destruction")
Assisted-by Claude:unspecified
---
 drivers/md/dm-crypt.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/md/dm-crypt.c +1 -2
@@ -1837,7 +1837,6 @@ static void crypt_dec_pending(struct dm_
 {
 	struct crypt_config *cc = io->cc;
 	struct bio *base_bio = io->base_bio;
-	blk_status_t error = io->error;
 
 	if (!atomic_dec_and_test(&io->io_pending))
 		return;
@@ -1859,7 +1858,7 @@ static void crypt_dec_pending(struct dm_
 	else
 		kfree(io->integrity_metadata);
 
-	base_bio->bi_status = error;
+	base_bio->bi_status = io->error;
 
 	bio_endio(base_bio);
 }
author maintainer
#102 lore raw

PATCH 102/247 dm-ebs: fix incorrect device offset check in ebs_ctr()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Genjian Zhang <[email protected]>

commit 7ac81e2d2240f2c57bd073b0733e0b2abca38e82 upstream.

<offset> is a backing-device sector offset; ti->len is the virtual target length. Comparing them rejects valid tables, e.g.:

dmsetup create ebs0 --table "0 1048576 ebs /dev/sda 2097152 1 8" -> ebs: Invalid device offset sector (-EINVAL)

Drop the check. Bounds against the backing device are already enforced later by device_area_is_invalid() via ebs_iterate_devices().

Fixes d3c7b35c20d6 ("dm: add emulated block size target")
---
 drivers/md/dm-ebs-target.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/md/dm-ebs-target.c +1 -2
@@ -265,8 +265,7 @@ static int ebs_ctr(struct dm_target *ti,
 
 	r = -EINVAL;
 	if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1 ||
-	    tmp != (sector_t)tmp ||
-	    (sector_t)tmp >= ti->len) {
+	    tmp != (sector_t)tmp) {
 		ti->error = "Invalid device offset sector";
 		goto bad;
 	}
author maintainer
#103 lore raw

PATCH 103/247 dm-integrity: fix infinite loop on discard with large tag size

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ben Cressey <[email protected]>

commit 18d80c77b4c7dd20699e81cedfbbff4e9d198f28 upstream.

When integrity_metadata handles a discard, it fills a buffer with DISCARD_FILLER and writes it over the tags, max_blocks blocks at a time. If the kmalloc fails, the buffer is the on-stack array checksums_onstack and max_size is set to HASH_MAX_DIGESTSIZE. So if the tag size is larger than HASH_MAX_DIGESTSIZE, max_blocks is zero, bi_size is never decremented and the loop never terminates.

Fix this by using sizeof(checksums_onstack) as max_size. The array has MAX_TAG_SIZE bytes since commit b93b6643e9b5 ("dm integrity: fix a crash with unusually large tag size"), so max_blocks is at least 1.

Fixes 84597a44a9d8 ("dm integrity: add optional discard support")
Assisted-by Claude:unspecified
---
 drivers/md/dm-integrity.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/md/dm-integrity.c +1 -1
@@ -1856,7 +1856,7 @@ static void integrity_metadata(struct wo
 
 		if (unlikely(dio->op == REQ_OP_DISCARD)) {
 			unsigned int bi_size = dio->bio_details.bi_iter.bi_size;
-			unsigned int max_size = likely(checksums != checksums_onstack) ? PAGE_SIZE : HASH_MAX_DIGESTSIZE;
+			unsigned int max_size = likely(checksums != checksums_onstack) ? PAGE_SIZE : sizeof(checksums_onstack);
 			unsigned int max_blocks = (max_size - extra_space) / ic->tag_size;
 			sector_t sector = dio->range.logical_sector;
 
author maintainer
#104 lore raw

PATCH 104/247 dm-integrity: require stable writes for internal hash modes

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Chen Cheng <[email protected]>

commit b2fd92f016e9d692fd3c8c08d0ee014e9212279d upstream.

dm-integrity direct, bitmap and inline internal-hash modes compute integrity tags from the pages carried by the write bio. The lower data write also uses those pages, so the tag and the data write depend on the same memory contents staying unchanged while writeback is in flight.

Without stable writes, a buffered writer can modify a writeback folio after dm-integrity has submitted the data bio and before the lower device has consumed the data. After a crash, this can leave data from the later contents with a tag calculated from the earlier contents, causing permanent checksum failures on read.

Set BLK_FEAT_STABLE_WRITES for internal-hash D, B and I modes so filesystems wait for writeback folios to become stable before modifying them again. Journal mode is left unchanged because it copies data into the journal before computing and persisting the tag.

Tested using dm-delay over a virtio-blk test disk, dm-integrity internal_hash:crc32c and no-journal ext4. The D and B reproducers both failed with checksum errors before this change and completed with READ_RC=0 and zero mismatches after it.

---
 drivers/md/dm-integrity.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/md/dm-integrity.c +4 -0
@@ -3979,6 +3979,10 @@ static void dm_integrity_io_hints(struct
 		limits->discard_granularity = ic->sectors_per_block << SECTOR_SHIFT;
 	}
 
+	if (ic->internal_hash &&
+	    (ic->mode == 'D' || ic->mode == 'B' || ic->mode == 'I'))
+		limits->features |= BLK_FEAT_STABLE_WRITES;
+
 	if (!ic->internal_hash) {
 		struct blk_integrity *bi = &limits->integrity;
 
author maintainer
#105 lore raw

PATCH 105/247 mac802154: fix data race and NULL deref on local->assoc_dev

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kaiwen Shi <[email protected]>

commit c037915f80c4db47f7d061d68e703ffd551b1a34 upstream.

local->assoc_dev is shared between the association path and the association-response worker without common synchronization.

mac802154_perform_association() stores the coordinator pointer and waits for a response. Its timeout and error paths clear the pointer and return to mac802154_associate(), which may then free the coordinator object. Meanwhile, mac802154_rx_mac_cmd_worker() may observe the associating bit and enter mac802154_process_association_resp(), which dereferences assoc_dev.

The worker's bit test and the handler's pointer dereference are not atomic with respect to cleanup. Cleanup can clear assoc_dev between them, causing a NULL dereference, or free the coordinator while the response handler still uses the pointer.

The recorded result is exposed to the same window. assoc_status and assoc_addr are written by the handler but read by the association path while the associating bit is still set, so a second response for the same request - a malicious one, for instance - can replace them between those reads and leave the caller with an incoherent status and address pair.

The response handler only needs the coordinator extended address. Replace assoc_dev with a cached address, removing the pointer lifetime dependency. Protect the cached address and the associating bit with a dedicated spinlock. A READ_ONCE()/WRITE_ONCE() pair would not guarantee an atomic __le64 access on all 32-bit architectures.

wpan_dev->association_lock cannot be reused here: nl802154_associate() holds it across rdev_associate(), hence for the whole of mac802154_perform_association() including the wait for the response. A response handler taking that lock would only get it once the association has already given up.

Reset the completion, publish the cached address, and set the associating bit while holding the lock. The response handler takes the lock, rechecks the bit and the cached address, records the response, clears the bit, and only then completes the waiter. Thus cleanup cannot pass the handler between its state check and completion, and the cached 64-bit value cannot tear.

The handler clears the bit before completing, not the woken waiter:
otherwise complete() is issued under the lock and a second (e.g. malicious) response can reacquire it before the waiter and replace the result. So a wait that returns success implies the bit is already clear, and the success and negative-response paths return directly. The transmit-error and timeout paths still clear it under assoc_lock, which serializes any racing response against the cleanup while the call returns the error it already selected. Both paths snapshot assoc_status and assoc_addr under the same lock.

Both users run in process context, so a plain spinlock is sufficient. The lock is not held while waiting for the completion.

---
 net/mac802154/ieee802154_i.h |    7 +++++
 net/mac802154/main.c         |    1 
 net/mac802154/scan.c         |   51 ++++++++++++++++++++++++++++++++-----------
 3 files changed, 45 insertions(+), 14 deletions(-)
net/mac802154/ieee802154_i.h +6 -1
@@ -80,7 +80,12 @@ struct ieee802154_local {
 	spinlock_t rx_lock;
 
 	/* Association */
-	struct ieee802154_pan_device *assoc_dev;
+	/* assoc_lock protects assoc_dev_extended_addr, assoc_addr,
+	 * assoc_status, the assoc_done reinit/complete pairing and the
+	 * IEEE802154_IS_ASSOCIATING bit in @ongoing.
+	 */
+	spinlock_t assoc_lock;
+	__le64 assoc_dev_extended_addr;
 	struct completion assoc_done;
 	__le16 assoc_addr;
 	u8 assoc_status;
net/mac802154/main.c +1 -0
@@ -105,6 +105,7 @@ ieee802154_alloc_hw(size_t priv_data_len
 	INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
 
 	init_completion(&local->assoc_done);
+	spin_lock_init(&local->assoc_lock);
 
 	/* init supported flags with 802.15.4 default ranges */
 	phy->supported.max_minbe = 8;
net/mac802154/scan.c +38 -13

98 lines not on this page · raw message

author maintainer
#106 lore raw

PATCH 106/247 net/iucv: fix the recvmsg window update

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryam Vargas <[email protected]>

commit fa5acd038ea657ad5033713d6916214cbd349151 upstream.

iucv_sock_recvmsg() sends the HiperSockets-only AF_IUCV_FLAG_WIN without testing the transport, so on a classic z/VM socket iucv_send_ctrl() sizes the skb through a NULL iucv->hs_dev. SO_MSGLIMIT accepts 1, so msglimit / 2 is zero and one recvmsg() on its own socket is enough for an unprivileged process to take a spurious disconnect.

It also calls iucv_send_ctrl() under spin_lock_bh(&message_q.lock), which allocates GFP_KERNEL inside a section the code treats as atomic. Sending outside that lock lets two recvmsg() reach afiucv_hs_send() at once, where msg_recv is sampled for the advertised window and subtracted after dev_queue_xmit() -- and sendmsg reaches that counter under lock_sock() while recvmsg holds no socket lock, so both can subtract the same value, the counter goes negative and the credit reaches the peer twice.

Test the transport, claim the credit with atomic_xchg() after the last error exit and hand it back if the transmit fails, and send once the lock is dropped.

Fixes 3881ac441f64 ("af_iucv: add HiperSockets transport")
Fixes 238965b71b96 ("net/af_iucv: build proper skbs for HiperTransport")
---
 net/iucv/af_iucv.c |   42 +++++++++++++++++++++++++-----------------
 1 file changed, 25 insertions(+), 17 deletions(-)
net/iucv/af_iucv.c +25 -17
@@ -207,12 +207,6 @@ static int afiucv_hs_send(struct iucv_me
 	phs_hdr->flags = flags;
 	if (flags == AF_IUCV_FLAG_SYN)
 		phs_hdr->window = iucv->msglimit;
-	else if ((flags == AF_IUCV_FLAG_WIN) || !flags) {
-		confirm_recv = atomic_read(&iucv->msg_recv);
-		phs_hdr->window = confirm_recv;
-		if (confirm_recv)
-			phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN;
-	}
 	memcpy(phs_hdr->destUserID, iucv->dst_user_id, 8);
 	memcpy(phs_hdr->destAppName, iucv->dst_name, 8);
 	memcpy(phs_hdr->srcUserID, iucv->src_user_id, 8);
@@ -247,13 +241,22 @@ static int afiucv_hs_send(struct iucv_me
 	}
 	skb->protocol = cpu_to_be16(ETH_P_AF_IUCV);
 
+	/* Claim the receive credit here, not while building the header: every
+	 * way this frame can be dropped has now been ruled out, so the window
+	 * is zeroed only for as long as the transmit itself takes.
+	 */
+	if (flags == AF_IUCV_FLAG_WIN || !flags) {
+		confirm_recv = atomic_xchg(&iucv->msg_recv, 0);
+		phs_hdr->window = confirm_recv;
+		if (confirm_recv)
+			phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN;
+	}
+
 	atomic_inc(&iucv->skbs_in_xmit);
 	err = dev_queue_xmit(skb);
 	if (net_xmit_eval(err)) {
 		atomic_dec(&iucv->skbs_in_xmit);
-	} else {
-		atomic_sub(confirm_recv, &iucv->msg_recv);
-		WARN_ON(atomic_read(&iucv->msg_recv) < 0);
+		atomic_add(confirm_recv, &iucv->msg_recv);
 	}
 	return net_xmit_eval(err);
 
@@ -1237,6 +1240,7 @@ static int iucv_sock_recvmsg(struct sock
 	struct iucv_sock *iucv = iucv_sk(sk);
 	unsigned int copied, rlen;
 	struct sk_buff *skb, *rskb, *cskb;
+	bool send_win = false;
 	int err = 0;
 	u32 offset;
 
@@ -1327,16 +1331,20 @@ static int iucv_sock_recvmsg(struct sock
 		if (skb_queue_empty(&iucv->backlog_skb_q)) {
 			if (!list_empty(&iucv->message_q.list))
 				iucv_process_message_q(sk);
-			if (atomic_read(&iucv->msg_recv) >=
-							iucv->msglimit / 2) {
-				err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
-				if (err) {
-					sk->sk_state = IUCV_DISCONN;
-					sk->sk_state_change(sk);
-				}
-			}
+			if (iucv->transport == AF_IUCV_TRANS_HIPER &&
+			    atomic_read(&iucv->msg_recv) >=
+							iucv->msglimit / 2)
+				send_win = true;
 		}
 		spin_unlock_bh(&iucv->message_q.lock);
+
+		if (send_win) {
+			err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
+			if (err) {
+				sk->sk_state = IUCV_DISCONN;
+				sk->sk_state_change(sk);
+			}
+		}
 	}
 
 done:
author maintainer
#107 lore raw

PATCH 107/247 xfs: fix capability check in xfs

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Carlos Maiolino <[email protected]>

commit e2f62a9744ebad3bcb6347a648e615026e9efeff upstream.

An user reported a bug where he managed to evade group's quota by changing a file's gid to a different group id the same user belonged to, even though quotas were enforced on both gids and the file's size was big enough to exceed the quota's hardlimit.

Commit eba0549bc7d1 replaced a capable() call by a has_capability_noaudit() to prevent unnecessary selinux audit messages. Turns out that both calls have slightly different semantics even though their documentation seems similar. Where in a nutshell:

capable() - Tests the task's effective credentials has_ns_capability_noaudit() - Tests the task's real credentials

This most of the time has no practical difference but in some cases like changing attrs (specifically group id in this case) through a NFS client this will allow the quota code to use XFS_QMOPT_FORCE_RES, effectively bypassing quota accounting checks.

Using instead ns_capable_noaudit() should fix this issue and prevent selinux audit messages.

This also fix the remaining calls to has_capability_noaudit()

Fixes eba0549bc7d1 ("xfs: don't generate selinux audit messages for capability testing")
---
 fs/xfs/xfs_fsmap.c |    2 +-
 fs/xfs/xfs_ioctl.c |    2 +-
 fs/xfs/xfs_iops.c  |    2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)
fs/xfs/xfs_fsmap.c +1 -1
@@ -916,7 +916,7 @@ xfs_getfsmap(
 		return -EINVAL;
 
 	use_rmap = xfs_has_rmapbt(mp) &&
-		   has_capability_noaudit(current, CAP_SYS_ADMIN);
+		   ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
 	head->fmh_entries = 0;
 
 	/* Set up our device handlers. */
fs/xfs/xfs_ioctl.c +1 -1
@@ -573,7 +573,7 @@ xfs_ioctl_setattr_get_trans(
 		goto out_error;
 
 	error = xfs_trans_alloc_ichange(ip, NULL, NULL, pdqp,
-			has_capability_noaudit(current, CAP_FOWNER), &tp);
+			ns_capable_noaudit(&init_user_ns, CAP_FOWNER), &tp);
 	if (error)
 		goto out_error;
 
fs/xfs/xfs_iops.c +1 -1
@@ -735,7 +735,7 @@ xfs_setattr_nonsize(
 	}
 
 	error = xfs_trans_alloc_ichange(ip, udqp, gdqp, NULL,
-			has_capability_noaudit(current, CAP_FOWNER), &tp);
+			ns_capable_noaudit(&init_user_ns, CAP_FOWNER), &tp);
 	if (error)
 		goto out_dqrele;
 
author maintainer
#108 lore raw

PATCH 108/247 workqueue: Use raise_softirq() to trigger softirq in irq_work handler

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zqiang <[email protected]>

commit 7e2f2a377ac9f50296ad60bd331f0d4def7aee51 upstream.

bh_pool_kick_normal() and bh_pool_kick_highpri() are registered via init_irq_work() without the IRQ_WORK_HARD_IRQ flag. On PREEMPT_RT, such irq_work items are processed by the per-CPU irq_workd kthread in preemptible task context with IRQs enabled. However, raise_softirq_irqoff() requires IRQs to be disabled. Calling it from irq_workd trips the lockdep assertion in __raise_softirq_irqoff() and the non-atomic update of the softirq pending mask can lose bits raised by an interrupt on the same CPU. Replace raise_softirq_irqoff() with raise_softirq() in the irq_work handlers.

Fixes 2f34d7337d98 ("workqueue: Fix queue_work_on() with BH workqueues")
---
 kernel/workqueue.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/workqueue.c +2 -2
@@ -7738,12 +7738,12 @@ static inline void wq_watchdog_init(void
 
 static void bh_pool_kick_normal(struct irq_work *irq_work)
 {
-	raise_softirq_irqoff(TASKLET_SOFTIRQ);
+	raise_softirq(TASKLET_SOFTIRQ);
 }
 
 static void bh_pool_kick_highpri(struct irq_work *irq_work)
 {
-	raise_softirq_irqoff(HI_SOFTIRQ);
+	raise_softirq(HI_SOFTIRQ);
 }
 
 static void __init restrict_unbound_cpumask(const char *name, const struct cpumask *mask)
author maintainer
#109 lore raw

PATCH 109/247 tipc: protect node reset trace dump with node lock

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Chengfeng Ye <[email protected]>

commit dddf197f29ba5e47a476dde82bf542ca2e0d5e5e upstream.

The tipc_node_reset_links trace event asks tipc_node_dump() to walk the node's link entries. Unlike the other node events that request link data, this event runs without the node lock.

This permits bearer teardown to free a link while the trace callback is dumping it:

CPU 0 CPU 1
trace_tipc_node_reset_links()

    tipc_node_dump()
      l = n->links[0].link
                                       tipc_node_write_lock()
                                       kfree(l)
                                       n->links[0].link = NULL
                                       tipc_node_write_unlock()
      tipc_link_dump(l)

tipc_link_dump() then dereferences the stale pointer. KASAN reported:

  BUG: KASAN: slab-use-after-free in tipc_link_dump
  Read of size 4 by task poc/115
  Call Trace:
   tipc_link_dump+0x10cb/0x16b0
   tipc_node_dump+0x4bb/0x740
   trace_event_raw_event_tipc_node_class+0x258/0x360
   tipc_node_reset_links+0x14d/0x1a0
   tipc_rcv+0x13f5/0x3030
   tipc_udp_recv+0x4e3/0x670
  Allocated by task 0:
   tipc_link_create+0x1e1/0x1020
   tipc_node_check_dest+0x7d2/0x11a0
   tipc_disc_rcv+0xdbf/0x1430
  Freed by task 89:
   kfree+0x131/0x3c0
   tipc_node_link_down+0x267/0x4b0
   tipc_node_delete_links+0xec/0x160
   bearer_disable+0x107/0x260

Take the node write lock around the trace event. This serializes the dump against tipc_node_link_down(delete=true), which frees the link under the same write lock.

---
 net/tipc/node.c |    2 ++
 1 file changed, 2 insertions(+)
net/tipc/node.c +2 -0
@@ -1333,7 +1333,9 @@ static void tipc_node_reset_links(struct
 
 	pr_warn("Resetting all links to %x\n", n->addr);
 
+	tipc_node_write_lock(n);
 	trace_tipc_node_reset_links(n, true, " ");
+	tipc_node_write_unlock_fast(n);
 	for (i = 0; i < MAX_BEARERS; i++) {
 		tipc_node_link_down(n, i, false);
 	}
author maintainer
#110 lore raw

PATCH 110/247 sctp: validate chunk length in the inqueue parser

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Charles Vosburgh <[email protected]>

commit 6cfc1b90cb86f4aabc69fb8e30128e07e2cdfa3a upstream.

SCTP chunks always include a four-byte generic header, but sctp_inq_pop() currently accepts shorter declared lengths. A zero-length chunk leaves chunk_end at the current header.

When ASCONF is covered by the association's SCTP-AUTH policy, sctp_assoc_bh_rcv() can continue before the state machine performs its normal chunk-length check. sctp_inq_pop() then returns the same malformed chunk repeatedly and the receive softirq can lock up.

A remote SCTP peer can trigger this after establishing an association on a kernel built with CONFIG_IP_SCTP and configured with net.sctp.addip_enable=1 and net.sctp.auth_enable=1. The reproducer did not require application credentials, a shared SCTP AUTH key, or net.sctp.addip_noauth_enable=1.

On commit f967455fb2a5 ("seg6: reset IP6CB after IPv6 decapsulation"), one zero-length ASCONF caused repeated
watchdog soft-lockup reports in a two-vCPU KVM guest. All 3 pre-trigger health probes succeeded, while 36 of 37 post-trigger probes failed. With this change, all 37 post-trigger probes succeeded and no equivalent soft-lockup signature appeared.

Reject chunks shorter than the generic SCTP header at the shared inqueue parser boundary. Mark the packet for discard before either caller can continue processing it, while preserving the four-byte generic minimum. Declared-length 1 through 4 controls and kernel-generated ASCONF traffic remained healthy. The patched sctp_hello selftest passed for IPv4 and IPv6.

The complete private reproducer and validation evidence are available directly to maintainers on request.

Fixes bbd0d59809f9 ("[SCTP]: Implement the receive and verification of AUTH chunk")
---
 net/sctp/inqueue.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
net/sctp/inqueue.c +4 -2
@@ -212,8 +212,10 @@ new_skb:
 	chunk->chunk_end = ((__u8 *)ch) + SCTP_PAD4(ntohs(ch->length));
 	skb_pull(chunk->skb, sizeof(*ch));
 	chunk->subh.v = NULL; /* Subheader is no longer valid.  */
-
-	if (chunk->chunk_end + sizeof(*ch) <= skb_tail_pointer(chunk->skb)) {
+	if (unlikely(ntohs(ch->length) < sizeof(*ch))) {
+		chunk->pdiscard = 1;
+	} else if (chunk->chunk_end + sizeof(*ch) <=
+		   skb_tail_pointer(chunk->skb)) {
 		/* This is not a singleton */
 		chunk->singleton = 0;
 	} else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) {
author maintainer
#111 lore raw

PATCH 111/247 page_pool: keep frag_offset aligned for odd-sized requests

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Florian Schauer <[email protected]>

commit dc0df5a0c62ccea1d0e08d39a4dc9064de81d411 upstream.

page_pool_alloc_frag_netmem() rounds the requested fragment size with

	size = ALIGN(size, dma_get_cache_alignment());

dma_get_cache_alignment() returns 1 unless the architecture defines ARCH_DMA_MINALIGN, which DMA-coherent architectures such as x86 do not. There the ALIGN() is a no-op and pool->frag_offset advances by the raw, unrounded size.

A single caller asking for an odd size then leaves frag_offset misaligned for every fragment carved out of that page afterwards. The pool is shared, so the damage is not confined to the caller that caused it.

The per-cpu system_page_pool used by generic XDP hits this. skb_pp_cow_data() allocates its fragments with the raw packet length:

	size = min_t(u32, len, PAGE_SIZE);
	truesize = size;
	page = page_pool_dev_alloc(pool, &page_off, &truesize);

leaving frag_offset odd for whatever is carved out of that page next. Its own head allocation is already aligned -- SKB_HEAD_ALIGN(size) plus the XDP_PACKET_HEADROOM its callers pass -- so it is a later user of the shared pool that pays: page_pool_dev_alloc_va() returns a misaligned buffer, napi_build_skb() installs it as skb->head, and skb_shinfo(skb) == skb->head + skb->end is misaligned with it.

skb_shinfo()->dataref is a 4-byte atomic_t at offset 0x20, so the atomic_inc() in __skb_clone() straddles a cache line. On x86 with split lock detection -- fatal for kernel split locks by default -- this panics the machine:

  Oops: Split lock detected
  RIP: 0010:skb_clone+0x154/0x1e0
  Call Trace:
   <IRQ>
   raw_local_deliver+0x1ed/0x2c0
   ip_protocol_deliver_rcu+0x54/0x1c0
   ip_local_deliver_finish+0x85/0x100
   ip_local_deliver+0x67/0x100
   __netif_receive_skb_one_core+0x85/0xa0
   process_backlog+0x87/0x130

Reproduced by attaching any generic-mode XDP program to loopback and opening a RAW IPPROTO_UDP socket, which makes raw_local_deliver() clone every locally delivered UDP packet; ordinary DNS traffic then triggers it, roughly once per 2500 clones. Observed on 6.12.101 and 7.1.8.

Tracing page_pool_alloc_frag_netmem() over one such run shows the amplification -- two odd-sized requests, nine misaligned offsets:

  requested size & 7:   0: 17035    5: 1    7: 1
  frag_offset & 7:      0: 17028    3: 1    4: 1    5: 1    6: 1    7: 5

and skb_pp_cow_data() returning heads that were aligned on entry:

head 0xffff8f4c86aeac00 -> 0xffff8f4c53a9a9c4 (&7=4) head 0xffff8f4d6a8a42c0 -> 0xffff8f4c4f7b7a45 (&7=5)

Round the fragment size up to at least the alignment struct skb_shared_info requires, so fragments are always suitably aligned for the objects callers build on them. Architectures needing a larger DMA alignment keep it.

This also makes the remainder computed in page_pool_alloc_netmem(),

	*size = max_size - *offset;

aligned, since max_size is a power of two -- which fixes the matching misalignment of skb->end.

Verified with a controlled A/B under QEMU/KVM: same tree, same config, same compiler, same rootfs and identical traffic, differing only by this patch. A SEC("xdp.frags") XDP_PASS program on lo plus UDP datagrams larger than max_head_size drives skb_pp_cow_data()'s fragment loop, which passes raw packet lengths to the pool. Measured at the return of skb_pp_cow_data():

unpatched patched
skb_pp_cow_data calls 40800 40800
misaligned skb->head 1120 0
dataref at line offset >60 80 0

The last row counts the accesses that actually fault:
skb_shinfo()->dataref sits at head+end+0x20 and is a 4-byte atomic, so lock incl splits a 64-byte cache line only when that address lands at offset 61..63. All 80 occurrences were at offset 61; the panic reported above was at offset 62. Eliminating the misalignment removes every one of them.

Same class of bug as commit 3bed3cc4156e ("net: Do not allocate page fragments that are not skb aligned"), which fixed the older netdev_alloc_frag()/napi_alloc_frag() allocators.

---
 net/core/page_pool.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
net/core/page_pool.c +2 -1
@@ -1040,7 +1040,8 @@ netmem_ref page_pool_alloc_frag_netmem(s
 	if (WARN_ON(size > max_size))
 		return 0;
 
-	size = ALIGN(size, dma_get_cache_alignment());
+	size = ALIGN(size, max_t(unsigned int, dma_get_cache_alignment(),
+				 __alignof__(struct skb_shared_info)));
 	*offset = pool->frag_offset;
 
 	if (netmem && *offset + size > max_size) {
author maintainer
#112 lore raw

PATCH 112/247 s390/ctcm: Prevent XID null dereference

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aswin Karuvally <[email protected]>

commit b264d8422779d69febce914efc47a92a85cc382c upstream.

The mpc_validate_xid() function sets grp->saved_xid2->xid2_flag2 to 0x40 to signal XID validation error. If peer XID is NULL or r/w channel pairing mismatch happens, grp->saved_xid2 is never initialized. An attempt to set the flag in such case leads to NULL dereference.

Fix this by using the always available priv->xid->xid2_flag2 instead of grp->saved_xid2->xid2_flag2 for validation errors.

---
 drivers/s390/net/ctcm_mpc.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/s390/net/ctcm_mpc.c +1 -2
@@ -826,7 +826,7 @@ static void mpc_action_go_ready(fsm_inst
 
 	fsm_deltimer(&grp->timer);
 
-	if (grp->saved_xid2->xid2_flag2 == 0x40) {
+	if (priv->xid->xid2_flag2 == 0x40) {
 		priv->xid->xid2_flag2 = 0x00;
 		if (grp->estconnfunc) {
 			grp->estconnfunc(grp->port_num, 1,
@@ -1636,7 +1636,6 @@ done:
 			"The XID used in the MPC protocol is not valid, "
 			"rc = %d\n", rc);
 		priv->xid->xid2_flag2 = 0x40;
-		grp->saved_xid2->xid2_flag2 = 0x40;
 	}
 
 	return rc;
author maintainer
#113 lore raw

PATCH 113/247 s390/pci: Fix leak of uninitialized kernel data in SCLP report

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Niklas Schnelle <[email protected]>

commit 8ac60ae2a307a50b599bf5d300b448d638f3ba29 upstream.

While report_error_write() checks that the provided buffer is at least as large as the header struct, but not that it is large enough to contain the report with the length claimed by report->length. If user-space provides a short buffer, meaning a larger report->length than the actually written payload, up to around 4K of kernel data from past the kmalloc(len + 1) sized buffer allocated in kernfs_fop_write_iter() will leak into the SCLP report.

However, as the entity processing the SCLP is privileged and able to access at least the page including the report, this does not leak data that entity could not access but it is still an out of bounds read and a malformed error report that should be rejected.

---
 arch/s390/pci/pci_sysfs.c |    3 +++
 1 file changed, 3 insertions(+)
arch/s390/pci/pci_sysfs.c +3 -0
@@ -150,6 +150,9 @@ static ssize_t report_error_write(struct
 	if (off || (count < sizeof(*report)))
 		return -EINVAL;
 
+	if (count < (report->length + sizeof(*report)))
+		return -EINVAL;
+
 	ret = sclp_pci_report(report, zdev->fh, zdev->fid);
 
 	return ret ? ret : count;
author maintainer
#114 lore raw

PATCH 114/247 ipv4: avoid divide by zero in fib_rebalance

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zihan Xi <[email protected]>

commit 5046d2880fec7d49ebed2fd2866747ee1d06ad71 upstream.

fib_rebalance() computes the total eligible nexthop weight in one pass and programs upper bounds in a second pass. A concurrent change to ignore_routes_with_linkdown can make the first pass return zero while the second pass sees an eligible nexthop, resulting in division by zero.

If the first pass reports a zero total, set each nexthop upper bound to -1 and skip the division. This matches the IPv6 fix in commit d2c26c2911dd ("ipv6: avoid divide by zero in rt6_multipath_rebalance") and preserves the lock-free rebalance path.

---
 net/ipv4/fib_semantics.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/fib_semantics.c +1 -1
@@ -856,7 +856,7 @@ static void fib_rebalance(struct fib_inf
 	change_nexthops(fi) {
 		int upper_bound;
 
-		if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD) {
+		if (!total || nexthop_nh->fib_nh_flags & RTNH_F_DEAD) {
 			upper_bound = -1;
 		} else if (ip_ignore_linkdown(nexthop_nh->fib_nh_dev) &&
 			   nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN) {
author maintainer
#115 lore raw

PATCH 115/247 drm/amdgpu: Fix VCE 3 ring align_mask

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: David Rosca <[email protected]>

[ Upstream commit 2ee9836545e690c9e66ec203445d7705959fd7a8 ]

The largest frame is 20 dwords, so 0xf mask is too small. This was always wrong, but we were lucky with the VCE_CMD_END commands inserted after fence and vm_flush.

Fixes 8897ea8c761b ("drm/amdgpu: Implement insert_end for VCE 3")
---
 drivers/gpu/drm/amd/amdgpu/vce_v3_0.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/vce_v3_0.c +1 -1
index ff0b7438ac76f..4c7f6c2e1adee 100644
@@ -959,7 +959,7 @@ static const struct amdgpu_ring_funcs vce_v3_0_ring_phys_funcs = {
 
 static const struct amdgpu_ring_funcs vce_v3_0_ring_vm_funcs = {
 	.type = AMDGPU_RING_TYPE_VCE,
-	.align_mask = 0xf,
+	.align_mask = 0x1f,
 	.nop = VCE_CMD_NO_OP,
 	.support_64bit_ptrs = false,
 	.no_user_fence = true,
Signature · 1 line
2.53.0
author maintainer
#116 lore raw

PATCH 116/247 mm, madvise: fix potential workingset node list_lru leaks

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kairui Song <[email protected]>

[ Upstream commit 62e72d2cf702a5e2fb53d9c46ed900d9384e4a06 ]

Since commit 5abc1e37afa0 ("mm: list_lru: allocate list_lru_one only when needed"), all list_lru users need to allocate the items using the new infrastructure that provides list_lru info for slab allocation, ensuring that the corresponding memcg list_lru is allocated before use.

For workingset shadow nodes (which are xa_node), users are converted to use the new infrastructure by commit 9bbdc0f32409 ("xarray: use kmem_cache_alloc_lru to allocate xa_node"). The xas->xa_lru will be set correctly for filemap users. However, there is a missing case: xa_node allocations caused by madvise(..., MADV_COLLAPSE).

madvise(..., MADV_COLLAPSE) will also read in the absent parts of file map, and there will be xa_nodes allocated for the caller's memcg (assuming it's not rootcg). However, these allocations won't trigger memcg list_lru allocation because the proper xas info was not set.

If nothing else has allocated other xa_nodes for that memcg to trigger list_lru creation, and memory pressure starts to evict file pages, workingset_update_node will try to add these xa_nodes to their corresponding memcg list_lru, and it does not exist (NULL). So they will be added to rootcg's list_lru instead.

This shouldn't be a significant issue in practice, but it is indeed unexpected behavior, and these xa_nodes will not be reclaimed effectively. And may lead to incorrect counting of the list_lru->nr_items counter.

This problem wasn't exposed until recent commit 28e98022b31ef ("mm/list_lru: simplify reparenting and initial allocation") added a sanity check: only dying memcg could have a NULL list_lru when list_lru_{add,del} is called. This problem triggered this WARNING.

So make madvise(..., MADV_COLLAPSE) also call xas_set_lru() to pass the list_lru which we may want to insert xa_node into later. And move mapping_set_update to mm/internal.h, and turn into a macro to avoid including extra headers in mm/internal.h.

Fixes 9bbdc0f32409 ("xarray: use kmem_cache_alloc_lru to allocate xa_node")
Reported-by [email protected]
Cc Chengming Zhou <[email protected]>
Cc Johannes Weiner <[email protected]>
Cc Matthew Wilcox (Oracle) <[email protected]>
Cc Michal Hocko <[email protected]>
Cc Muchun Song <[email protected]>
Cc Qi Zheng <[email protected]>
Cc Roman Gushchin <[email protected]>
Cc Sasha Levin <[email protected]>
Cc Shakeel Butt <[email protected]>
Cc Yu Zhao <[email protected]>

Stable-dep-of: aca1f2d5de17 ("mm/huge_memory: initialise workingset state before folio split")

---
 mm/filemap.c    | 9 ---------
 mm/internal.h   | 6 ++++++
 mm/khugepaged.c | 3 +++
 3 files changed, 9 insertions(+), 9 deletions(-)
mm/filemap.c +0 -9
index c2287a1d1c6b7..64c2325b92389 100644
@@ -125,15 +125,6 @@
  *    ->private_lock		(zap_pte_range->block_dirty_folio)
  */
 
-static void mapping_set_update(struct xa_state *xas,
-		struct address_space *mapping)
-{
-	if (dax_mapping(mapping) || shmem_mapping(mapping))
-		return;
-	xas_set_update(xas, workingset_update_node);
-	xas_set_lru(xas, &shadow_nodes);
-}
-
 static void page_cache_delete(struct address_space *mapping,
 				   struct folio *folio, void *shadow)
 {
mm/internal.h +6 -0
index 9d277bc2c26e9..7aca909c54052 100644
@@ -1526,6 +1526,12 @@ static inline void shrinker_debugfs_remove(struct dentry *debugfs_entry,
 /* Only track the nodes of mappings with shadow entries */
 void workingset_update_node(struct xa_node *node);
 extern struct list_lru shadow_nodes;
+#define mapping_set_update(xas, mapping) do {			\
+	if (!dax_mapping(mapping) && !shmem_mapping(mapping)) {	\
+		xas_set_update(xas, workingset_update_node);	\
+		xas_set_lru(xas, &shadow_nodes);		\
+	}							\
+} while (0)
 
 /* mremap.c */
 unsigned long move_page_tables(struct vm_area_struct *vma,
mm/khugepaged.c +3 -0
index 84bacd7e19620..769e2f9f99920 100644
@@ -20,6 +20,7 @@
 #include <linux/rcupdate_wait.h>
 #include <linux/swapops.h>
 #include <linux/shmem_fs.h>
+#include <linux/dax.h>
 #include <linux/ksm.h>
 
 #include <asm/tlb.h>
@@ -1823,6 +1824,8 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr,
 	if (result != SCAN_SUCCEED)
 		goto out;
 
+	mapping_set_update(&xas, mapping);
+
 	__folio_set_locked(new_folio);
 	if (is_shmem)
 		__folio_set_swapbacked(new_folio);
Signature · 1 line
2.53.0
author maintainer
#117 lore raw

PATCH 117/247 mm/huge_memory: initialise workingset state before folio split

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Matt Fleming <[email protected]>

[ Upstream commit aca1f2d5de17e138bc6c4859126b77e516b82541 ]

xas_try_split() adds __GFP_ACCOUNT for page-cache xa_nodes, but __folio_split() leaves the xa_state's xa_lru unset. That lets a live, memcg-charged xa_node exist without being linked into the mapping's shadow_nodes list_lru; when reclaim later walks the list_lru it trips VM_WARN_ON(!css_is_dying()).

Use mapping_set_update() to install both the workingset update callback and the shadow_nodes list_lru on the xa_state.

Fixes 58729c04cf10 ("mm/huge_memory: add buddy allocator like (non-uniform) folio_split()")
Reported-by [email protected]
Cc Baolin Wang <[email protected]>
Cc Barry Song <[email protected]>
Cc Dave Chinner <[email protected]>
Cc Dev Jain <[email protected]>
Cc Kairui Song <[email protected]>
Cc Lance Yang <[email protected]>
Cc Liam Howlett <[email protected]>
Cc Lorenzo Stoakes <[email protected]>
Cc Matthew Wilcox (Oracle) <[email protected]>
Cc Muchun Song <[email protected]>
Cc Nico Pache <[email protected]>
Cc Roman Gushchin <[email protected]>
Cc Ryan Roberts <[email protected]>
Cc Shakeel Butt <[email protected]>
---
 mm/huge_memory.c | 2 ++
 1 file changed, 2 insertions(+)
mm/huge_memory.c +2 -0
index 348fb144e63be..f6ba19b57265b 100644
@@ -3532,6 +3532,8 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
 			goto out;
 		}
 
+		mapping_set_update(&xas, mapping);
+
 		xas_split_alloc(&xas, folio, folio_order(folio), gfp);
 		if (xas_error(&xas)) {
 			ret = xas_error(&xas);
Signature · 1 line
2.53.0
author maintainer
#118 lore raw

PATCH 118/247 Revert "s390/mm: Add missing secure storage access fixups for donated memory"

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

This reverts commit 74ccc099f1baa9236b8fb600406457a0870c83a9.

---
 arch/s390/mm/fault.c | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)
arch/s390/mm/fault.c +2 -9
index 2d03af60ed88f..f907e916aa20d 100644
@@ -443,17 +443,10 @@ void do_secure_storage_access(struct pt_regs *regs)
 		folio = phys_to_folio(addr);
 		if (unlikely(!folio_try_get(folio)))
 			return;
-		rc = uv_convert_from_secure(folio_to_phys(folio));
-		if (!rc)
-			clear_bit(PG_arch_1, &folio->flags.f);
+		rc = arch_make_folio_accessible(folio);
 		folio_put(folio);
-		/*
-		 * There are some valid fixup types for kernel
-		 * accesses to donated secure memory. zeropad is one
-		 * of them.
-		 */
 		if (rc)
-			return handle_fault_error_nolock(regs, 0);
+			BUG();
 	} else {
 		if (faulthandler_disabled())
 			return handle_fault_error_nolock(regs, 0);
Signature · 1 line
2.53.0
author maintainer
#119 lore raw

PATCH 119/247 netfilter: nft_byteorder: remove multi-register support

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Florian Westphal <[email protected]>

[ Upstream commit bb061d3de41707415269be75ebf700efb03ec212 ]

64bit byteorder conversion is broken when several registers need to be converted because the source register array advances in steps for 4 bytes instead of 8:

for (i = ...

      src64 = nft_reg_load64(&src[i]);
                             ~~~~~ u32 *src
      nft_reg_store64(&dst64[i],

Remove the multi-register support, it has other issues as well:

Pablo points out that commit
caf3ef7468f7 ("netfilter: nf_tables: prevent OOB access in nft_byteorder_eval") alters semantics: before the loop operated on registers, i.e.
for ( ... )
dst32[i] = htons((u16)src32[i])

.. but after the patch it will operate on bytes, which makes this useless to convert e.g. concatenations, which store each compound in its own register.

Multi-convert of u32 has one theoretical application:

ct mark . meta mark . tcp dport @intervalset

Because ct mark and meta mark are host byte order, use with intervals has to convert the byteorder for ct/meta mark value to network byte order (bigendian).

nftables emits this:
[ meta load mark => reg 1 ]
[ byteorder reg 1 = hton(reg 1, 4, 4) ]
[ ct load mark => reg 9 ]
[ byteorder reg 9 = hton(reg 9, 4, 4) ]
...

I.e. two separate calls. Theoretically it could be changed to do:
[ meta load mark => reg 1 ]
[ ct load mark => reg 9 ]
[ byteorder reg 1 = htonl(reg 1, 4, 8) ]
...

But then all it would take to change the set to
meta mark . tcp dport . ct mark

... and we'd be back to two "byteorder" calls. IOW, support to convert a range of registers is both dysfunctional and dubious.

Simplify this: remove the feature.

Pablo Neira Ayuso points out that nftables before 1.1.0 can generate incorrect byteorder conversions, see 9fe58952c45a, "evaluate: skip byteorder conversion for selector smaller than 2 bytes" in nftables.git). Affected rulesets fail to load with this change and old userspace due to 'len != size' check.

Fixes c301f0981fdd ("netfilter: nf_tables: fix pointer math issue in nft_byteorder_eval()")
Cc <[email protected]> # may break rule load with old nftables versions
---
 net/netfilter/nft_byteorder.c | 51 ++++++++++++++---------------------
 1 file changed, 20 insertions(+), 31 deletions(-)
net/netfilter/nft_byteorder.c +20 -31
index 74548b4c114b9..391aade36ef3c 100644
@@ -19,7 +19,6 @@ struct nft_byteorder {
 	u8			sreg;
 	u8			dreg;
 	enum nft_byteorder_ops	op:8;
-	u8			len;
 	u8			size;
 };
 
@@ -28,13 +27,8 @@ void nft_byteorder_eval(const struct nft_expr *expr,
 			const struct nft_pktinfo *pkt)
 {
 	const struct nft_byteorder *priv = nft_expr_priv(expr);
-	u32 *src = &regs->data[priv->sreg];
+	const u32 *src = &regs->data[priv->sreg];
 	u32 *dst = &regs->data[priv->dreg];
-	u16 *s16, *d16;
-	unsigned int i;
-
-	s16 = (void *)src;
-	d16 = (void *)dst;
 
 	switch (priv->size) {
 	case 8: {
@@ -43,18 +37,14 @@ void nft_byteorder_eval(const struct nft_expr *expr,
 
 		switch (priv->op) {
 		case NFT_BYTEORDER_NTOH:
-			for (i = 0; i < priv->len / 8; i++) {
-				src64 = nft_reg_load64(&src[i]);
-				nft_reg_store64(&dst64[i],
-						be64_to_cpu((__force __be64)src64));
-			}
+			src64 = nft_reg_load64(src);
+
+			nft_reg_store64(dst64, be64_to_cpu((__force __be64)src64));
 			break;
 		case NFT_BYTEORDER_HTON:
-			for (i = 0; i < priv->len / 8; i++) {
-				src64 = (__force __u64)
-					cpu_to_be64(nft_reg_load64(&src[i]));
-				nft_reg_store64(&dst64[i], src64);
-			}
+			src64 = (__force __u64)cpu_to_be64(nft_reg_load64(src));
+
+			nft_reg_store64(dst64, src64);
 			break;
 		}
 		break;
@@ -62,24 +52,20 @@ void nft_byteorder_eval(const struct nft_expr *expr,
 	case 4:
 		switch (priv->op) {
 		case NFT_BYTEORDER_NTOH:
-			for (i = 0; i < priv->len / 4; i++)
-				dst[i] = ntohl((__force __be32)src[i]);
+			*dst = ntohl((__force __be32)*src);
 			break;
 		case NFT_BYTEORDER_HTON:
-			for (i = 0; i < priv->len / 4; i++)
-				dst[i] = (__force __u32)htonl(src[i]);
+			*dst = (__force __u32)htonl(*src);
 			break;
 		}
 		break;
 	case 2:
 		switch (priv->op) {
 		case NFT_BYTEORDER_NTOH:
-			for (i = 0; i < priv->len / 2; i++)
-				d16[i] = ntohs((__force __be16)s16[i]);
+			nft_reg_store16(dst, ntohs(nft_reg_load_be16(src)));
 			break;
 		case NFT_BYTEORDER_HTON:
-			for (i = 0; i < priv->len / 2; i++)
-				d16[i] = (__force __u16)htons(s16[i]);
+			nft_reg_store_be16(dst, htons(nft_reg_load16(src)));
 			break;
 		}
 		break;
@@ -137,20 +123,22 @@ static int nft_byteorder_init(const struct nft_ctx *ctx,
 	if (err < 0)
 		return err;
 
-	priv->len = len;
+	/* no longer support multi-reg conversions */
+	if (len != size)
+		return -EOPNOTSUPP;
 
 	err = nft_parse_register_load(ctx, tb[NFTA_BYTEORDER_SREG], &priv->sreg,
-				      priv->len);
+				      len);
 	if (err < 0)
 		return err;
 
 	err = nft_parse_register_store(ctx, tb[NFTA_BYTEORDER_DREG],
 				       &priv->dreg, NULL, NFT_DATA_VALUE,
-				       priv->len);
+				       len);
 	if (err < 0)
 		return err;
 
-	if (nft_reg_overlap(priv->sreg, priv->dreg, priv->len))
+	if (nft_reg_overlap(priv->sreg, priv->dreg, len))
 		return -EINVAL;
 
 	return 0;
@@ -167,10 +155,11 @@ static int nft_byteorder_dump(struct sk_buff *skb,
 		goto nla_put_failure;
 	if (nla_put_be32(skb, NFTA_BYTEORDER_OP, htonl(priv->op)))
 		goto nla_put_failure;
-	if (nla_put_be32(skb, NFTA_BYTEORDER_LEN, htonl(priv->len)))
-		goto nla_put_failure;
 	if (nla_put_be32(skb, NFTA_BYTEORDER_SIZE, htonl(priv->size)))
 		goto nla_put_failure;
+	/* compatibility for old userspace which permitted size != len */
+	if (nla_put_be32(skb, NFTA_BYTEORDER_LEN, htonl(priv->size)))
+		goto nla_put_failure;
 	return 0;
 
 nla_put_failure:
Signature · 1 line
2.53.0
author maintainer
#120 lore raw

PATCH 120/247 mm/huge_memory: bypass THP tuneables for huge pfnmap mappings

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Lorenzo Stoakes (ARM) <[email protected]>

[ Upstream commit e384abeb559d10d6505aec053ede9368d81d4c71 ]

The sysfs THP tuneables at /sys/kernel/mm/transparent_huge_pages/ rather confusingly only control the behaviour of THP in some instances.

They are not applicable to MADV_COLLAPSE operations, nor to DAX mappings.

Long-term, THP is predicated upon compaction being able to obtain large folios to populate THP ranges.

However, vm_normal_folio() returns NULL for PFN map mappings, thus their reference count is maintained by the driver, not core mm.

As a consequence, the folios are not subject to reclaim nor compaction, so are not truly part of the THP mechanism at all.

However, since commit 5dd40721f147 ("mm: allow THP orders for PFNMAPs") introduced the ability to establish huge PFN maps, they have been subject to THP tuneables.

This is incorrect - if a huge PFN map is available (defined by vma->vm_ops->huge_fault being non-NULL for a VM_PFNMAP VMA), then it should be mapped huge upon fault-in.

Correct this by explicitly checking for this while ensuring that smaps continues to accurately report THPeligible statistics.

While here, abstract the entire file-backed THP check in vma_can_map_huge_file(), with sensible separation of logic into helper functions.

Failing to map huge PFN maps has resulted in significant real-world performance degradation, see links for details.

[ ljs - Converted to tva_flags, made file_thp_enabled() const in huge_mm.h
+ other smaller changes. Updated commit msg to reflect. ]

---
 include/linux/huge_mm.h |  2 +-
 mm/huge_memory.c        | 79 ++++++++++++++++++++++++++++++-----------
 2 files changed, 59 insertions(+), 22 deletions(-)
include/linux/huge_mm.h +1 -1
index bd34b70dd0cf9..10e41e8b16349 100644
@@ -254,7 +254,7 @@ static inline unsigned long thp_vma_suitable_orders(struct vm_area_struct *vma,
 	return orders;
 }
 
-static inline bool file_thp_enabled(struct vm_area_struct *vma)
+static inline bool file_thp_enabled(const struct vm_area_struct *vma)
 {
 	struct inode *inode;
 
mm/huge_memory.c +58 -21

91 lines not on this page · raw message

Signature · 1 line
2.53.0
author maintainer
#121 lore raw

PATCH 121/247 KVM: x86/mmu: Preserve nested TDP shadow page tables if they are used as roots

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Hyunwoo Kim <[email protected]>

[ Upstream commit 7a2c70e777a00c32ebafd376a6fe31ebb91c5b20 ]

kvm_mmu_zap_oldest_mmu_pages() excludes a shadow page whose root_count is non-zero from top-level reclaim, because such a page cannot be freed. The path in mmu_page_zap_pte() that recursively zaps a parentless nested TDP child has no such check. As a result, a shadow page can be zapped even if the page itself can't be freed; as the comment in kvm_mmu_zap_oldest_mmu_pages() notes, zapping it will just force vCPUs to rebuild the page.

As in top-level reclaim, do not recursively prepare zapping of a nested TDP child whose root_count is non-zero.

Fixes 2de4085cccea ("KVM: x86/MMU: Recursively zap nested TDP SPs when zapping last/only parent")
[sean: resolve minor conflict due to lack of lockless aging in 6.12]
---
 arch/x86/kvm/mmu/mmu.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
arch/x86/kvm/mmu/mmu.c +3 -1
index ab6d3f565b0a1..1dfc568631e29 100644
@@ -2500,7 +2500,9 @@ static int mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp,
 			 * avoids retaining a large number of stale nested SPs.
 			 */
 			if (tdp_enabled && invalid_list &&
-			    child->role.guest_mode && !child->parent_ptes.val)
+			    child->role.guest_mode &&
+			    !child->root_count &&
+			    !child->parent_ptes.val)
 				return kvm_mmu_prepare_zap_page(kvm, child,
 								invalid_list);
 		}
Signature · 1 line
2.53.0
author maintainer
#122 lore raw

PATCH 122/247 fs: get rid of __FMODE_NONOTIFY kludge

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Al Viro <[email protected]>

commit ebe559609d7829b52c6642b581860760984faf9d upstream.

All it takes to get rid of the __FMODE_NONOTIFY kludge is switching fanotify from anon_inode_getfd() to anon_inode_getfile_fmode() and adding a dentry_open_nonotify() helper to be used by fanotify on the other path. That's it - no more weird shit in OPEN_FMODE(), etc.

---
 fs/fcntl.c                         |    4 ++--
 fs/notify/fanotify/fanotify_user.c |   25 ++++++++++++++++---------
 fs/open.c                          |   23 +++++++++++++++++++----
 include/linux/fs.h                 |    6 +++---
 include/uapi/asm-generic/fcntl.h   |    1 -
 5 files changed, 40 insertions(+), 19 deletions(-)
fs/fcntl.c +2 -2
@@ -1166,10 +1166,10 @@ static int __init fcntl_init(void)
 	 * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY
 	 * is defined as O_NONBLOCK on some platforms and not on others.
 	 */
-	BUILD_BUG_ON(21 - 1 /* for O_RDONLY being 0 */ !=
+	BUILD_BUG_ON(20 - 1 /* for O_RDONLY being 0 */ !=
 		HWEIGHT32(
 			(VALID_OPEN_FLAGS & ~(O_NONBLOCK | O_NDELAY)) |
-			__FMODE_EXEC | __FMODE_NONOTIFY));
+			__FMODE_EXEC));
 
 	fasync_cache = kmem_cache_create("fasync_cache",
 					 sizeof(struct fasync_struct), 0,
fs/notify/fanotify/fanotify_user.c +16 -9
@@ -100,8 +100,7 @@ static void __init fanotify_sysctls_init
  *
  * Internal and external open flags are stored together in field f_flags of
  * struct file. Only external open flags shall be allowed in event_f_flags.
- * Internal flags like FMODE_NONOTIFY, FMODE_EXEC, FMODE_NOCMTIME shall be
- * excluded.
+ * Internal flags like FMODE_EXEC shall be excluded.
  */
 #define	FANOTIFY_INIT_ALL_EVENT_F_BITS				( \
 		O_ACCMODE	| O_APPEND	| O_NONBLOCK	| \
@@ -258,12 +257,11 @@ static int create_fd(struct fsnotify_gro
 		return client_fd;
 
 	/*
-	 * we need a new file handle for the userspace program so it can read even if it was
-	 * originally opened O_WRONLY.
+	 * We provide an fd for the userspace program, so it could access the
+	 * file without generating fanotify events itself.
 	 */
-	new_file = dentry_open(path,
-			       group->fanotify_data.f_flags | __FMODE_NONOTIFY,
-			       current_cred());
+	new_file = dentry_open_nonotify(path, group->fanotify_data.f_flags,
+					current_cred());
 	if (IS_ERR(new_file)) {
 		put_unused_fd(client_fd);
 		client_fd = PTR_ERR(new_file);
@@ -1400,6 +1398,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned
 	unsigned int fid_mode = flags & FANOTIFY_FID_BITS;
 	unsigned int class = flags & FANOTIFY_CLASS_BITS;
 	unsigned int internal_flags = 0;
+	struct file *file;
 
 	pr_debug("%s: flags=%x event_f_flags=%x\n",
 		 __func__, flags, event_f_flags);
@@ -1468,7 +1467,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned
 	    (!(fid_mode & FAN_REPORT_NAME) || !(fid_mode & FAN_REPORT_FID)))
 		return -EINVAL;
 
-	f_flags = O_RDWR | __FMODE_NONOTIFY;
+	f_flags = O_RDWR;
 	if (flags & FAN_CLOEXEC)
 		f_flags |= O_CLOEXEC;
 	if (flags & FAN_NONBLOCK)
@@ -1546,10 +1545,18 @@ SYSCALL_DEFINE2(fanotify_init, unsigned
 			goto out_destroy_group;
 	}
 
-	fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags);
+	fd = get_unused_fd_flags(f_flags);
 	if (fd < 0)
 		goto out_destroy_group;
 
+	file = anon_inode_getfile_fmode("[fanotify]", &fanotify_fops, group,
+					f_flags, FMODE_NONOTIFY);
+	if (IS_ERR(file)) {
+		fd = PTR_ERR(file);
+		put_unused_fd(fd);
+		goto out_destroy_group;
+	}
+	fd_install(fd, file);
 	return fd;
 
 out_destroy_group:
fs/open.c +19 -4
@@ -1124,6 +1124,23 @@ struct file *dentry_open(const struct pa
 }
 EXPORT_SYMBOL(dentry_open);
 
+struct file *dentry_open_nonotify(const struct path *path, int flags,
+				  const struct cred *cred)
+{
+	struct file *f = alloc_empty_file(flags, cred);
+	if (!IS_ERR(f)) {
+		int error;
+
+		f->f_mode |= FMODE_NONOTIFY;
+		error = vfs_open(path, f);
+		if (error) {
+			fput(f);
+			f = ERR_PTR(error);
+		}
+	}
+	return f;
+}
+
 /**
  * dentry_create - Create and open a file
  * @path: path to create
@@ -1221,7 +1238,7 @@ inline struct open_how build_open_how(in
 inline int build_open_flags(const struct open_how *how, struct open_flags *op)
 {
 	u64 flags = how->flags;
-	u64 strip = __FMODE_NONOTIFY | O_CLOEXEC;
+	u64 strip = O_CLOEXEC;
 	int lookup_flags = 0;
 	int acc_mode = ACC_MODE(flags);
 
@@ -1229,9 +1246,7 @@ inline int build_open_flags(const struct
 			 "struct open_flags doesn't yet handle flags > 32 bits");
 
 	/*
-	 * Strip flags that either shouldn't be set by userspace like
-	 * FMODE_NONOTIFY or that aren't relevant in determining struct
-	 * open_flags like O_CLOEXEC.
+	 * Strip flags that aren't relevant in determining struct open_flags.
 	 */
 	flags &= ~strip;
 
include/linux/fs.h +3 -3
@@ -2784,6 +2784,8 @@ static inline struct file *file_open_roo
 }
 struct file *dentry_open(const struct path *path, int flags,
 			 const struct cred *creds);
+struct file *dentry_open_nonotify(const struct path *path, int flags,
+				  const struct cred *cred);
 struct file *dentry_create(const struct path *path, int flags, umode_t mode,
 			   const struct cred *cred);
 struct path *backing_file_user_path(const struct file *f);
@@ -3719,11 +3721,9 @@ struct ctl_table;
 int __init list_bdev_fs_names(char *buf, size_t size);
 
 #define __FMODE_EXEC		((__force int) FMODE_EXEC)
-#define __FMODE_NONOTIFY	((__force int) FMODE_NONOTIFY)
 
 #define ACC_MODE(x) ("\004\002\006\006"[(x)&O_ACCMODE])
-#define OPEN_FMODE(flag) ((__force fmode_t)(((flag + 1) & O_ACCMODE) | \
-					    (flag & __FMODE_NONOTIFY)))
+#define OPEN_FMODE(flag) ((__force fmode_t)((flag + 1) & O_ACCMODE))
 
 static inline bool is_sxid(umode_t mode)
 {
include/uapi/asm-generic/fcntl.h +0 -1
@@ -6,7 +6,6 @@
 
 /*
  * FMODE_EXEC is 0x20
- * FMODE_NONOTIFY is 0x4000000
  * These cannot be used by userspace O_* until internal and external open
  * flags are split.
  * -Eric Paris
#123 lore raw

Re: [PATCH 6.12 122/247] fs: get rid of __FMODE_NONOTIFY kludge

Quoted 2 lines · Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

Objection.

This causes a (potential) deadlock regression:

https://lore.kernel.org/linux-unionfs/[email protected]/

Working on a fix, but no reason to backport a commit, which is essentially code cleanup that causes a regression.

Not sure if this already hit other LTS kernels.

Thanks,
Amir.

maintainer
#124 lore raw

Re: [PATCH 6.12 122/247] fs: get rid of __FMODE_NONOTIFY kludge

Quoted 5 lines · Amir Goldstein

Objection.

This causes a (potential) deadlock regression:

https://lore.kernel.org/linux-unionfs/[email protected]/

Dropped from the 6.12 queue, together with its follow-up "fanotify: Fix crash in fanotify_init(2)", which does not apply without it.

For context, it had been queued only to fix the sparc 6.12.y build (fcntl_init() BUILD_BUG_ON) that came from the 6.12 backport of "fsnotify: opt-in for permission events at file open time". We will revisit it together with Edvard's fix once that is in mainline.

Quoted 1 line

Not sure if this already hit other LTS kernels.

6.18.y, 7.2.y and 7.3.y already carry it via mainline (v6.14), so they will need the upstream fix. 6.6.y and older do not have it.

Signature · 2 lines
Thanks,
Sasha
author maintainer
#125 lore raw

PATCH 123/247 net: bridge: mcast: dont truncate the port group walk on teardown

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jun Yang <[email protected]>

commit 5a3f7a683aee56e1f15c9d53041f3236767eaae7 upstream.

__br_multicast_disable_port_ctx() and br_multicast_del_port() walk port->mglist with hlist_for_each_entry_safe(). However, br_multicast_find_del_pg() can also delete other entries from the same list through br_multicast_fwd_src_remove() or __fwd_del_star_excl().

If such an entry is the iterator's saved next node, hlist_del_init() clears its ->next and terminates the walk early. The reproducer triggers this in both teardown walks, leaving port groups in the bridge mdb with a dangling ->key.port after del_nbp() frees the port:

  BUG: KASAN: slab-use-after-free in __mdb_fill_info+0x1191/0x1320
   __mdb_fill_info+0x1191/0x1320
   br_mdb_dump+0x594/0xe40
   rtnl_mdb_dump+0x1cf/0x5d0

Use hlist_del_init_rcu() to unlink the group while preserving ->next. br_multicast_del_pg() and the teardown walks run under br->multicast_lock. The GC worker must acquire the same lock before detaching the group for destruction, so the node remains alive while the walk uses the preserved pointer.

Preserving ->next means a walk can now reach a group that an earlier iteration already deleted as a side effect. That group is off mp->ports, so br_multicast_find_del_pg() would fall through its port scan and hit the trailing WARN_ON(1). Skip such groups at the top of that helper: a port group is put on port->mglist when it is created and only unlinked when it is deleted, so hlist_unhashed() identifies exactly this case.

---
 net/bridge/br_multicast.c |   13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)
net/bridge/br_multicast.c +12 -1
@@ -813,7 +813,11 @@ static void __br_multicast_del_pg(struct
 	struct hlist_node *tmp;
 
 	rcu_assign_pointer(*pp, pg->next);
-	hlist_del_init(&pg->mglist);
+	/* Keep ->next (held under multicast_lock, freed later by the GC work):
+	 * a port->mglist teardown walk may have latched this node as its next,
+	 * and deleting other groups of the same port must not truncate it.
+	 */
+	hlist_del_init_rcu(&pg->mglist);
 	br_multicast_eht_clean_sets(pg);
 	hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
 		br_multicast_del_group_src(ent, false);
@@ -848,6 +852,13 @@ static void br_multicast_find_del_pg(str
 	struct net_bridge_mdb_entry *mp;
 	struct net_bridge_port_group *p;
 
+	/* A teardown walk over port->mglist can reach a group that an earlier
+	 * iteration already deleted as a side effect. It is off mp->ports by
+	 * now, so skip it instead of falling through to the WARN_ON() below.
+	 */
+	if (hlist_unhashed(&pg->mglist))
+		return;
+
 	mp = br_mdb_ip_get(br, &pg->key.addr);
 	if (WARN_ON(!mp))
 		return;
author maintainer
#126 lore raw

PATCH 124/247 ksmbd: fill in FileSysIdentifier in FS_POSIX_INFORMATION

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aleksandr Khromov <[email protected]>

commit db2267b27c054a6c2151ff7fbb67927e784f31d6 upstream.

smb2_get_info_filesystem() reports 56 bytes for FS_POSIX_INFORMATION, that is the whole of FILE_SYSTEM_POSIX_INFO, but never assigns FileSysIdentifier. Those eight bytes go to the client as they are found in the response buffer.

The buffer is zeroed on allocation, so a standalone request leaks nothing. A compound request can leak: the offset of the next response is advanced by the length pinned for the previous one, so a reply that was written into the buffer and then dropped in favour of the short error response of smb2_set_err_rsp() stays there, and the next reply is laid over it with only the header cleared.

Report the file system id statfs() returned, which is what the field is for. FileSysIdentifier is __le64 and f_fsid is a pair of ints, so assemble the value first, val[0] as the low half, and convert it on the way out.

Fixes e2f34481b24d ("cifsd: add server-side procedures for SMB3")
---
 fs/smb/server/smb2pdu.c |    3 +++
 1 file changed, 3 insertions(+)
fs/smb/server/smb2pdu.c +3 -0
@@ -5774,6 +5774,9 @@ static int smb2_get_info_filesystem(stru
 			info->UserBlocksAvail = cpu_to_le64(stfs.f_bavail);
 			info->TotalFileNodes = cpu_to_le64(stfs.f_files);
 			info->FreeFileNodes = cpu_to_le64(stfs.f_ffree);
+			info->FileSysIdentifier =
+				cpu_to_le64((u64)(u32)stfs.f_fsid.val[1] << 32 |
+					    (u32)stfs.f_fsid.val[0]);
 			rsp->OutputBufferLength = cpu_to_le32(56);
 			fixed_len = 56;
 		}
author maintainer
#127 lore raw

PATCH 125/247 ksmbd: initialize FileSystemControlFlags in FS_CONTROL_INFORMATION

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aleksandr Khromov <[email protected]>

commit c0cd3fc6824122014da2b3b0cb7ddeaa2946ec8e upstream.

smb2_get_info_filesystem() reports 48 bytes for FS_CONTROL_INFORMATION, that is the whole of struct smb2_fs_control_info, but never assigns FileSystemControlFlags. Those four bytes go to the client as they are found in the response buffer.

The buffer is zeroed on allocation, so a standalone request leaks nothing. A compound request can leak: the offset of the next response is advanced by the length pinned for the previous one, so a reply that was written into the buffer and then dropped in favour of the short error response of smb2_set_err_rsp() stays there, and the next reply is laid over it with only the header cleared.

ksmbd does not implement quota tracking, so report no control flags.

Fixes e2f34481b24d ("cifsd: add server-side procedures for SMB3")
---
 fs/smb/server/smb2pdu.c |    1 +
 1 file changed, 1 insertion(+)
fs/smb/server/smb2pdu.c +1 -0
@@ -5752,6 +5752,7 @@ static int smb2_get_info_filesystem(stru
 		info->FreeSpaceStopFiltering = 0;
 		info->DefaultQuotaThreshold = cpu_to_le64(SMB2_NO_FID);
 		info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID);
+		info->FileSystemControlFlags = 0;
 		info->Padding = 0;
 		rsp->OutputBufferLength = cpu_to_le32(48);
 		fixed_len = 48;
author maintainer
#128 lore raw

PATCH 126/247 ksmbd: zero the FS_OBJECT_ID_INFORMATION buffer before filling it in

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aleksandr Khromov <[email protected]>

commit 399aa12450a61a5c73dc77e73f069ece9687c95d upstream.

smb2_get_info_filesystem() reports 64 bytes for FS_OBJECT_ID_INFORMATION, that is the whole of struct object_id_info, but writes only 46 of them:

- objid[] is 16 bytes, and when the volume UUID is not available only sizeof(stfs.f_fsid) (8) bytes are copied into it;
- extended_info.version_string[] is STRING_LENGTH (28) bytes, and only strlen("1.1.0") (5) bytes are copied into it.

The response buffer is zeroed on allocation (kvzalloc() in smb2_allocate_rsp_buf()), so for a standalone request the remaining 31 bytes are zero. In a compound request they need not be. The offset of the next response is advanced by the length pinned for the previous one, so if a preceding command wrote its reply into the buffer and then failed, smb2_set_err_rsp() pins only the short error response and the next reply lands inside the area that has already been written. Only the header is cleared there:

	memset((char *)rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);

The client then receives up to 31 bytes of a response it was not meant to see, including one that failed with an access denied error.

Clear the structure before filling it in. As a side effect version_string is now NUL terminated.

Fixes e2f34481b24d ("cifsd: add server-side procedures for SMB3")
---
 fs/smb/server/smb2pdu.c |    1 +
 1 file changed, 1 insertion(+)
fs/smb/server/smb2pdu.c +1 -0
@@ -5698,6 +5698,7 @@ static int smb2_get_info_filesystem(stru
 		struct object_id_info *info;
 
 		info = (struct object_id_info *)(rsp->Buffer);
+		memset(info, 0, sizeof(*info));
 
 		if (path.mnt->mnt_sb->s_uuid_len == 16)
 			memcpy(info->objid, path.mnt->mnt_sb->s_uuid.b,
author maintainer
#129 lore raw

PATCH 127/247 net/sched: cls_flower: validate mask pointer after nla_next()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aohan Mei <[email protected]>

commit fee10655709c5c597e8e9f722f3035d9ea31ff3a upstream.

fl_set_enc_opt() iterates the key's nested tunnel-option attributes with nla_for_each_attr() while advancing a single mask pointer via nla_next() at the bottom of each loop, so the mask cursor is driven by the number of key attributes rather than by the mask's own attributes. The nla_ok() added by commit c96adff956191 ("cls_flower:
call nla_ok() before nla_next()") only validates the mask pointer that was just consumed; the pointer produced by nla_next() is used by the next iteration (fl_set_geneve_opt() and siblings) without any validation.

The mask's nested attributes are validated with NL_VALIDATE_LIBERAL, which merely warns on trailing bytes that do not form a complete attribute. A mask carrying one valid attribute plus 1-3 residue bytes (or a non-aligned attribute length making msk_depth negative) therefore reaches the next iteration with msk_depth != 0, so neither the !msk_depth check in fl_set_enc_opt() nor the !depth check in the per-type helpers fires. nla_type() then reads past the mask payload and nla_parse_nested_deprecated() iterates with an nla_len taken from those bytes, reading well beyond the mask attribute (KASAN:
slab-out-of-bounds read in __nla_validate_parse from fl_change()).

Validate the advanced mask pointer as well: when the mask is not legitimately exhausted (msk_depth != 0) and the new pointer fails nla_ok(), reject the filter with -EINVAL. An exactly exhausted mask still skips the check, preserving exact-match behaviour for the remaining key attributes.

---
 net/sched/cls_flower.c |    5 +++++
 1 file changed, 5 insertions(+)
net/sched/cls_flower.c +5 -0
@@ -1707,6 +1707,11 @@ static int fl_set_enc_opt(struct nlattr
 			return -EINVAL;
 		}
 		nla_opt_msk = nla_next(nla_opt_msk, &msk_depth);
+
+		if (msk_depth && !nla_ok(nla_opt_msk, msk_depth)) {
+			NL_SET_ERR_MSG(extack, "A mask attribute is invalid");
+			return -EINVAL;
+		}
 	}
 
 	return 0;
author maintainer
#130 lore raw

PATCH 128/247 smb: client: fix heap overflow in cifs_do_set_acl()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Frank Sorenson <[email protected]>

commit 1dac61e2c29d1a71784604b48cf3b1234a65ec29 upstream.

cifs_set_acl() validates ACL size using posix_acl_xattr_size():

4 + (count * 8) // 4-byte header + 8 bytes per ACE

cifs_do_set_acl() then calls posix_acl_to_cifs() to write the CIFS wire format into the same buffer:

        6 + (count * 10)  // 6-byte header + 10 bytes per ACE

An ACL that passes the xattr-based check in cifs_set_acl() can overflow the heap when posix_acl_to_cifs() writes the larger CIFS format.

Validate the CIFS format size against the remaining buffer space and USHRT_MAX before converting--data_count is __u16, so sizes above USHRT_MAX truncate the on-wire packet length, causing the server to apply a partial ACL. Replace MaxDataCount = 1000 with min(CIFSMaxBufSize, USHRT_MAX).

Fixes dc1af4c4b4721 ("cifs: implement set acl method")
---
 fs/smb/client/cifssmb.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
fs/smb/client/cifssmb.c +11 -2
@@ -3188,6 +3188,7 @@ int cifs_do_set_acl(const unsigned int x
 	int rc = 0;
 	int bytes_returned = 0;
 	__u16 params, byte_count, data_count, param_offset, offset;
+	size_t cifs_acl_size, bytes_available;
 
 	cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
 setAclRetry:
@@ -3206,8 +3207,7 @@ setAclRetry:
 	}
 	params = 6 + name_len;
 	pSMB->MaxParameterCount = cpu_to_le16(2);
-	/* BB find max SMB size from sess */
-	pSMB->MaxDataCount = cpu_to_le16(1000);
+	pSMB->MaxDataCount = cpu_to_le16(min_t(unsigned int, CIFSMaxBufSize, USHRT_MAX));
 	pSMB->MaxSetupCount = 0;
 	pSMB->Reserved = 0;
 	pSMB->Flags = 0;
@@ -3219,6 +3219,15 @@ setAclRetry:
 	parm_data = ((char *)pSMB) + sizeof(pSMB->hdr.smb_buf_length) + offset;
 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
 
+	/* make sure we can fit the larger cifs_posix_aces in the buffer */
+	cifs_acl_size = sizeof(struct cifs_posix_acl) +
+		       (acl->a_count * sizeof(struct cifs_posix_ace));
+	bytes_available = (CIFSMaxBufSize + MAX_HEADER_SIZE(tcon->ses->server)) - offset;
+	if (cifs_acl_size > bytes_available || cifs_acl_size > USHRT_MAX) {
+		rc = -E2BIG;
+		goto setACLerrorExit;
+	}
+
 	/* convert to on the wire format for POSIX ACL */
 	data_count = posix_acl_to_cifs(parm_data, acl, acl_type);
 
author maintainer
#131 lore raw

PATCH 129/247 smb: client: fix multiuser mount with krb5

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Paulo Alcantara <[email protected]>

commit 69499395867332364ed4ac8b546537eb3bab6ebb upstream.

Customer reported that they could no longer mount their SMB shares with multiuser mount option and krb5. Turned out that the client wasn't duplicating username option when creating multiuser connections, therefore failing to retrieve credentials as cifs.upcall(8) couldn't find them in keytab.

Fix this by duplicating username option (if set) from original fs context before creating multiuser connections with krb5.

Reproducer:

  ```
  $ ktutil
  ktutil:  add_entry -password -p testuser -k 1 -e aes256-cts
  Password for [email protected]:
  ktutil:  write_kt /etc/krb5.keytab
  ktutil:  quit
  $ klist -ke
  Keytab name: FILE:/etc/krb5.keytab
  KVNO Principal
   ---- ----------------------------------------------------------------
     1 [email protected] (aes256-cts-hmac-sha1-96)
  $ mount.cifs //w22-root2/scratch /mnt/1 -o \
      	uid=1000,sec=krb5,[email protected],multiuser
  mount error(13): Permission denied
  Refer to the mount.cifs(8) manual page (e.g. man mount.cifs) and
  kernel log messages (dmesg)
  ```
Fixes 12b4c5d98cd7 ("smb: client: fix krb5 mount with username option")
Cc Ronnie Sahlberg <[email protected]>
Cc Shyam Prasad N <[email protected]>
Cc Tom Talpey <[email protected]>
Cc Bharath SM <[email protected]>
Cc Namjae Jeon <[email protected]>
---
 fs/smb/client/connect.c |   21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)
fs/smb/client/connect.c +16 -5
@@ -4151,14 +4151,25 @@ cifs_setup_session(const unsigned int xi
 	return rc;
 }
 
-static int
-cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses)
+static int set_fs_context_auth(struct smb3_fs_context *ctx,
+			       struct cifs_ses *ses)
 {
 	ctx->sectype = ses->sectype;
 
-	/* krb5 is special, since we don't need username or pw */
-	if (ctx->sectype == Kerberos)
+	/*
+	 * krb5 is special as we might need to pass username (passwordless) down
+	 * to cifs.upcall(8) for keytab.
+	 */
+	if (ctx->sectype == Kerberos) {
+		if (ses->user_name && ses->user_name[0]) {
+			ctx->username = kstrndup(ses->user_name,
+						 CIFS_MAX_USERNAME_LEN,
+						 GFP_KERNEL);
+			if (!ctx->username)
+				return -ENOMEM;
+		}
 		return 0;
+	}
 
 	return cifs_set_cifscreds(ctx, ses);
 }
@@ -4197,7 +4208,7 @@ cifs_construct_tcon(struct cifs_sb_info
 	ctx->witness = master_tcon->use_witness;
 	ctx->dfs_root_ses = master_tcon->ses->dfs_root_ses;
 
-	rc = cifs_set_vol_auth(ctx, master_tcon->ses);
+	rc = set_fs_context_auth(ctx, master_tcon->ses);
 	if (rc) {
 		tcon = ERR_PTR(rc);
 		goto out;
author maintainer
#132 lore raw

PATCH 130/247 scsi: st: Fix use-after-free race between st_remove() and st_open()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Laurence Oberman <[email protected]>

commit 71caf7f84bd62a4bf91cc95abd2de690afb8d15f upstream.

st_remove() drops the probe reference on the scsi_tape with kref_put() and only afterwards removes its index from st_index_idr. If that was the last reference, scsi_tape_release() frees the structure while it is still published in the idr.

scsi_tape_get(), called from st_open(), looks up the tape in st_index_idr and takes a reference under st_ref_mutex and st_index_lock. remove_cdevs() does not wait for opens that are already past chrdev_open(), so an open that is blocked on st_ref_mutex while st_remove() holds it can take the mutex as soon as st_remove() drops it, find the freed tpnt in the idr and call kref_get() on it:

  BUG: KASAN: slab-use-after-free in st_open
  Write of size 4 ...
   st_open (drivers/scsi/st.c:254 drivers/scsi/st.c:1291)
   chrdev_open
  ...
  Freed by task ...:
   kfree
   st_remove (drivers/scsi/st.c:4512)

Remove the index from the idr before dropping the reference, all under st_ref_mutex. A concurrent scsi_tape_get() now either runs first and holds a valid reference that keeps the tape alive, or runs afterwards and finds nothing, so st_open() fails with -ENXIO.

This restores the ordering st_remove() had before the conversion to st_index_idr, where the tape was unpublished from scsi_tapes[] before the final kref_put().

---
 drivers/scsi/st.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/scsi/st.c +2 -2
index 95da0d15e0490..53d37c8497714 100644
@@ -4448,11 +4448,11 @@ static int st_remove(struct device *dev)
 	remove_cdevs(tpnt);
 
 	mutex_lock(&st_ref_mutex);
-	kref_put(&tpnt->kref, scsi_tape_release);
-	mutex_unlock(&st_ref_mutex);
 	spin_lock(&st_index_lock);
 	idr_remove(&st_index_idr, index);
 	spin_unlock(&st_index_lock);
+	kref_put(&tpnt->kref, scsi_tape_release);
+	mutex_unlock(&st_ref_mutex);
 	return 0;
 }
 
Signature · 1 line
2.53.0
author maintainer
#133 lore raw

PATCH 131/247 perf: Track the perf mlock gift separately

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Lorenzo Stoakes (ARM) <[email protected]>

commit 78436f3882fb8d848c823bf8cf754988202c5ca5 upstream.

The locked_vm field in struct user_struct was originally introduced by commit 789f90fcf6b0 ("perf_counter: per user mlock gift") to provide an mlock-like budget for perf measured against mm->pinned_vm (since 2011).

perf grants each user a 'gift' of perf_event_mlock_kb * online CPUs of buffer pages which is charged to user->locked_vm without any RLIMIT_MEMLOCK check.

Only pages beyond the gift are checked against RLIMIT_MEMLOCK, per-mm, via mm->pinned_vm, which makes it different from the standard mlock() check, which is per-mm and made against RLIMIT_MEMLOCK.

However, since this was introduced, a number of other components have utilised this field where a shared resource needed to be limited against RLIMIT_MEMLOCK.

The other users are currently MSG_ZEROCOPY, io_uring, AF_XDP, iommufd, s390 KVM zPCI and most recently, secretmem in commit 97d34aa65c29 ("mm/secretmem: properly account locked pages").

This field is shared between all of these, but because the gift is not checked against RLIMIT_MEMLOCK, user->locked_vm alone can exceed it - 16 CPUs * 516 KiB is already more than the default 8 MiB - after which every other user of the field fails unconditionally.

This was reported by a user who saw secretmem failing while running a simultaneous perf record.

Resolve the issue by simply giving perf its own field separate from the rest.

BPF used user->locked_vm up until commit 80ee81e0403c ("bpf: Eliminate rlimit-based memory accounting infra for bpf maps") which landed in 5.11.

Thus also eliminate the now-defunct ifdef for CONFIG_BPF_SYSCALL for user->locked_vm.

This issue exists between all of the components which use user->locked_vm and perf, however secretmem is the first user who has caused a report over many years and has been backported, so target the fix at that.

Fixes 97d34aa65c29 ("mm/secretmem: properly account locked pages")
[ ljs - Applied to open-coded accounting in perf_mmap()/perf_mmap_close(). ]
---
 include/linux/sched/user.h |  8 +++++---
 kernel/events/core.c       | 12 ++++++------
 2 files changed, 11 insertions(+), 9 deletions(-)
include/linux/sched/user.h +5 -3
index 8d7e5521f7cdd..ec262b605437c 100644
@@ -23,11 +23,13 @@ struct user_struct {
 	struct hlist_node uidhash_node;
 	kuid_t uid;
 
-#if defined(CONFIG_PERF_EVENTS) || defined(CONFIG_BPF_SYSCALL) || \
-	defined(CONFIG_NET) || defined(CONFIG_IO_URING) || \
+#if defined(CONFIG_NET) || defined(CONFIG_IO_URING) || \
 	defined(CONFIG_VFIO_PCI_ZDEV_KVM) || IS_ENABLED(CONFIG_IOMMUFD) || \
 	defined(CONFIG_SECRETMEM)
-	atomic_long_t locked_vm;
+	atomic_long_t locked_vm;	/* pinned pages charged to RLIMIT_MEMLOCK */
+#endif
+#ifdef CONFIG_PERF_EVENTS
+	atomic_long_t perf_mlock;	/* perf buffer pages within perf_event_mlock_kb */
 #endif
 #ifdef CONFIG_WATCH_QUEUE
 	atomic_t nr_watches;	/* The number of watches this user currently has */
kernel/events/core.c +6 -6
index 5f02d85957637..d14cf23a16a63 100644
@@ -6534,7 +6534,7 @@ static void perf_mmap_close(struct vm_area_struct *vma)
 		perf_pmu_output_stop(event);
 
 		/* now it's safe to free the pages */
-		atomic_long_sub(rb->aux_nr_pages - rb->aux_mmap_locked, &mmap_user->locked_vm);
+		atomic_long_sub(rb->aux_nr_pages - rb->aux_mmap_locked, &mmap_user->perf_mlock);
 		atomic64_sub(rb->aux_mmap_locked, &vma->vm_mm->pinned_vm);
 
 		/* this has to be the last one */
@@ -6609,7 +6609,7 @@ static void perf_mmap_close(struct vm_area_struct *vma)
 	 */
 
 	atomic_long_sub((size >> PAGE_SHIFT) + 1 - mmap_locked,
-			&mmap_user->locked_vm);
+			&mmap_user->perf_mlock);
 	atomic64_sub(mmap_locked, &vma->vm_mm->pinned_vm);
 	free_uid(mmap_user);
 
@@ -6769,11 +6769,11 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
 	 */
 	user_lock_limit *= num_online_cpus();
 
-	user_locked = atomic_long_read(&user->locked_vm);
+	user_locked = atomic_long_read(&user->perf_mlock);
 
 	/*
 	 * sysctl_perf_event_mlock may have changed, so that
-	 *     user->locked_vm > user_lock_limit
+	 *     user->perf_mlock > user_lock_limit
 	 */
 	if (user_locked > user_lock_limit)
 		user_locked = user_lock_limit;
@@ -6781,7 +6781,7 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
 
 	if (user_locked > user_lock_limit) {
 		/*
-		 * charge locked_vm until it hits user_lock_limit;
+		 * charge perf_mlock until it hits user_lock_limit;
 		 * charge the rest from pinned_vm
 		 */
 		extra = user_locked - user_lock_limit;
@@ -6833,7 +6833,7 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
 
 unlock:
 	if (!ret) {
-		atomic_long_add(user_extra, &user->locked_vm);
+		atomic_long_add(user_extra, &user->perf_mlock);
 		atomic64_add(extra, &vma->vm_mm->pinned_vm);
 
 		atomic_inc(&event->mmap_count);
Signature · 1 line
2.53.0
author maintainer
#134 lore raw

PATCH 132/247 eventfs: fix SRCU read lock leak in eventfs_iterate()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Joseph Qi <[email protected]>

Commit 1b95382da85c ("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.12.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 1b95382da85c ("eventfs: Use children field for rcu head and add memory barriers")
---
 fs/tracefs/event_inode.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
fs/tracefs/event_inode.c +4 -2
index a992c2919f79a..e060dfe7cdac5 100644
@@ -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--;
Signature · 1 line
2.53.0
author maintainer
#135 lore raw

PATCH 133/247 net: call getsockopt_iter if available

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Breno Leitao <[email protected]>

commit 5bd0dec150f56b6307d599132dcb7c01007bbecc upstream.

Update do_sock_getsockopt() to use the new getsockopt_iter callback when available. Add do_sock_getsockopt_iter() helper that:

1. Reads optlen from user/kernel space
2. Initializes a sockopt_t with the appropriate iov_iter (kvec for kernel, ubuf for user buffers) and sets opt.optlen
3. Calls the protocol's getsockopt_iter callback
4. Writes opt.optlen back to user/kernel space

The optlen is always written back, even on failure. Some protocols (e.g. CAN raw) return -ERANGE and set optlen to the required buffer size so userspace knows how much to allocate.

The callback is responsible for setting opt.optlen to indicate the returned data size.

Important to say that iov_out does not need to be copied back in do_sock_getsockopt().

When optval is not kernel (the userspace path), sockptr_to_sockopt() sets up opt->iter_out as a ITER_DEST ubuf iterator pointing directly at the userspace buffer (optval.user). So when getsockopt_iter implementations call copy_to_iter(..., &opt->iter_out), the data is written directly to userspace — no intermediate kernel buffer is involved.

When optval.is_kernel is true (the in-kernel path, e.g. from io_uring), the kvec points at the already-provided kernel buffer (optval.kernel), so the data lands in the caller's buffer directly via the kvec-backed iterator.

In both cases the iterator writes to the final destination in-place at protocol callback. There's nothing to copy back — only optlen needs to be written back.

---
 net/socket.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 51 insertions(+), 3 deletions(-)
net/socket.c +51 -3
index 723bc3a1ba5cd..b8d662e193103 100644
@@ -77,6 +77,7 @@
 #include <linux/mount.h>
 #include <linux/pseudo_fs.h>
 #include <linux/security.h>
+#include <linux/uio.h>
 #include <linux/syscalls.h>
 #include <linux/compat.h>
 #include <linux/kmod.h>
@@ -2345,11 +2346,45 @@ SYSCALL_DEFINE5(setsockopt, int, fd, int, level, int, optname,
 INDIRECT_CALLABLE_DECLARE(bool tcp_bpf_bypass_getsockopt(int level,
 							 int optname));
 
+/*
+ * Initialize a sockopt_t from sockptr optval/optlen, setting up iov_iter
+ * for both input and output directions.
+ * It is important to remember that both iov points to the same data, but,
+ * .iter_in is read-only and .iter_out is write-only by the protocol callbacks
+ */
+static int sockptr_to_sockopt(sockopt_t *opt, sockptr_t optval,
+			      sockptr_t optlen, struct kvec *kvec)
+{
+	int koptlen;
+
+	if (copy_from_sockptr(&koptlen, optlen, sizeof(int)))
+		return -EFAULT;
+
+	if (koptlen < 0)
+		return -EINVAL;
+
+	if (optval.is_kernel) {
+		kvec->iov_base = optval.kernel;
+		kvec->iov_len = koptlen;
+		iov_iter_kvec(&opt->iter_out, ITER_DEST, kvec, 1, koptlen);
+		iov_iter_kvec(&opt->iter_in, ITER_SOURCE, kvec, 1, koptlen);
+	} else {
+		iov_iter_ubuf(&opt->iter_out, ITER_DEST, optval.user, koptlen);
+		iov_iter_ubuf(&opt->iter_in, ITER_SOURCE, optval.user,
+			      koptlen);
+	}
+	opt->optlen = koptlen;
+
+	return 0;
+}
+
 int do_sock_getsockopt(struct socket *sock, bool compat, int level,
 		       int optname, sockptr_t optval, sockptr_t optlen)
 {
 	int max_optlen __maybe_unused = 0;
 	const struct proto_ops *ops;
+	struct kvec kvec;
+	sockopt_t opt;
 	int err;
 
 	err = security_socket_getsockopt(sock, level, optname);
@@ -2362,15 +2397,28 @@ int do_sock_getsockopt(struct socket *sock, bool compat, int level,
 	ops = READ_ONCE(sock->ops);
 	if (level == SOL_SOCKET) {
 		err = sk_getsockopt(sock->sk, level, optname, optval, optlen);
-	} else if (unlikely(!ops->getsockopt)) {
-		err = -EOPNOTSUPP;
-	} else {
+	} else if (ops->getsockopt_iter) {
+		err = sockptr_to_sockopt(&opt, optval, optlen, &kvec);
+		if (err)
+			return err;
+
+		err = ops->getsockopt_iter(sock, level, optname, &opt);
+
+		/* Always write back optlen, even on failure. Some protocols
+		 * (e.g. CAN raw) return -ERANGE and set optlen to the
+		 * required buffer size so userspace can discover it.
+		 */
+		if (copy_to_sockptr(optlen, &opt.optlen, sizeof(int)))
+			return -EFAULT;
+	} else if (ops->getsockopt) {
 		if (WARN_ONCE(optval.is_kernel || optlen.is_kernel,
 			      "Invalid argument type"))
 			return -EOPNOTSUPP;
 
 		err = ops->getsockopt(sock, level, optname, optval.user,
 				      optlen.user);
+	} else {
+		err = -EOPNOTSUPP;
 	}
 
 	if (!compat)
Signature · 1 line
2.53.0
author maintainer
#136 lore raw

PATCH 134/247 kernel/watch_queue: Use pipe_buf() to retrieve the pipe buffer

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: K Prateek Nayak <[email protected]>

[ Upstream commit 547476063e123e73fc3aea8432104ab092ffea84 ]

Use pipe_buf() helper to retrieve the pipe buffer in post_one_notification() replacing the open-coded the logic.

Stable-dep-of: 5e531d62f1f8 ("watch_queue: Fix note pool publication race in watch_queue_set_size()")

---
 kernel/watch_queue.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
kernel/watch_queue.c +3 -4
index 6d1936fb8ff02..5da67a1047d00 100644
@@ -101,12 +101,11 @@ static bool post_one_notification(struct watch_queue *wqueue,
 	struct pipe_inode_info *pipe = wqueue->pipe;
 	struct pipe_buffer *buf;
 	struct page *page;
-	unsigned int head, tail, mask, note, offset, len;
+	unsigned int head, tail, note, offset, len;
 	bool done = false;
 
 	spin_lock_irq(&pipe->rd_wait.lock);
 
-	mask = pipe->ring_size - 1;
 	head = pipe->head;
 	tail = pipe->tail;
 	if (pipe_full(head, tail, pipe->ring_size))
@@ -124,7 +123,7 @@ static bool post_one_notification(struct watch_queue *wqueue,
 	memcpy(p + offset, n, len);
 	kunmap_atomic(p);
 
-	buf = &pipe->bufs[head & mask];
+	buf = pipe_buf(pipe, head);
 	buf->page = page;
 	buf->private = (unsigned long)wqueue;
 	buf->ops = &watch_queue_pipe_buf_ops;
@@ -147,7 +146,7 @@ static bool post_one_notification(struct watch_queue *wqueue,
 	return done;
 
 lost:
-	buf = &pipe->bufs[(head - 1) & mask];
+	buf = pipe_buf(pipe, head - 1);
 	buf->flags |= PIPE_BUF_FLAG_LOSS;
 	goto out;
 }
Signature · 1 line
2.53.0
author maintainer
#137 lore raw

PATCH 135/247 watch_queue: Fix note pool publication race in watch_queue_set_size()

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Cen Zhang (Microsoft) <[email protected]>

[ Upstream commit 5e531d62f1f8834faf7a4eed0d41105cf6570a8f ]

There is a race between watch_queue_set_size() and post_one_notification(). watch_queue_set_size() publishes wqueue->notes, notes_bitmap, nr_pages and nr_notes with plain stores, and post_one_notification() reads them with plain loads under a different lock, so nothing orders the four stores against the poster's loads: depending on the build, notes can be stored last (gcc 14 on x86-64 does this). Since keyctl_watch_key() lets a watch attach to a queue before it is sized, post_one_notification() can already be running on another CPU at that moment: it reads nr_notes > 0, passes its bounds check, and then reads notes while it is still NULL, dereferencing notes[0].

An unprivileged user triggers this reliably by watching one of its own keys through a notification pipe and racing ioctl(IOC_WATCH_QUEUE_SET_SIZE) on that pipe against keyctl(KEYCTL_SETPERM) on the key from another CPU, triggering:

  BUG: kernel NULL pointer dereference, address: 0000000000000000
  RIP: 0010:post_one_notification.isra.0+0xa2/0x1e0
  Call Trace:
   __post_watch_notification+0x148/0x180
   keyctl_setperm_key+0x119/0x130
   do_syscall_64+0x108/0x4c0
   entry_SYSCALL_64_after_hwframe+0x77/0x7f
  Kernel panic - not syncing: Fatal exception in interrupt

Fix this by publishing nr_notes last with smp_store_release(), and by reading it once with smp_load_acquire() in post_one_notification() before the bitmap and the page array are touched. A poster that observes a non-zero nr_notes then also observes the pool behind it; one that observes zero drops the notification as before.

---
 kernel/watch_queue.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
kernel/watch_queue.c +7 -4
index 5da67a1047d00..8f809cc60c036 100644
@@ -101,7 +101,7 @@ static bool post_one_notification(struct watch_queue *wqueue,
 	struct pipe_inode_info *pipe = wqueue->pipe;
 	struct pipe_buffer *buf;
 	struct page *page;
-	unsigned int head, tail, note, offset, len;
+	unsigned int head, tail, note, offset, len, nr_notes;
 	bool done = false;
 
 	spin_lock_irq(&pipe->rd_wait.lock);
@@ -111,8 +111,10 @@ static bool post_one_notification(struct watch_queue *wqueue,
 	if (pipe_full(head, tail, pipe->ring_size))
 		goto lost;
 
-	note = find_first_bit(wqueue->notes_bitmap, wqueue->nr_notes);
-	if (note >= wqueue->nr_notes)
+	/* Pairs with the smp_store_release() in watch_queue_set_size(). */
+	nr_notes = smp_load_acquire(&wqueue->nr_notes);
+	note = find_first_bit(wqueue->notes_bitmap, nr_notes);
+	if (note >= nr_notes)
 		goto lost;
 
 	page = wqueue->notes[note / WATCH_QUEUE_NOTES_PER_PAGE];
@@ -297,7 +299,8 @@ long watch_queue_set_size(struct pipe_inode_info *pipe, unsigned int nr_notes)
 	wqueue->notes = pages;
 	wqueue->notes_bitmap = bitmap;
 	wqueue->nr_pages = nr_pages;
-	wqueue->nr_notes = nr_notes;
+	/* Pairs with the smp_load_acquire() in post_one_notification(). */
+	smp_store_release(&wqueue->nr_notes, nr_notes);
 	return 0;
 
 error_p:
Signature · 1 line
2.53.0
author maintainer
#138 lore raw

PATCH 136/247 ntfs3: free the sbi after a grace period

In reply to Greg Kroah-Hartman

6.12-stable review patch. If anyone has any objections, please let me know.

------------------

From: Christian Brauner <[email protected]>

[ Upstream commit c01b091b06412d3b1b53d8c2925f6de586c8a2fc ]

If the "nocase" mount option ntfs3 uses custom hash and dentry compare functions. Both access ntfs3 superblock private information in rcu pathwalk. ntfs_put_super() and ntfs3_kill_sb() drop the info without any rcu delay. A lookup that's still inside the filesystem when the last reference to the mount is dropped causes a UAF. With a pathwalk held up in ntfs_d_hash() while the last reference to the mount goes away:

  BUG: KASAN: slab-use-after-free in ntfs_nls_to_utf16+0x752/0x940
  Read of size 8 at addr ff1100010453c850 by task walker/99
  Call Trace:
   <TASK>
   ntfs_nls_to_utf16+0x752/0x940
   ntfs_d_hash+0x22b/0x500
   link_path_walk+0x11ad/0x1d30
   path_lookupat+0x63/0xb00
   filename_lookup+0x1f1/0x5b0
   vfs_statx+0xd7/0x3b0
  [...]
  Allocated by task 95:
   __kmalloc_cache_noprof+0x249/0x560
   ntfs_init_fs_context+0x2db/0x5a0
   alloc_fs_context+0x52e/0xc70
  [...]
  Freed by task 98:
   kfree+0x1db/0x5b0
   deactivate_locked_super+0x9c/0xf0
   cleanup_mnt+0x9f/0x130
   task_work_run+0x131/0x250

The bug is currently masked by the grace period in namespace_unlock() for umount(2). Keep the necessary information until the filesystem private info is freed and free it with an rcu delay.

ntfs3_free_sbi() now runs from an rcu callback and drops the upcase table via ntfs_put_shared(). So switch s_shared_lock over to spin_lock_bh(). Otherwise the callback can deadlock against a task holding the lock on the same cpu.

Fixes a3a956c78efa ("fs/ntfs3: Add option "nocase"")
[ sashal: Adjusted ntfs_put_super() context because the early mount-options cleanup block was already absent. ]
---
 fs/ntfs3/ntfs_fs.h |  1 +
 fs/ntfs3/super.c   | 29 ++++++++++++++++++++++-------
 2 files changed, 23 insertions(+), 7 deletions(-)
fs/ntfs3/ntfs_fs.h +1 -0
index 11be9a6f041cf..934dcb136d6da 100644
@@ -209,6 +209,7 @@ struct ntfs_index {
 /* Ntfs file system in-core superblock data. */
 struct ntfs_sb_info {
 	struct super_block *sb;
+	struct rcu_head rcu;
 
 	u32 discard_granularity;
 	u64 discard_granularity_mask_inv; // ~(discard_granularity_mask_inv-1)
fs/ntfs3/super.c +22 -7
index 4b1941e3f3a67..c7c1896acc2c5 100644
@@ -187,7 +187,7 @@ void *ntfs_set_shared(void *ptr, u32 bytes)
 	void *ret = NULL;
 	int i, j = -1;
 
-	spin_lock(&s_shared_lock);
+	spin_lock_bh(&s_shared_lock);
 	for (i = 0; i < ARRAY_SIZE(s_shared); i++) {
 		if (!s_shared[i].cnt) {
 			j = i;
@@ -205,7 +205,7 @@ void *ntfs_set_shared(void *ptr, u32 bytes)
 		s_shared[j].cnt = 1;
 		ret = ptr;
 	}
-	spin_unlock(&s_shared_lock);
+	spin_unlock_bh(&s_shared_lock);
 
 	return ret;
 }
@@ -222,7 +222,7 @@ void *ntfs_put_shared(void *ptr)
 	void *ret = ptr;
 	int i;
 
-	spin_lock(&s_shared_lock);
+	spin_lock_bh(&s_shared_lock);
 	for (i = 0; i < ARRAY_SIZE(s_shared); i++) {
 		if (s_shared[i].cnt && s_shared[i].ptr == ptr) {
 			if (--s_shared[i].cnt)
@@ -230,7 +230,7 @@ void *ntfs_put_shared(void *ptr)
 			break;
 		}
 	}
-	spin_unlock(&s_shared_lock);
+	spin_unlock_bh(&s_shared_lock);
 
 	return ret;
 }
@@ -630,6 +630,8 @@ static noinline void ntfs3_put_sbi(struct ntfs_sb_info *sbi)
 
 static void ntfs3_free_sbi(struct ntfs_sb_info *sbi)
 {
+	if (sbi->options)
+		put_mount_options(sbi->options);
 	kfree(sbi->new_rec);
 	kvfree(ntfs_put_shared(sbi->upcase));
 	kvfree(sbi->def_table);
@@ -641,6 +643,11 @@ static void ntfs3_free_sbi(struct ntfs_sb_info *sbi)
 	kfree(sbi);
 }
 
+static void ntfs3_free_sbi_rcu(struct rcu_head *head)
+{
+	ntfs3_free_sbi(container_of(head, struct ntfs_sb_info, rcu));
+}
+
 static void ntfs_put_super(struct super_block *sb)
 {
 	struct ntfs_sb_info *sbi = sb->s_fs_info;
@@ -657,6 +664,12 @@ static void ntfs_put_super(struct super_block *sb)
 
 	/* Mark rw ntfs as clear, if possible. */
 	ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
+
+	/*
+	 * The mount options stay until the sbi is freed: ->d_hash() and
+	 * ->d_compare() of a pathwalk in rcu mode read the nls table through
+	 * them.
+	 */
 	ntfs3_put_sbi(sbi);
 }
 
@@ -1806,9 +1819,11 @@ static void ntfs3_kill_sb(struct super_block *sb)
 
 	kill_block_super(sb);
 
-	if (sbi->options)
-		put_mount_options(sbi->options);
-	ntfs3_free_sbi(sbi);
+	/*
+	 * A pathwalk in rcu mode may still be in ->d_hash() or ->d_compare()
+	 * and read the upcase table and the nls table through the sbi.
+	 */
+	call_rcu(&sbi->rcu, ntfs3_free_sbi_rcu);
 }
 
 // clang-format off
Signature · 1 line
2.53.0