6.1.185-rc1 review
Greg Kroah-Hartman
maintainercover 6.1.185-rc1 review
This is the start of the stable review cycle for the 6.1.185 release. There are 79 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 Thu, 27 Aug 2026 13:25:02 +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.1.185-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.1.y
and the diffstat can be found below.
thanks,
greg k-h
-------------
Pseudo-Shortlog of commits:
Greg Kroah-Hartman <[email protected]>
Linux 6.1.185-rc1
Chengfeng Ye <[email protected]>
Bluetooth: hci_event: fix LE list UAF on reset
Michael Bommarito <[email protected]>
HID: hyperv: validate initial device info bounds
Haoxiang Li <[email protected]>
HID: sensor: custom: Fix use-after-free in enable_sensor
Jann Horn <[email protected]>
HID: core: fix number/pointer type confusion on long items
Ibrahim Hashimov <[email protected]>
HID: nintendo: fix out-of-bounds read in joycon_ctlr_read_handler()
Sukrut Bellary <[email protected]>
misc: fastrpc: Fix double free of 'buf' in error path
Oliver Hartkopp <[email protected]>
can: isotp: fix timer drain order, wakeup handling and tx_gen ordering
Oliver Hartkopp <[email protected]>
can: use skb hash instead of private variable in headroom
Donglin Lyu <[email protected]>
Input: atkbd - skip deactivate for HONOR ZQC-P
Cryolitia PukNgae <[email protected]>
Input: atkbd - skip deactivate for HONOR FMB-P's internal keyboard
Eric Farman <[email protected]>
s390/vfio_ccw: Free all memory if cp_init() fails
Xiang Mei (Microsoft) <[email protected]>
xfrm: fix sk_dst_cache double-free in xfrm_user_policy()
Baul Lee <[email protected]>
HID: core: fix OOB read of field->usage in hid_set_field()
Lee Jones <[email protected]>
HID: magicmouse: Prevent out-of-bounds (OOB) read during DOUBLE_REPORT_ID
Jose Villaseñor Montfort <[email protected]>
HID: magicmouse: do not keep a stale msc->input if no input is claimed
Qing Luo <[email protected]>
mptcp: pm: fix data race in add_addr timer callback
Matthieu Baerts (NGI0) <[email protected]>
mptcp: pm: ADD_ADDR rtx: free sk if last
Matthieu Baerts (NGI0) <[email protected]>
mptcp: pm: ADD_ADDR rtx: always decrease sk refcount
Dawid Wróbel <[email protected]>
ASoC: codecs: lpass-tx-macro: Fix enum kcontrol accesses
Linmao Li <[email protected]>
Input: byd - synchronize timer deletion before freeing private data
Greg Kroah-Hartman <[email protected]>
nvmet-tcp: Do not WARN on remotely-controlled oversized SGL allocations
Jiang HongHui <[email protected]>
nvmet-fc: fix invalid free in LS IOD error path
Bryam Vargas <[email protected]>
nvmet-auth: zero the AUTH_RECEIVE response buffer
Luxiao Xu <[email protected]>
ipv6: fix use-after-free in ip6_finish_output2()
Yong Wang <[email protected]>
ipv4: reject undersized MTUs in ip_do_fragment()
Pavitra Jha <[email protected]>
libceph: fix OOB read in decode_watchers() via missing bounds check
Eric Dumazet <[email protected]>
ndisc: ndisc_send_redirect() cleanup
Linmao Li <[email protected]>
nfc: nci: free destination parameters when closing a connection
Samuel Page <[email protected]>
nfc: nci: fix uninit-value in the RF discover/activated NTF handlers
Samuel Page <[email protected]>
nfc: nci: fix out-of-bounds write in nci_target_auto_activated()
Doruk Tan Ozturk <[email protected]>
nfc: st21nfca: validate ATR_REQ length against the received frame
Xu Rao <[email protected]>
nfc: pn533: purge fragmented skbs during cleanup
Doruk Tan Ozturk <[email protected]>
nfc: llcp: reject PDUs shorter than the LLCP header
Muhammad Bilal <[email protected]>
nfc: llcp: fix OOB read and u8 offset wrap in TLV parsers
Doruk Tan Ozturk <[email protected]>
nfc: llcp: bound the connect_sn TLV walk to the skb
Pengpeng Hou <[email protected]>
nfc: microread: validate target discovery payload lengths
Bryam Vargas <[email protected]>
nfc: fdp: bound the device-reported read length and fix an skb leak
Doruk Tan Ozturk <[email protected]>
nfc: digital: clamp SENSF_RES length to the destination buffer
Joanne Koong <[email protected]>
iomap: adjust read range correctly for non-block-aligned positions
Jakub Kicinski <[email protected]>
tls: handle data disappearing from under the TLS ULP
Sabrina Dubroca <[email protected]>
tls: fix lockless read of strp->msg_ready in ->poll
Junjie Cao <[email protected]>
gpio: ml-ioh: use raw_spinlock_t for the register lock
Yang Wang <[email protected]>
drm/amdgpu: check ASPM on the dGPU host link
Ibrahim Hashimov <[email protected]>
xfs: bounds-check buffer log item's dirty bitmap
Christoph Hellwig <[email protected]>
xfs: don't use a xfs_log_iovec for ri_buf in log recovery
Eric Farman <[email protected]>
s390/vfio_ccw: Implement a crw lock
Eric Farman <[email protected]>
s390/vfio_ccw: Selectively expand io_mutex
Eric Farman <[email protected]>
s390/vfio_ccw: Move cp cleanup out of not operational
Junrui Luo <[email protected]>
drm/amdgpu: disallow multiple FENCE chunks in one submit
Eric Farman <[email protected]>
s390/vfio_ccw: Cancel existing workqueues
Pei Xiao <[email protected]>
mmc: atmel-mci: Fix use-after-free in atmci_remove due to race condition
Zhiling Zou <[email protected]>
inet: frags: publish queues before arming timer
Zihan Xi <[email protected]>
packet: synchronize pressure clearing with ring reconfiguration
Eric Dumazet <[email protected]>
net/packet: convert po->pressure to an atomic flag
Luca Fresi <[email protected]>
serial: sc16is7xx: enable THRI before filling TX FIFO
Hugo Villeneuve <[email protected]>
serial: sc16is7xx: use guards for simple mutex locks
Hugo Villeneuve <[email protected]>
serial: sc16is7xx: rename EFR mutex with generic name
Lech Perczak <[email protected]>
serial: sc16is7xx: convert bitmask definitions to use BIT() macro
Lech Perczak <[email protected]>
serial: sc16is7xx: fix copy-paste errors in EFR_SWFLOWx_BIT constants
Qihang Tang <[email protected]>
packet: use consistent hard_header_len in TX_RING send path
Qihang Tang <[email protected]>
packet: use consistent hard_header_len in non-ring send paths
Aditya Chillara <[email protected]>
perf/core: Fix group leader use-after-free after sibling detach
Yeoreum Yun <[email protected]>
perf: Fix dangling cgroup pointer in cpuctx
Peter Zijlstra <[email protected]>
perf: Fix cgroup state vs ERROR
Yeoreum Yun <[email protected]>
perf/core: Fix child_total_time_enabled accounting bug at task exit
Fan Wu <[email protected]>
serial: amba-pl011: synchronize DMA teardown
Koichiro Den <[email protected]>
NTB: ntb_netdev: Preserve RX queue depth on allocation failure
Ekansh Gupta <[email protected]>
misc: fastrpc: Remove buffer from list prior to unmap operation
Abel Vesa <[email protected]>
misc: fastrpc: Rework fastrpc_req_munmap
Hongling Zeng <[email protected]>
xfs: validate attr entry pointer before field access
Guanghui Yang <[email protected]>
ext4: clear error before retrying inode xattr space fallback
Matthias Goergens <[email protected]>
ext4: stop retrying saturated xattr cache entries
Tetsuo Handa <[email protected]>
kcov: fix data corruption and race conditions on PREEMPT_RT
Rik van Riel <[email protected]>
null_blk: fix UBSAN shift-out-of-bounds when zone_size is 0 or overflows
Ian Bridges <[email protected]>
ocfs2: fix missing metadata reservation for large xattrs
Takashi Iwai <[email protected]>
ALSA: dummy: Check card index validity at probe
Griffin Kroah-Hartman <[email protected]>
rndis_host: add overflow check in rndis_rx_fixup()
Ali Ahmet Memis <[email protected]>
Bluetooth: RFCOMM: take rfcomm_mutex for the deferred setup accept
Steffen Persvold <[email protected]>
PCI: host-generic: Fix NULL pointer dereference on 32-bit CAM systems
-------------
Diffstat:
Makefile | 4 +- drivers/block/null_blk/zoned.c | 10 +- drivers/gpio/gpio-ml-ioh.c | 36 ++-- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 4 + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 50 ++++- drivers/hid/hid-core.c | 11 +- drivers/hid/hid-hyperv.c | 27 ++- drivers/hid/hid-magicmouse.c | 26 +++ drivers/hid/hid-nintendo.c | 7 +- drivers/hid/hid-sensor-custom.c | 21 +- drivers/input/keyboard/atkbd.c | 23 ++- drivers/input/mouse/byd.c | 2 +- drivers/misc/fastrpc.c | 64 +++--- drivers/mmc/host/atmel-mci.c | 2 + drivers/net/can/dev/skb.c | 2 - drivers/net/ntb_netdev.c | 15 +- drivers/net/usb/rndis_host.c | 6 +- drivers/nfc/fdp/i2c.c | 27 +++ drivers/nfc/microread/microread.c | 31 ++- drivers/nfc/pn533/pn533.c | 1 + drivers/nfc/st21nfca/dep.c | 3 + drivers/nvme/target/fabrics-cmd-auth.c | 2 +- drivers/nvme/target/fc.c | 2 +- drivers/nvme/target/tcp.c | 5 +- drivers/pci/controller/pci-host-generic.c | 11 +- drivers/pci/ecam.c | 13 ++ drivers/s390/cio/vfio_ccw_chp.c | 26 ++- drivers/s390/cio/vfio_ccw_cp.c | 30 ++- drivers/s390/cio/vfio_ccw_drv.c | 26 ++- drivers/s390/cio/vfio_ccw_fsm.c | 8 +- drivers/s390/cio/vfio_ccw_ops.c | 27 +++ drivers/s390/cio/vfio_ccw_private.h | 9 +- drivers/tty/serial/amba-pl011.c | 8 +- drivers/tty/serial/sc16is7xx.c | 222 ++++++++++---------- fs/ext4/xattr.c | 7 +- fs/iomap/buffered-io.c | 19 +- fs/ocfs2/xattr.c | 18 +- fs/xfs/libxfs/xfs_attr_leaf.c | 14 ++ fs/xfs/libxfs/xfs_log_recover.h | 4 +- fs/xfs/xfs_attr_item.c | 24 +-- fs/xfs/xfs_bmap_item.c | 18 +- fs/xfs/xfs_buf_item.c | 8 +- fs/xfs/xfs_buf_item.h | 2 +- fs/xfs/xfs_buf_item_recover.c | 91 +++++--- fs/xfs/xfs_dquot_item_recover.c | 20 +- fs/xfs/xfs_extfree_item.c | 43 ++-- fs/xfs/xfs_icreate_item.c | 2 +- fs/xfs/xfs_inode_item.c | 6 +- fs/xfs/xfs_inode_item.h | 4 +- fs/xfs/xfs_inode_item_recover.c | 26 +-- fs/xfs/xfs_log_recover.c | 16 +- fs/xfs/xfs_refcount_item.c | 16 +- fs/xfs/xfs_rmap_item.c | 16 +- fs/xfs/xfs_trans.h | 1 - include/linux/can/core.h | 1 + include/linux/can/skb.h | 2 - include/linux/netdevice.h | 6 +- include/linux/pci-ecam.h | 3 + include/linux/sched.h | 8 + include/net/tls.h | 3 +- kernel/events/core.c | 121 +++++------ kernel/kcov.c | 90 ++++---- lib/Kconfig.debug | 5 +- net/bluetooth/hci_event.c | 2 + net/bluetooth/rfcomm/core.c | 24 ++- net/can/af_can.c | 14 +- net/can/bcm.c | 2 - net/can/isotp.c | 320 +++++++++++++++++++++-------- net/can/j1939/socket.c | 1 - net/can/j1939/transport.c | 2 - net/can/raw.c | 7 +- net/ceph/osd_client.c | 5 +- net/ipv4/inet_fragment.c | 6 +- net/ipv4/ip_output.c | 4 + net/ipv6/ip6_output.c | 2 + net/ipv6/ndisc.c | 8 +- net/mptcp/pm_netlink.c | 36 ++-- net/nfc/digital_technology.c | 2 + net/nfc/llcp_commands.c | 18 +- net/nfc/llcp_core.c | 15 +- net/nfc/nci/ntf.c | 10 +- net/nfc/nci/rsp.c | 1 + net/packet/af_packet.c | 79 ++++--- net/packet/internal.h | 2 +- net/tls/tls.h | 4 +- net/tls/tls_strp.c | 17 +- net/tls/tls_sw.c | 3 +- net/xfrm/xfrm_state.c | 4 +- sound/drivers/dummy.c | 6 + sound/soc/codecs/lpass-tx-macro.c | 4 +- 90 files changed, 1283 insertions(+), 670 deletions(-)
Greg Kroah-Hartman
maintainerPATCH 1/79 PCI: host-generic: Fix NULL pointer dereference on 32-bit CAM systems
6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Steffen Persvold <[email protected]>
commit 008cb88edb41f3c7c8e0ed763ff9f26719830984 upstream.
On 32-bit systems the config space is too large to ioremap in one go, so pci_ecam_create() maps each bus segment separately and relies on the ->add_bus callback (pci_ecam_add_bus) to populate the per-bus mapping in cfg->winp[]. pci_ecam_map_bus() then uses that mapping as the base for every config access.
The generic ECAM ops (pci_generic_ecam_ops) already provide the ->add_bus and ->remove_bus callbacks, but the CAM (legacy) ops in pci-host-generic.c do not. As a result, on a 32-bit host using "pci-host-cam-generic" the per-bus mapping is never set up and the first config read dereferences a NULL base, crashing during bus enumeration:
Unable to handle kernel NULL pointer dereference at virtual address 00000800 Oops [#1] CPU: 0 PID: 1 Comm: swapper Not tainted 6.9.7+ #43 Hardware name: Digilent Nexys-Video-A7 RV32 (DT) epc : pci_generic_config_read+0x40/0xb0 ra : pci_generic_config_read+0x2c/0xb0 [<c038db9c>] pci_generic_config_read+0x40/0xb0 [<c038da04>] pci_bus_read_config_dword+0x50/0xb0 [<c0391e94>] pci_bus_generic_read_dev_vendor_id+0x3c/0x1ec [<c039245c>] pci_scan_single_device+0xa4/0x11c [<c0392570>] pci_scan_slot+0x9c/0x23c [<c039388c>] pci_scan_child_bus_extend+0x58/0x2f4 [<c0393db0>] pci_scan_root_bus_bridge+0x64/0xe8 [<c0393e54>] pci_host_probe+0x20/0xc8 [<c03bc6f4>] pci_host_common_probe+0x144/0x1e4
Fix this by giving the CAM ops the same ->add_bus/->remove_bus callbacks. Since pci_ecam_add_bus() and pci_ecam_remove_bus() are static to ecam.c, move the CAM ops definition there as pci_generic_cam_ops (mirroring pci_generic_ecam_ops) and export it for pci-host-generic.c to reference.
--- drivers/pci/controller/pci-host-generic.c | 11 +---------- drivers/pci/ecam.c | 13 +++++++++++++ include/linux/pci-ecam.h | 3 +++ 3 files changed, 17 insertions(+), 10 deletions(-)
drivers/pci/controller/pci-host-generic.c +1 -10
@@ -14,15 +14,6 @@#include <linux/pci-ecam.h> #include <linux/platform_device.h> -static const struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = { - .bus_shift =16 , - .pci_ops = { - .map_bus = pci_ecam_map_bus, - .read = pci_generic_config_read, - .write = pci_generic_config_write, - } -}; -static bool pci_dw_valid_device (struct pci_bus *bus,unsigned int devfn) {struct pci_config_window *cfg = bus->sysdata;
@@ -58,7 +49,7 @@ static const struct pci_ecam_ops pci_dw_static const struct of_device_id gen_pci_of_match[] = { { .compatible ="pci-host-cam-generic" , - .data = &gen_pci_cfg_cam_bus_ops }, + .data = &pci_generic_cam_ops }, { .compatible ="pci-host-ecam-generic" , .data = &pci_generic_ecam_ops },
drivers/pci/ecam.c +13 -0
@@ -208,6 +208,19 @@ const struct pci_ecam_ops pci_generic_ec };EXPORT_SYMBOL_GPL (pci_generic_ecam_ops); +/* CAM ops */ +const struct pci_ecam_ops pci_generic_cam_ops = { + .bus_shift =16 , + .pci_ops = { + .add_bus = pci_ecam_add_bus, + .remove_bus = pci_ecam_remove_bus, + .map_bus = pci_ecam_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, + } +}; +EXPORT_SYMBOL_GPL (pci_generic_cam_ops); +#if defined(CONFIG_ACPI ) && defined(CONFIG_PCI_QUIRKS )/* ECAM ops for 32-bit access only (non-compliant) */ const struct pci_ecam_ops pci_32b_ops = {
include/linux/pci-ecam.h +3 -0
@@ -77,6 +77,9 @@ void __iomem *pci_ecam_map_bus(struct pc/* default ECAM ops */ extern const struct pci_ecam_ops pci_generic_ecam_ops; +/* default CAM ops */ +extern const struct pci_ecam_ops pci_generic_cam_ops; +#if defined(CONFIG_ACPI ) && defined(CONFIG_PCI_QUIRKS )extern const struct pci_ecam_ops pci_32b_ops;/* 32-bit accesses only */ extern const struct pci_ecam_ops pci_32b_read_ops;/* 32-bit read only */
Greg Kroah-Hartman
maintainerPATCH 2/79 Bluetooth: RFCOMM: take rfcomm_mutex for the deferred setup accept
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ali Ahmet Memis <[email protected]>
commit 43a556b2fd43f2df6dded59c2e26560a27874c24 upstream.
rfcomm_sock_recvmsg() completes a deferred setup by calling rfcomm_dlc_accept() without holding any RFCOMM lock:
if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
rfcomm_dlc_accept(d);
return 0;
}and rfcomm_dlc_accept() dereferences the session on its first line:
struct sock *sk = d->session->sock->sk;
Every other path that touches d->session runs under rfcomm_mutex:rfcomm_dlc_open(), rfcomm_dlc_close(), rfcomm_dlc_exists(), rfcomm_dlc_send_rpn(), and the RFCOMM thread through rfcomm_process_sessions(). rfcomm_connect_ind() is even documented as "called under rfcomm_lock()". This call site is the only one that skips it.
The RFCOMM_DEFER_SETUP bit looks like it serialises the accept against teardown, since __rfcomm_dlc_close() returns early when it wins the test_and_clear. But rfcomm_recv_disc() forces the state first:
d->state = BT_CLOSED; __rfcomm_dlc_close(d, err);
and the early return only covers BT_CONNECT, BT_CONFIG, BT_OPEN and BT_CONNECT2. With the state already BT_CLOSED that switch does not match, the bit is never consulted, and __rfcomm_dlc_close() falls through to rfcomm_dlc_unlink(), which sets d->session = NULL.
So a remote DISC on a deferred dlc clears the session while leaving RFCOMM_DEFER_SETUP set. The next recvmsg() then passes the test_and_clear and dereferences a NULL session. No timing window is
needed: once the DISC has been processed, the dereference is unconditional.
Give rfcomm_dlc_accept() the same shape as rfcomm_dlc_open() and rfcomm_dlc_close(): an exported wrapper that takes rfcomm_mutex and re-checks the session, around a __rfcomm_dlc_accept() that the two in-core callers, which already hold the mutex, keep using.
Reproduced on a KASAN + PROVE_LOCKING kernel with a BR/EDR peer emulated over /dev/vhci: the peer brings up an ACL link, opens L2CAP on the RFCOMM PSM, starts a session, opens a dlc on a channel bound with BT_DEFER_SETUP, and sends DISC after the socket is accepted. recv() on the accepted socket then hits:
Oops: general protection fault
KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017]
RIP: 0010:rfcomm_dlc_accept+0x54/0x350
Call Trace:
rfcomm_sock_recvmsg+0x1cd/0x230
sock_recvmsg+0x166/0x1c0
__sys_recvfrom+0x20d/0x3000x10 is the offset of sock in struct rfcomm_session. With this patch the same run completes with recv() returning 0 and no report, and lockdep stays quiet, confirming rfcomm_mutex is still taken before lock_sock on this path as it is on the thread side.
--- net/bluetooth/rfcomm/core.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-)
net/bluetooth/rfcomm/core.c +21 -3
@@ -1330,7 +1330,10 @@ static struct rfcomm_session *rfcomm_recreturn s; } -void rfcomm_dlc_accept (struct rfcomm_dlc *d) +/* Must be called with rfcomm_mutex held, so that the session cannot be +* unlinked from under us. +*/ +static void __rfcomm_dlc_accept (struct rfcomm_dlc *d) {struct sock *sk = d->session->sock->sk;struct l2cap_conn *conn =l2cap_pi (sk)->chan->conn;
@@ -1352,6 +1355,21 @@ void rfcomm_dlc_accept(struct rfcomm_dlcrfcomm_send_msc (d->session,1 , d->dlci, d->v24_sig); } +void rfcomm_dlc_accept (struct rfcomm_dlc *d) +{ +rfcomm_lock (); + +/* rfcomm_recv_disc() sets the dlc state to BT_CLOSED before calling +* __rfcomm_dlc_close(), so the RFCOMM_DEFER_SETUP handshake there is +* skipped and the session can already be unlinked by the time the +* deferred accept runs from rfcomm_sock_recvmsg(). +*/ +if (d->session) +__rfcomm_dlc_accept (d); + +rfcomm_unlock (); +} +static void rfcomm_check_accept (struct rfcomm_dlc *d) {if (rfcomm_check_security (d)) {
@@ -1364,7 +1382,7 @@ static void rfcomm_check_accept(struct r d->state_change (d,0 );rfcomm_dlc_unlock (d); }else -rfcomm_dlc_accept (d); +__rfcomm_dlc_accept (d); }else {set_bit (RFCOMM_AUTH_PENDING , &d->flags);rfcomm_dlc_set_timer (d,RFCOMM_AUTH_TIMEOUT );
@@ -1949,7 +1967,7 @@ static void rfcomm_process_dlcs(struct r d->state_change (d,0 );rfcomm_dlc_unlock (d); }else -rfcomm_dlc_accept (d); +__rfcomm_dlc_accept (d); }continue ; }else if (test_and_clear_bit (RFCOMM_AUTH_REJECT , &d->flags)) {
Greg Kroah-Hartman
maintainerPATCH 3/79 rndis_host: add overflow check in rndis_rx_fixup()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Griffin Kroah-Hartman <[email protected]>
commit 965a251f23ff69cfb4486974d4532e9bb551c7fc upstream.
Add an overflow check to ensure that data_offset + data_len + 8 does not wrap, which would enable an OOB read of the USB data buffer.
--- drivers/net/usb/rndis_host.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/net/usb/rndis_host.c +5 -1
@@ -14,6 +14,7 @@#include <linux/usb/cdc.h> #include <linux/usb/usbnet.h> #include <linux/usb/rndis_host.h> +#include <linux/overflow.h> /*
@@ -506,6 +507,7 @@ int rndis_rx_fixup(struct usbnet *dev, sstruct rndis_data_hdr *hdr = (void *)skb->data;struct sk_buff *skb2;u32 msg_type, msg_len, data_offset, data_len; +u32 overflow_check; msg_type =le32_to_cpu (hdr->msg_type); msg_len =le32_to_cpu (hdr->msg_len);
@@ -514,7 +516,9 @@ int rndis_rx_fixup(struct usbnet *dev, s/* don't choke if we see oob, per-packet data, etc */ if (unlikely (msg_type !=RNDIS_MSG_PACKET || skb->len < msg_len - || (data_offset + data_len +8 ) > msg_len)) { + || (data_offset + data_len +8 ) > msg_len + ||check_add_overflow (data_offset, data_len, &overflow_check) + ||check_add_overflow (overflow_check,8 , &overflow_check))) { dev->net->stats.rx_frame_errors++; netdev_dbg(dev->net, "bad rndis message %d/%d/%d/%d, len %d\n", le32_to_cpu(hdr->msg_type),
Greg Kroah-Hartman
maintainerPATCH 4/79 ALSA: dummy: Check card index validity at probe
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
commit 02442d5fe8ee365a084b055d4fa81a0c1abfc3fd upstream.
snd_dummy_probe() blindly trusts that the given devptr->id value is within the proper card index range. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.
Add a sanity check for the card index and warn/correct it if it's a value out of the range.
--- sound/drivers/dummy.c | 6 ++++++ 1 file changed, 6 insertions(+)
sound/drivers/dummy.c +6 -0
@@ -1033,6 +1033,12 @@ static int snd_dummy_probe(struct platfoint idx, err;int dev = devptr->id; +if (dev <0 || dev >=SNDRV_CARDS ) { +dev_warn (&devptr->dev, +"Invalid card index %d, using default 0\n" , dev); + dev =0 ; + } + err =snd_devm_card_new (&devptr->dev, index[dev], id[dev],THIS_MODULE ,sizeof (struct snd_dummy ), &card);if (err <0 )
Greg Kroah-Hartman
maintainerPATCH 5/79 ocfs2: fix missing metadata reservation for large xattrs
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ian Bridges <[email protected]>
commit 0cdc7dde00ec63ac714271fa8b2918d630b8da1a upstream.
[BUG]lsetxattr() panics the kernel when setting a large xattr value on a fragmented filesystem where the file already has an external xattr block.
[CAUSE]ocfs2_calc_xattr_set_need() never reserves metadata blocks for a new xattr value's extent tree when the file already has an external xattr block. The not_found path leaves meta_add at zero, so meta_ac is NULL when ocfs2_xattr_extend_allocation() runs.
A new value root has room for a single extent record. On a fragmented filesystem, the allocator cannot satisfy the xattr value in one contiguous run, so each non-contiguous run requires its own extent record. When the value root's extent list is full and meta_ac is NULL, ocfs2_add_clusters_in_btree() returns RESTART_META, and ocfs2_xattr_extend_allocation() hits BUG_ON(why == RESTART_META).
[FIX]
The case where no xattr block exists yet already calls ocfs2_extend_meta_needed(&def_xv.xv.xr_list) to reserve value tree metadata. Add the same reservation to the case where an xattr block already exists, making the two cases consistent.
Replace the BUG_ON with a -ENOSPC return so that if RESTART_META is returned despite the reservation, the error propagates to userspace instead of panicking the kernel.
--- fs/ocfs2/xattr.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-)
fs/ocfs2/xattr.c +12 -6
@@ -740,12 +740,10 @@ static int ocfs2_xattr_extend_allocation prev_clusters;if (why !=RESTART_NONE && clusters_to_add) { -/* -* We can only fail in case the alloc file doesn't give -* up enough clusters. -*/ -BUG_ON (why ==RESTART_META ); - +if (why ==RESTART_META ) { + status = -ENOSPC ; +break ; + } credits =ocfs2_calc_extend_credits (inode->i_sb, &vb->vb_xv->xr_list); status =ocfs2_extend_trans (handle, credits);
@@ -3215,6 +3213,14 @@ meta_guess: credits +=OCFS2_SUBALLOC_ALLOC +1 ;/* +* Reserve metadata for the new xattr's value extent tree. +* The not_found path above adds credits for this tree but +* omits meta_add, leaving meta_ac NULL for large values. +*/ +if (xi->xi_value_len >OCFS2_XATTR_INLINE_SIZE ) + meta_add +=ocfs2_extend_meta_needed (&def_xv.xv.xr_list); + +/* * This cluster will be used either for new bucket or for * new xattr block. * If the cluster size is the same as the bucket size, one
Greg Kroah-Hartman
maintainerPATCH 6/79 null_blk: fix UBSAN shift-out-of-bounds when zone_size is 0 or overflows
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rik van Riel <[email protected]>
commit 95491fb05105b61050cb623a5e0227eb26aa3525 upstream.
null_zone_no() does sect >> ilog2(dev->zone_size_sects). When zone_size_sects is 0, ilog2(0) returns -1, producing shift exponent -1 which UBSAN reports as shift-out-of-bounds.
UBSAN: shift-out-of-bounds in drivers/block/null_blk/zoned.c:21:14 shift exponent -1 is negative Call Trace: null_zone_no drivers/block/null_blk/zoned.c:21 [inline] null_process_zoned_cmd+0xf76/0xf80 drivers/block/null_blk/zoned.c:728 null_handle_cmd drivers/block/null_blk/main.c:1455 [inline] null_queue_rq+0x8bc/0xe70 drivers/block/null_blk/main.c:1703 __blk_mq_issue_directly block/blk-mq.c:2694 [inline] blk_mq_try_issue_directly+0x3f4/0x880 block/blk-mq.c:2754 blk_mq_submit_bio+0x20c0/0x2a40 block/blk-mq.c:3208 submit_bio_noacct_nocheck+0x2f4/0xa40 block/blk-core.c:790 block_read_full_folio+0x7a6/0x810 fs/buffer.c:2463 filemap_read_folio+0x12c/0x3a0 mm/filemap.c:2510 read_part_sector+0xb6/0x2b0 block/partitions/core.c:724 adfspart_check_ICS+0xb1/0x960 block/partitions/acorn.c:357 check_partition block/partitions/core.c:143 [inline] blk_add_partitions block/partitions/core.c:591 [inline] bdev_disk_changed+0x851/0x17a0 block/partitions/core.c:695 blkdev_get_whole+0x372/0x510 block/bdev.c:751 add_disk_final block/genhd.c:412 [inline] add_disk_fwnode+0x24b/0x3a0 block/genhd.c:606 null_add_dev+0x130b/0x1d70 drivers/block/null_blk/main.c:2052 nullb_device_power_store+0x240/0x380 drivers/block/null_blk/main.c:501 configfs_write_iter+0x337/0x430 fs/configfs/file.c:229
Syzkaller triggers this by creating a zoned null_blk device via configfs. The Call Trace shows configfs_write_iter in configfs/file.c handling a write to power file, which calls nullb_device_power_store in main.c, which calls null_add_dev in main.c, which calls add_disk in genhd.c, which triggers partition scan via bdev_disk_changed in partitions/core.c.
A zoned null_blk device with zone_size 0 should not be legal. Existing code tries to reject it via is_power_of_2() check in zoned.c and !zone_size check in main.c, but syzkaller can still reach null_zone_no() with zone_size_sects 0 via two paths:
1. Direct 0 via configfs: zone_size attribute store in main.c has NULLB_DEVICE_ATTR(zone_size, ulong, NULL) with no validation callback, so echo 0 > zone_size succeeds before power store. If zoned is false at power store time, the !zone_size check in main.c is skipped, and later zoned set true leaves zone_size 0.
2. Large value overflow: mb_to_sects() in zoned.c does (sector_t)mb * SZ_1M >> SECTOR_SHIFT which is mb * 2048. If mb is 1UL << 53 (9PB), mb * 2048 overflows 64-bit to 0. The value is power-of-two so is_power_of_2() passes, but mb_to_sects() returns 0.
Check for zero zone_size explicitly in null_init_zoned_dev() in zoned.c, returning -EINVAL with "must be non-zero power-of-two". Check for zero zone_size_sects after mb_to_sects() conversion, returning -EINVAL for overflow case. Keep defensive check in null_zone_no() returning 0 for zero sectors to avoid shift out-of-bounds even if zero slips through.
This change should be safe because zone_size is set once in null_init_zoned_dev() under device lock and never changes after, and 0 is never valid for a zoned device. Returning -EINVAL at init time fails device creation early with clear error, while defensive return 0 in null_zone_no() makes zoned command fail via offline zone check. No new locking is introduced.
--- drivers/block/null_blk/zoned.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/block/null_blk/zoned.c +8 -2
@@ -16,6 +16,8 @@ static inline sector_t mb_to_sects(unsigstatic inline unsigned int null_zone_no (struct nullb_device *dev,sector_t sect) { +if (WARN_ON_ONCE (!dev->zone_size_sects)) +return 0 ;return sect >>ilog2 (dev->zone_size_sects); }
@@ -65,8 +67,8 @@ int null_init_zoned_dev(struct nullb_devsector_t sector =0 ;unsigned int i; -if (!is_power_of_2 (dev->zone_size)) { -pr_err ("zone_size must be power-of-two\n" ); +if (!dev->zone_size || !is_power_of_2 (dev->zone_size)) { +pr_err ("zone_size must be non-zero power-of-two\n" );return -EINVAL ; }if (dev->zone_size > dev->size) {
@@ -97,6 +99,10 @@ int null_init_zoned_dev(struct nullb_dev zone_capacity_sects =mb_to_sects (dev->zone_capacity); dev_capacity_sects =mb_to_sects (dev->size); dev->zone_size_sects =mb_to_sects (dev->zone_size); +if (!dev->zone_size_sects) { +pr_err ("zone_size too large or too small, leads to zero sectors\n" ); +return -EINVAL ; + } dev->nr_zones =round_up (dev_capacity_sects, dev->zone_size_sects) >>ilog2 (dev->zone_size_sects);
Greg Kroah-Hartman
maintainerPATCH 7/79 kcov: fix data corruption and race conditions on PREEMPT_RT
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tetsuo Handa <[email protected]>
commit 2eed77fdcb0cc48e8eccb2bcd4b7f2c6d650e84c upstream.
syzbot is reporting KCOV state corruption on PREEMPT_RT kernels, for the temporary storage used for saving/restoring remote KCOV state is currently allocated as the per-CPU area.
On PREEMPT_RT kernels, softirq handlers run as preemptible task threads (e.g., ksoftirqd). If a softirq context preempts a task running a remote KCOV session, it safely saves the task's state into the per-CPU area. However, if that softirq thread is subsequently preempted by a higher- priority softirq thread on the same CPU, the second softirq will overwrite the same per-CPU area, permanently destroying the original task's KCOV state.
Fix this data corruption by moving the temporary storage from the per-CPU area to the per-thread area. Since each softirq thread now owns its own task context, nested softirq preemption no longer causes data overwrites.
Note that while the temporary storage is now on a per-thread basis, the per-CPU kcov_percpu_data.lock must be retained, for we need to ensure that kcov_remote_start() and kcov_remote_stop() operate atomically without racing against asynchronous interrupts that manipulate the current task's KCOV state.
It is likely that GFP_KERNEL allocation by vmalloc_node() in kcov_init() has already called panic() before returning NULL, for there will be no OOM-killable userspace processes when __init function of built-in module runs. But this patch also fixes crashing the kernel when vmalloc_node() in kcov_init() returned NULL, for kcov_init() left per-CPU irq_area == NULL but kcov_remote_start() depends on per-CPU irq_area != NULL, resulting in
(1) doing vmalloc() in kcov_remote_start() despite !in_task() context
(2) out-of-array-bounds access if (1) succeeded but
kcov->remote_size < CONFIG_KCOV_IRQ_AREA_SIZE
(3) always leak memory allocated by (1), eventually killing all OOM-killable userspace processes
problems.
--- include/linux/sched.h | 8 ++++ kernel/kcov.c | 90 ++++++++++++++++++++++++++------------------------ lib/Kconfig.debug | 5 +- 3 files changed, 58 insertions(+), 45 deletions(-)
include/linux/sched.h +8 -0
@@ -1428,6 +1428,14 @@ struct task_struct {/* Collect coverage from softirq context: */ unsigned int kcov_softirq; + +/* Temporary storage for preempting remote coverage collection: */ +unsigned int kcov_saved_mode; +unsigned int kcov_saved_size; +void *kcov_saved_area; +struct kcov *kcov_saved_kcov; +int kcov_saved_sequence; +#endif #ifdef CONFIG_MEMCG
kernel/kcov.c +47 -43
169 lines not on this page · raw message
lib/Kconfig.debug +3 -2
@@ -2044,10 +2044,11 @@ config KCOV_INSTRUMENT_ALL config KCOV_IRQ_AREA_SIZE hex "Size of interrupt coverage collection area in words" depends on KCOV + range 0x80 0x1000000 default 0x40000 help - KCOV uses preallocated per-cpu areas to collect coverage from - soft interrupts. This specifies the size of those areas in the + KCOV uses preallocated areas to collect coverage from soft + interrupts. This specifies the size of those areas in the number of unsigned long words. menuconfig RUNTIME_TESTING_MENU
Greg Kroah-Hartman
maintainerPATCH 8/79 ext4: stop retrying saturated xattr cache entries
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matthias Goergens <[email protected]>
commit 54b6bd40898de7906acb2bccc9a96d1b8e6b4323 upstream.
ext4_xattr_block_set() retries when a cache entry selected for reuse has a saturated reference count after taking the buffer lock. The retry returns to the mbcache lookup without making that entry ineligible, so it can select the same unusable entry indefinitely. A task spinning there can hold the parent directory's i_rwsem and leave concurrent rmdir callers blocked.
Normally a reusable entry has a reference count below EXT4_XATTR_REFCOUNT_MAX because the count and MBE_REUSABLE_B are updated under the same buffer lock. A corrupted filesystem can violate that invariant. The syzbot reproducer reports allocator and xattr corruption before triggering this retry loop.
Check the untrusted on-disk count before incrementing it, avoiding overflow, and clear MBE_REUSABLE_B when it is already saturated. The next lookup then skips the entry that was just proven unusable. This mirrors the normal transition at EXT4_XATTR_REFCOUNT_MAX; the release path marks the entry reusable again on the exact 1024-to-1023 transition.
Using the same QEMU harness and guest parameters, current unpatched Linux hung in 6 of 8 420-second trials with the do_rmdir signature; representative NMI backtraces caught the owner spinning in ext4_xattr_block_set(). The patched kernel completed 28 of 28 trials without a hung-task report; the final twelve trials exercised the reviewed overflow-safe form of the change. syzbot's patch testing also completed without reproducing the hang.
--- fs/ext4/xattr.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
fs/ext4/xattr.c +4 -2
@@ -2017,12 +2017,13 @@ inserted:* stable so we can check the additional * reference fits. */ - ref =le32_to_cpu (BHDR (new_bh)->h_refcount) +1 ; -if (ref >EXT4_XATTR_REFCOUNT_MAX ) { + ref =le32_to_cpu (BHDR (new_bh)->h_refcount); +if (ref >=EXT4_XATTR_REFCOUNT_MAX ) {/* * Undo everything and check mbcache * again. */ +clear_bit (MBE_REUSABLE_B , &ce->e_flags);unlock_buffer (new_bh); dquot_free_block(inode, EXT4_C2B(EXT4_SB(sb),
@@ -2033,6 +2034,7 @@ inserted: new_bh =NULL ; goto inserted; } + ref++;BHDR (new_bh)->h_refcount =cpu_to_le32 (ref);if (ref ==EXT4_XATTR_REFCOUNT_MAX )clear_bit (MBE_REUSABLE_B , &ce->e_flags);
Greg Kroah-Hartman
maintainerPATCH 9/79 ext4: clear error before retrying inode xattr space fallback
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guanghui Yang <[email protected]>
commit 409a7f12a0933ff2c617fa814c76cef0bd1d457a upstream.
When ext4_xattr_make_inode_space() returns -ENOSPC, ext4_expand_extra_isize_ea() can retry the expansion with s_min_extra_isize. If that retry succeeds by finding enough ibody free space, control jumps directly to the shift label.
The previous -ENOSPC is still stored in error in that path, so the function can update i_extra_isize but still return -ENOSPC to the caller. Clear error before retrying so a successful fallback expansion returns success.
Reproduced with an ext4 image using 1 KiB blocks, project quota support, 256-byte inodes, and min_extra_isize/want_extra_isize set to 32. FS_IOC_FSSETXATTR failures dropped from 802 to 86 after the fix.
--- fs/ext4/xattr.c | 1 + 1 file changed, 1 insertion(+)
fs/ext4/xattr.c +1 -0
@@ -2783,6 +2783,7 @@ retry: s_min_extra_isize) { tried_min_extra_isize++; new_extra_isize = s_min_extra_isize; + error =0 ; goto retry; } goto cleanup;
Greg Kroah-Hartman
maintainerPATCH 10/79 xfs: validate attr entry pointer before field access
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hongling Zeng <[email protected]>
commit b7eea80be25f3334f131d52982b3131aba77b97d upstream.
xfs_attr3_leaf_verify_entry() accesses lentry/rentry fields (namelen, valuelen) before checking if the entry pointer itself is within bounds. If nameidx is crafted to point near the end of the buffer, these field accesses can read out-of-bounds before the bounds check at name_end > buf_end is performed.
Add explicit bounds checks for entry pointers before accessing their fields. Use offsetof() to check that the start of the flexible array member (nameval/name) is within bounds, which ensures all preceding fields are safe to access.
--- fs/xfs/libxfs/xfs_attr_leaf.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
fs/xfs/libxfs/xfs_attr_leaf.c +14 -0
@@ -267,6 +267,13 @@ xfs_attr3_leaf_verify_entry(*/ if (ent->flags &XFS_ATTR_LOCAL ) { lentry =xfs_attr3_leaf_name_local (leaf, idx); + +/* Validate lentry pointer is within bounds before field access */ +if ((char *)lentry >= buf_end) +return __this_address; +if ((char *)lentry + offsetof(struct xfs_attr_leaf_name_local , nameval) > buf_end) +return __this_address; + namesize =xfs_attr_leaf_entsize_local (lentry->namelen,be16_to_cpu (lentry->valuelen)); name_end = (char *)lentry + namesize;
@@ -274,6 +281,13 @@ xfs_attr3_leaf_verify_entry(return __this_address; }else { rentry =xfs_attr3_leaf_name_remote (leaf, idx); + +/* Validate rentry pointer is within bounds before field access */ +if ((char *)rentry >= buf_end) +return __this_address; +if ((char *)rentry + offsetof(struct xfs_attr_leaf_name_remote , name) > buf_end) +return __this_address; + namesize =xfs_attr_leaf_entsize_remote (rentry->namelen); name_end = (char *)rentry + namesize;if (rentry->namelen ==0 )
Greg Kroah-Hartman
maintainerPATCH 11/79 misc: fastrpc: Rework fastrpc_req_munmap
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abel Vesa <[email protected]>
[ Upstream commit 72fa6f7820c4cf96c5f7aabc4e54bdf52d1e2ac2 ]
Move the lookup of the munmap request to the fastrpc_req_munmap and pass on only the buf to the lower level fastrpc_req_munmap_impl. That way we can use the lower level fastrpc_req_munmap_impl on error path in fastrpc_req_mmap to free the buf without searching for the munmap request it belongs to.
Stable-dep-of: 6102ceb4eab8 ("misc: fastrpc: Remove buffer from list prior to unmap operation")
--- drivers/misc/fastrpc.c | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-)
drivers/misc/fastrpc.c +23 -24
@@ -1657,30 +1657,14 @@ static int fastrpc_get_dsp_info(struct freturn 0 ; } -static int fastrpc_req_munmap_impl (struct fastrpc_user *fl, -struct fastrpc_req_munmap *req) +static int fastrpc_req_munmap_impl (struct fastrpc_user *fl,struct fastrpc_buf *buf) {struct fastrpc_invoke_args args[1 ] = { [0 ] = {0 } }; -struct fastrpc_buf *buf =NULL , *iter, *b;struct fastrpc_munmap_req_msg req_msg;struct device *dev = fl->sctx->dev;int err;u32 sc; -spin_lock (&fl->lock); -list_for_each_entry_safe (iter, b, &fl->mmaps, node) { -if ((iter->raddr == req->vaddrout) && (iter->size == req->size)) { - buf = iter; -break ; - } - } -spin_unlock (&fl->lock); - -if (!buf) { -dev_err (dev,"mmap not in list\n" ); -return -EINVAL ; - } - req_msg.pgid = fl->tgid; req_msg.size = buf->size; req_msg.vaddr = buf->raddr;
@@ -1706,12 +1690,29 @@ static int fastrpc_req_munmap_impl(strucstatic int fastrpc_req_munmap (struct fastrpc_user *fl,char __user *argp) { +struct fastrpc_buf *buf =NULL , *iter, *b;struct fastrpc_req_munmap req; +struct device *dev = fl->sctx->dev;if (copy_from_user (&req, argp,sizeof (req)))return -EFAULT ; -return fastrpc_req_munmap_impl (fl, &req); +spin_lock (&fl->lock); +list_for_each_entry_safe (iter, b, &fl->mmaps, node) { +if ((iter->raddr == req.vaddrout) && (iter->size == req.size)) { + buf = iter; +break ; + } + } +spin_unlock (&fl->lock); + +if (!buf) { +dev_err (dev,"mmap\t\tpt 0x%09llx [len 0x%08llx] not in list\n" , + req.vaddrout, req.size); +return -EINVAL ; + } + +return fastrpc_req_munmap_impl (fl, buf); }static int fastrpc_req_mmap (struct fastrpc_user *fl,char __user *argp)
@@ -1720,7 +1721,6 @@ static int fastrpc_req_mmap(struct fastrstruct fastrpc_buf *buf =NULL ;struct fastrpc_mmap_req_msg req_msg;struct fastrpc_mmap_rsp_msg rsp_msg; -struct fastrpc_req_munmap req_unmap;struct fastrpc_phy_page pages;struct fastrpc_req_mmap req;struct device *dev = fl->sctx->dev;
@@ -1782,11 +1782,8 @@ static int fastrpc_req_mmap(struct fastrspin_unlock (&fl->lock);if (copy_to_user ((void __user *)argp, &req,sizeof (req))) { -/* unmap the memory and release the buffer */ - req_unmap.vaddrout = buf->raddr; - req_unmap.size = buf->size; -fastrpc_req_munmap_impl (fl, &req_unmap); -return -EFAULT ; + err = -EFAULT ; + goto err_assign; } dev_dbg(dev, "mmap\t\tpt 0x%09lx OK [len 0x%08llx]\n",
@@ -1794,6 +1791,8 @@ static int fastrpc_req_mmap(struct fastrreturn 0 ; +err_assign: +fastrpc_req_munmap_impl (fl, buf); err_invoke:fastrpc_buf_free (buf);
Greg Kroah-Hartman
maintainerPATCH 12/79 misc: fastrpc: Remove buffer from list prior to unmap operation
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ekansh Gupta <[email protected]>
[ Upstream commit 6102ceb4eab845743ee57acd3863fbd06e93c927 ]
fastrpc_req_munmap_impl() is called to unmap any buffer. The buffer is getting removed from the list after it is unmapped from DSP. This can create potential race conditions if multiple threads invoke unmap concurrently, where one thread may remove the entry from the list while another thread's unmap operation is still ongoing.
Fix this by removing the buffer entry from the list before calling the unmap operation. If the unmap fails, the entry is re-added to the list so that userspace can retry the unmap, or alternatively, the buffer will be cleaned up during device release when the DSP process is torn down and all DSP-side mappings are freed along with remaining buffers in the list.
--- drivers/misc/fastrpc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
drivers/misc/fastrpc.c +10 -4
@@ -1677,9 +1677,6 @@ static int fastrpc_req_munmap_impl(struc &args[0]);if (!err) {dev_dbg (dev,"unmmap\tpt 0x%09lx OK\n" , buf->raddr); -spin_lock (&fl->lock); -list_del (&buf->node); -spin_unlock (&fl->lock);fastrpc_buf_free (buf); }else {dev_err (dev,"unmmap\tpt 0x%09lx ERROR\n" , buf->raddr);
@@ -1693,6 +1690,7 @@ static int fastrpc_req_munmap(struct fasstruct fastrpc_buf *buf =NULL , *iter, *b;struct fastrpc_req_munmap req;struct device *dev = fl->sctx->dev; +int err;if (copy_from_user (&req, argp,sizeof (req)))return -EFAULT ;
@@ -1700,6 +1698,7 @@ static int fastrpc_req_munmap(struct fasspin_lock (&fl->lock);list_for_each_entry_safe (iter, b, &fl->mmaps, node) {if ((iter->raddr == req.vaddrout) && (iter->size == req.size)) { +list_del (&iter->node); buf = iter;break ; }
@@ -1712,7 +1711,14 @@ static int fastrpc_req_munmap(struct fasreturn -EINVAL ; } -return fastrpc_req_munmap_impl (fl, buf); + err =fastrpc_req_munmap_impl (fl, buf); +if (err) { +spin_lock (&fl->lock); +list_add_tail (&buf->node, &fl->mmaps); +spin_unlock (&fl->lock); + } + +return err; }static int fastrpc_req_mmap (struct fastrpc_user *fl,char __user *argp)
Greg Kroah-Hartman
maintainerPATCH 13/79 NTB: ntb_netdev: Preserve RX queue depth on allocation failure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
[ Upstream commit d2121faf133ac3bf9531b53a7e21273649a08517 ]
ntb_netdev_rx_handler() hands the received skb to the network stack before allocating its replacement. If the allocation fails, nothing is reposted. Every failure therefore takes one buffer out of the RX queue while the interface remains up, and enough failures eventually stall reception.
A retry path could refill the queue later, but ntb_netdev has none. Allocate the replacement first instead. If that fails, drop the packet and repost the same skb. This keeps the queue full and lets packet delivery resume as soon as memory is available again.
--- drivers/net/ntb_netdev.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-)
drivers/net/ntb_netdev.c +8 -7
@@ -100,7 +100,7 @@ static void ntb_netdev_rx_handler(struct void *data, int len) {struct net_device *ndev = qp_data; -struct sk_buff *skb; +struct sk_buff *skb, *new_skb;int rc; skb = data;
@@ -115,6 +115,12 @@ static void ntb_netdev_rx_handler(struct goto enqueue_again; } + new_skb =netdev_alloc_skb (ndev, ndev->mtu +ETH_HLEN ); +if (!new_skb) { + ndev->stats.rx_dropped++; + goto enqueue_again; + } +skb_put (skb, len); skb->protocol =eth_type_trans (skb, ndev); skb->ip_summed =CHECKSUM_NONE ;
@@ -127,12 +133,7 @@ static void ntb_netdev_rx_handler(struct ndev->stats.rx_bytes += len; } - skb =netdev_alloc_skb (ndev, ndev->mtu +ETH_HLEN ); -if (!skb) { - ndev->stats.rx_errors++; - ndev->stats.rx_frame_errors++; -return ; - } + skb = new_skb; enqueue_again: rc =ntb_transport_rx_enqueue (qp, skb, skb->data, ndev->mtu +ETH_HLEN );
Greg Kroah-Hartman
maintainerPATCH 14/79 serial: amba-pl011: synchronize DMA teardown
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
[ Upstream commit 440915499231e9db1c361aa45bb702e8fd3b4a32 ]
dmaengine_terminate_all() does not wait for a running callback, so the TX callback can still touch the TX buffer after it is freed. The RX poll timer reads the RX buffers without the port lock.
Switch to dmaengine_terminate_sync() and delete the RX timer before freeing the buffers.
--- drivers/tty/serial/amba-pl011.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/tty/serial/amba-pl011.c +4 -4
@@ -1193,7 +1193,7 @@ static void pl011_dma_shutdown(struct uaif (uap->using_tx_dma) {/* In theory, this should already be done by pl011_dma_flush_buffer */ -dmaengine_terminate_all (uap->dmatx.chan); +dmaengine_terminate_sync (uap->dmatx.chan);if (uap->dmatx.queued) { dma_unmap_single(uap->dmatx.chan->device->dev, uap->dmatx.dma, uap->dmatx.len,
@@ -1206,12 +1206,12 @@ static void pl011_dma_shutdown(struct ua }if (uap->using_rx_dma) { -dmaengine_terminate_all (uap->dmarx.chan); +if (uap->dmarx.poll_rate) +timer_delete_sync (&uap->dmarx.timer); +dmaengine_terminate_sync (uap->dmarx.chan);/* Clean up the RX DMA */ pl011_dmabuf_free (uap->dmarx.chan, &uap->dmarx.dbuf_a,DMA_FROM_DEVICE );pl011_dmabuf_free (uap->dmarx.chan, &uap->dmarx.dbuf_b,DMA_FROM_DEVICE ); -if (uap->dmarx.poll_rate) -del_timer_sync (&uap->dmarx.timer); uap->using_rx_dma = false; } }
Greg Kroah-Hartman
maintainerPATCH 15/79 perf/core: Fix child_total_time_enabled accounting bug at task exit
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yeoreum Yun <[email protected]>
[ Upstream commit a3c3c66670cee11eb13aa43905904bf29cb92d32 ]
The perf events code fails to account for total_time_enabled of inactive events.
Here is a failure case for accounting total_time_enabled for CPU PMU events:
sudo ./perf stat -vvv -e armv8_pmuv3_0/event=0x08/ -e armv8_pmuv3_1/event=0x08/ -- stress-ng --pthread=2 -t 2s ...
armv8_pmuv3_0/event=0x08/: 1138698008 2289429840 2174835740 armv8_pmuv3_1/event=0x08/: 1826791390 1950025700 847648440
` ` `
` ` > total_time_running with child
` > total_time_enabled with child> count with child
Performance counter stats for 'stress-ng --pthread=2 -t 2s':
1,138,698,008 armv8_pmuv3_0/event=0x08/ (94.99%)
1,826,791,390 armv8_pmuv3_1/event=0x08/ (43.47%)The two events above are opened on two different CPU PMUs, for example, each event is opened for a cluster in an Arm big.LITTLE system, they will never run on the same CPU. In theory, the total enabled time should be same for both events, as two events are opened and closed together.
As the result show, the two events' total enabled time including child event is different (2289429840 vs 1950025700).
This is because child events are not accounted properly if a event is INACTIVE state when the task exits:
perf_event_exit_event()
`> perf_remove_from_context()
`> __perf_remove_from_context()
`> perf_child_detach() -> Accumulate child_total_time_enabled
`> list_del_event() -> Update child event's timeThe problem is the time accumulation happens prior to child event's time updating. Thus, it misses to account the last period's time when the event exits.
The perf core layer follows the rule that timekeeping is tied to state change. To address the issue, make __perf_remove_from_context() handle the task exit case by passing 'DETACH_EXIT' to it and invoke perf_event_state() for state alongside with accounting the time.
Then, perf_child_detach() populates the time into the parent's time metrics.
After this patch, the bug is fixed:
sudo ./perf stat -vvv -e armv8_pmuv3_0/event=0x08/ -e armv8_pmuv3_1/event=0x08/ -- stress-ng --pthread=2 -t 10s ... armv8_pmuv3_0/event=0x08/: 15396770398 32157963940 21898169000 armv8_pmuv3_1/event=0x08/: 22428964974 32157963940 10259794940
Performance counter stats for 'stress-ng --pthread=2 -t 10s':
15,396,770,398 armv8_pmuv3_0/event=0x08/ (68.10%)
22,428,964,974 armv8_pmuv3_1/event=0x08/ (31.90%)[ mingo: Clarified the changelog. ]
Stable-dep-of: 42c5ca1f0a28 ("perf/core: Fix group leader use-after-free after sibling detach")
--- kernel/events/core.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
kernel/events/core.c +9 -9
@@ -2373,6 +2373,7 @@ group_sched_out(struct perf_event *group#define DETACH_GROUP 0x01UL#define DETACH_CHILD 0x02UL#define DETACH_DEAD 0x04UL +#define DETACH_EXIT 0x08UL/* * Cross CPU call to remove a performance event
@@ -2386,6 +2387,7 @@ __perf_remove_from_context(struct perf_estruct perf_event_context *ctx, void *info) { + enum perf_event_state state = PERF_EVENT_STATE_OFF;unsigned long flags = (unsigned long )info;if (ctx->is_active &EVENT_TIME ) {
@@ -2397,16 +2399,19 @@ __perf_remove_from_context(struct perf_e* Ensure event_sched_out() switches to OFF, at the very least * this avoids raising perf_pending_task() at this time. */ -if (flags &DETACH_DEAD ) +if (flags &DETACH_EXIT ) + state =PERF_EVENT_STATE_EXIT ; +if (flags &DETACH_DEAD ) { event->pending_disable =1 ; + state =PERF_EVENT_STATE_DEAD ; + }event_sched_out (event, cpuctx, ctx); +perf_event_set_state (event,min (event->state, state));if (flags &DETACH_GROUP )perf_group_detach (event);if (flags &DETACH_CHILD )perf_child_detach (event);list_del_event (event, ctx); -if (flags &DETACH_DEAD ) - event->state =PERF_EVENT_STATE_DEAD ;if (!ctx->nr_events && ctx->is_active) {if (ctx == &cpuctx->ctx)
@@ -13077,12 +13082,7 @@ perf_event_exit_event(struct perf_eventmutex_lock (&parent_event->child_mutex); } -perf_remove_from_context (event, detach_flags); - -raw_spin_lock_irq (&ctx->lock); -if (event->state >PERF_EVENT_STATE_EXIT ) -perf_event_set_state (event,PERF_EVENT_STATE_EXIT ); -raw_spin_unlock_irq (&ctx->lock); +perf_remove_from_context (event, detach_flags |DETACH_EXIT );/* * Child events can be freed.
Greg Kroah-Hartman
maintainerPATCH 16/79 perf: Fix cgroup state vs ERROR
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Zijlstra <[email protected]>
[ Upstream commit 61988e36dc5457cdff7ae7927e8d9ad1419ee998 ]
While chasing down a missing perf_cgroup_event_disable() elsewhere, Leo Yan found that both perf_put_aux_event() and perf_remove_sibling_event() were also missing one.
Specifically, the rule is that events that switch to OFF,ERROR need to call perf_cgroup_event_disable().
Unify the disable paths to ensure this.
Stable-dep-of: 42c5ca1f0a28 ("perf/core: Fix group leader use-after-free after sibling detach")
--- kernel/events/core.c | 56 +++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 26 deletions(-)
kernel/events/core.c +30 -26
@@ -2059,14 +2059,13 @@ perf_aux_output_match(struct perf_event }static void put_event (struct perf_event *event); -static void event_sched_out (struct perf_event *event, -struct perf_cpu_context *cpuctx, -struct perf_event_context *ctx); +static void __event_disable (struct perf_event *event, +struct perf_event_context *ctx, +enum perf_event_state state);static void perf_put_aux_event (struct perf_event *event) {struct perf_event_context *ctx = event->ctx; -struct perf_cpu_context *cpuctx =__get_cpu_context (ctx);struct perf_event *iter;/*
@@ -2095,8 +2094,7 @@ static void perf_put_aux_event(struct pe* state so that we don't try to schedule it again. Note * that perf_event_enable() will clear the ERROR status. */ -event_sched_out (iter, cpuctx, ctx); -perf_event_set_state (event,PERF_EVENT_STATE_ERROR ); +__event_disable (iter, ctx,PERF_EVENT_STATE_ERROR ); } }
@@ -2150,21 +2148,6 @@ static inline struct list_head *get_evenreturn event->attr.pinned ? &ctx->pinned_active : &ctx->flexible_active; } -/* -* Events that have PERF_EV_CAP_SIBLING require being part of a group and -* cannot exist on their own, schedule them out and move them into the ERROR -* state. Also see _perf_event_enable(), it will not be able to recover -* this ERROR state. -*/ -static inline void perf_remove_sibling_event (struct perf_event *event) -{ -struct perf_event_context *ctx = event->ctx; -struct perf_cpu_context *cpuctx =__get_cpu_context (ctx); - -event_sched_out (event, cpuctx, ctx); -perf_event_set_state (event,PERF_EVENT_STATE_ERROR ); -} -static void perf_group_detach (struct perf_event *event) {struct perf_event *leader = event->group_leader;
@@ -2200,8 +2183,15 @@ static void perf_group_detach(struct per*/ list_for_each_entry_safe (sibling, tmp, &event->sibling_list, sibling_list) { +/* +* Events that have PERF_EV_CAP_SIBLING require being part of +* a group and cannot exist on their own, schedule them out +* and move them into the ERROR state. Also see +* _perf_event_enable(), it will not be able to recover this +* ERROR state. +*/ if (sibling->event_caps &PERF_EV_CAP_SIBLING ) -perf_remove_sibling_event (sibling); +__event_disable (sibling, ctx,PERF_EVENT_STATE_ERROR ); sibling->group_leader = sibling;list_del_init (&sibling->sibling_list);
@@ -2463,6 +2453,15 @@ static void perf_remove_from_context(strevent_function_call (event, __perf_remove_from_context, (void *)flags); } +static void __event_disable (struct perf_event *event, +struct perf_event_context *ctx, +enum perf_event_state state) +{ +event_sched_out (event,__get_cpu_context (ctx), ctx); +perf_cgroup_event_disable (event, ctx); +perf_event_set_state (event, state); +} +/* * Cross CPU call to disable a performance event */
@@ -2479,13 +2478,18 @@ static void __perf_event_disable(structupdate_cgrp_time_from_event (event); } +/* +* When disabling a group leader, the whole group becomes ineligible +* to run, so schedule out the full group. +*/ if (event == event->group_leader)group_sched_out (event, cpuctx, ctx); -else -event_sched_out (event, cpuctx, ctx); -perf_event_set_state (event,PERF_EVENT_STATE_OFF ); -perf_cgroup_event_disable (event, ctx); +/* +* But only mark the leader OFF; the siblings will remain +* INACTIVE. +*/ +__event_disable (event, ctx,PERF_EVENT_STATE_OFF ); }/*
Greg Kroah-Hartman
maintainerPATCH 17/79 perf: Fix dangling cgroup pointer in cpuctx
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yeoreum Yun <[email protected]>
[ Upstream commit 3b7a34aebbdf2a4b7295205bf0c654294283ec82 ]
Commit a3c3c6667("perf/core: Fix child_total_time_enabled accounting bug at task exit") moves the event->state update to before list_del_event(). This makes the event->state test in list_del_event() always false; never calling perf_cgroup_event_disable().
As a result, cpuctx->cgrp won't be cleared properly; causing havoc.
Stable-dep-of: 42c5ca1f0a28 ("perf/core: Fix group leader use-after-free after sibling detach")
--- kernel/events/core.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-)
kernel/events/core.c +4 -12
@@ -2031,18 +2031,6 @@ list_del_event(struct perf_event *event,if (event->group_leader == event)del_event_from_groups (event, ctx); -/* -* If event was in error state, then keep it -* that way, otherwise bogus counts will be -* returned on read(). The only way to get out -* of error state is by explicit re-enabling -* of the event -*/ -if (event->state >PERF_EVENT_STATE_OFF ) { -perf_cgroup_event_disable (event, ctx); -perf_event_set_state (event,PERF_EVENT_STATE_OFF ); - } - ctx->generation++; }
@@ -2396,6 +2384,10 @@ __perf_remove_from_context(struct perf_e state =PERF_EVENT_STATE_DEAD ; }event_sched_out (event, cpuctx, ctx); + +if (event->state >PERF_EVENT_STATE_OFF ) +perf_cgroup_event_disable (event, ctx); +perf_event_set_state (event,min (event->state, state));if (flags &DETACH_GROUP )perf_group_detach (event);
Greg Kroah-Hartman
maintainerPATCH 18/79 perf/core: Fix group leader use-after-free after sibling detach
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aditya Chillara <[email protected]>
[ Upstream commit 42c5ca1f0a288a52878bd72a5595b08261057438 ]
perf_group_detach() handles leader and sibling detach differently. When the group leader is detached, all siblings are promoted to singleton events and their group_leader pointer is reset to themselves. When a sibling is detached, it is removed from the leader's sibling_list, but its group_leader pointer is left pointing at the old leader.
That is harmless when the sibling is being closed and freed immediately, as in the DETACH_DEAD path. It is not safe when the sibling is detached but kept alive, such as during CPU hotplug with DETACH_GROUP. In that case the sibling is removed from the context, while its file descriptor can still keep it alive.
A typical failing sequence is:
- A group contains leader L and sibling S.
- CPU hot-unplug detaches S with DETACH_GROUP, removing it from
L->sibling_list but leaving S->group_leader == L.
- L is later closed and freed.
- A PERF_IOC_FLAG_GROUP ioctl on S follows S->group_leader and dereferences the freed leader.
This was reproduced by running the perf event fuzzer, CPU hotplug, and a stress workload concurrently:
Unable to handle kernel paging request at virtual address 006b6b6b6b6b6cdb CPU: 2 PID: 12489 Comm: perf_fuzzer 6.18.7 PREEMPT pc : perf_ioctl+0x34c/0xc68 x20: ffffff89a3fa2c70 x8 : 6b6b6b6b6b6b6b6b Code: 943c4a0e 340047a0 f9404a94 f9411e88 (f940b908) Call trace: perf_ioctl+0x34c/0xc68 (P) __arm64_sys_ioctl+0xa0/0xf4 invoke_syscall+0x58/0xe4 el0_svc_common+0xa8/0xdc do_el0_svc+0x1c/0x28 el0_svc+0x40/0xc0 el0t_64_sync_handler+0x68/0xdc el0t_64_sync+0x1c4/0x1c8
The fault happened in perf_ioctl(), where perf_event_for_each() follows the stale group_leader pointer and perf_event_for_each_child() then dereferences the freed leader's context.
Fix the use-after-free by promoting the detached sibling to a singleton. Also fix __event_disable() cgroup accounting and event state change.
--- kernel/events/core.c | 65 +++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 28 deletions(-)
kernel/events/core.c +37 -28
@@ -2136,6 +2136,34 @@ static inline struct list_head *get_evenreturn event->attr.pinned ? &ctx->pinned_active : &ctx->flexible_active; } +/* @sibling must already be unlinked from its old leader's sibling_list. */ +static void perf_promote_sibling_to_leader (struct perf_event *sibling, +struct perf_event_context *ctx, +int group_caps) +{ +/* +* Events that have PERF_EV_CAP_SIBLING require being part of +* a group and cannot exist on their own, schedule them out +* and move them into the ERROR state. Also see +* _perf_event_enable(), it will not be able to recover this +* ERROR state. +*/ +if (sibling->event_caps &PERF_EV_CAP_SIBLING ) +__event_disable (sibling, ctx,PERF_EVENT_STATE_ERROR ); + + sibling->group_leader = sibling; + sibling->group_caps = group_caps; + +if (sibling->attach_state &PERF_ATTACH_CONTEXT ) { +add_event_to_groups (sibling, ctx); + +if (sibling->state ==PERF_EVENT_STATE_ACTIVE ) +list_add_tail (&sibling->active_list,get_event_list (sibling)); + } + +perf_event__header_size (sibling); +} +static void perf_group_detach (struct perf_event *event) {struct perf_event *leader = event->group_leader;
@@ -2159,8 +2187,9 @@ static void perf_group_detach(struct per*/ if (leader != event) {list_del_init (&event->sibling_list); - event->group_leader->nr_siblings--; - event->group_leader->group_generation++; + leader->nr_siblings--; + leader->group_generation++; +perf_promote_sibling_to_leader (event, ctx, event->event_caps); goto out; }
@@ -2170,32 +2199,14 @@ static void perf_group_detach(struct per* to whatever list we are on. */ list_for_each_entry_safe (sibling, tmp, &event->sibling_list, sibling_list) { - -/* -* Events that have PERF_EV_CAP_SIBLING require being part of -* a group and cannot exist on their own, schedule them out -* and move them into the ERROR state. Also see -* _perf_event_enable(), it will not be able to recover this -* ERROR state. -*/ -if (sibling->event_caps &PERF_EV_CAP_SIBLING ) -__event_disable (sibling, ctx,PERF_EVENT_STATE_ERROR ); - - sibling->group_leader = sibling;list_del_init (&sibling->sibling_list);/* Inherit group flags from the previous leader */ - sibling->group_caps = event->group_caps; - -if (sibling->attach_state &PERF_ATTACH_CONTEXT ) { -add_event_to_groups (sibling, event->ctx); - -if (sibling->state ==PERF_EVENT_STATE_ACTIVE ) -list_add_tail (&sibling->active_list,get_event_list (sibling)); - } +perf_promote_sibling_to_leader (sibling, ctx, event->group_caps);WARN_ON_ONCE (sibling->ctx != event->ctx); } + event->nr_siblings =0 ; out:for_each_sibling_event (tmp, leader)
@@ -2383,12 +2394,9 @@ __perf_remove_from_context(struct perf_e event->pending_disable =1 ; state =PERF_EVENT_STATE_DEAD ; } -event_sched_out (event, cpuctx, ctx); -if (event->state >PERF_EVENT_STATE_OFF ) -perf_cgroup_event_disable (event, ctx); +__event_disable (event, ctx, state); -perf_event_set_state (event,min (event->state, state));if (flags &DETACH_GROUP )perf_group_detach (event);if (flags &DETACH_CHILD )
@@ -2450,8 +2458,9 @@ static void __event_disable(struct perf_ enum perf_event_state state) {event_sched_out (event,__get_cpu_context (ctx), ctx); -perf_cgroup_event_disable (event, ctx); -perf_event_set_state (event, state); +if (event->state >PERF_EVENT_STATE_OFF ) +perf_cgroup_event_disable (event, ctx); +perf_event_set_state (event,min (event->state, state)); }/*
Greg Kroah-Hartman
maintainerPATCH 19/79 packet: use consistent hard_header_len in non-ring send paths
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qihang Tang <[email protected]>
[ Upstream commit 03390aa32e669cc4ecd7d34108e2e1afc13d689d ]
packet_snd() reads dev->hard_header_len multiple times while allocating and constructing an skb. Device reconfiguration can change this value concurrently, for example through bonding device type changes.
For SOCK_RAW, packet_snd() can save a larger value in reserve and later allocate headroom using a smaller value. Moving skb->data back by reserve then places it before skb->head, and the following copy from userspace can attempt an out-of-bounds write.
packet_sendmsg_spkt() has the same issue because it calculates its reservation and header offset from separate reads before dropping the RCU read lock to allocate the skb.
Add LL_RESERVED_SPACE_EX() for callers that already saved a header length. Read hard_header_len once in packet_snd() and use it for allocation and construction. In packet_sendmsg_spkt(), preserve the allocation-time value through the device lookup retry.
The separate SOCK_DGRAM consistency problem between hard_header_len and header_ops->create is not addressed here.
Stable-dep-of: 21b5953e7494 ("packet: use consistent hard_header_len in TX_RING send path")
--- include/linux/netdevice.h | 6 ++++-- net/packet/af_packet.c | 26 ++++++++++++++++---------- 2 files changed, 20 insertions(+), 12 deletions(-)
include/linux/netdevice.h +4 -2
@@ -293,9 +293,11 @@ struct hh_cache {* We could use other alignment values, but we must maintain the * relationship HH alignment <= LL alignment. */ -#define LL_RESERVED_SPACE (dev) \ - ((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom)) \ +#define LL_RESERVED_SPACE_EX (dev, hlen) \ + ((((hlen) + READ_ONCE((dev)->needed_headroom)) \ & ~(HH_DATA_MOD - 1)) + HH_DATA_MOD) +#define LL_RESERVED_SPACE (dev) \ + LL_RESERVED_SPACE_EX(dev, (dev)->hard_header_len)#define LL_RESERVED_SPACE_EXTRA (dev,extra) \ ((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom) + (extra)) \ & ~(HH_DATA_MOD - 1)) + HH_DATA_MOD)
net/packet/af_packet.c +16 -10
@@ -2008,8 +2008,9 @@ static int packet_sendmsg_spkt(struct sostruct net_device *dev;struct sockcm_cookie sockc;__be16 proto =0 ; -int err; +int hard_header_len;int extra_len =0 ; +int err;/* * Get and verify the address.
@@ -2052,14 +2053,18 @@ retry: extra_len =4 ;/* We're doing our own CRC */ } +/* Keep the allocation-time header length across retry. */ +if (!skb) + hard_header_len =READ_ONCE (dev->hard_header_len); + err = -EMSGSIZE ; -if (len > dev->mtu + dev->hard_header_len +VLAN_HLEN + extra_len) +if (len > dev->mtu + hard_header_len +VLAN_HLEN + extra_len) goto out_unlock;if (!skb) { -size_t reserved =LL_RESERVED_SPACE (dev); +size_t reserved =LL_RESERVED_SPACE_EX (dev, hard_header_len);int tlen = dev->needed_tailroom; -unsigned int hhlen = dev->header_ops ? dev->hard_header_len :0 ; +unsigned int hhlen = dev->header_ops ? hard_header_len :0 ;rcu_read_unlock (); skb =sock_wmalloc (sk, len + reserved + tlen,0 ,GFP_KERNEL );
@@ -2089,7 +2094,7 @@ retry: err = -EINVAL ; goto out_unlock; } -if (len > (dev->mtu + dev->hard_header_len + extra_len) && +if (len > (dev->mtu + hard_header_len + extra_len) && !packet_extra_vlan_len_allowed (dev, skb)) { err = -EMSGSIZE ; goto out_unlock;
@@ -3008,7 +3013,7 @@ static int packet_snd(struct socket *socint offset =0 ;struct packet_sock *po =pkt_sk (sk);bool has_vnet_hdr = false; -int hlen, tlen, linear; +int hard_header_len, hlen, tlen, linear;int extra_len =0 ;/*
@@ -3049,8 +3054,9 @@ static int packet_snd(struct socket *soc goto out_unlock; } + hard_header_len =READ_ONCE (dev->hard_header_len);if (sock->type ==SOCK_RAW ) - reserve = dev->hard_header_len; + reserve = hard_header_len;if (packet_sock_flag (po,PACKET_SOCK_HAS_VNET_HDR )) { err =packet_snd_vnet_parse (msg, &len, &vnet_hdr);if (err)
@@ -3072,10 +3078,10 @@ static int packet_snd(struct socket *soc goto out_unlock; err = -ENOBUFS ; - hlen =LL_RESERVED_SPACE (dev); + hlen =LL_RESERVED_SPACE_EX (dev, hard_header_len); tlen = dev->needed_tailroom; linear =__virtio16_to_cpu (vio_le (), vnet_hdr.hdr_len); - linear =max (linear,min_t (int, len, dev->hard_header_len)); + linear =max (linear,min_t (int, len, hard_header_len)); skb =packet_alloc_skb (sk, hlen + tlen, hlen, len, linear, msg->msg_flags &MSG_DONTWAIT , &err);if (skb ==NULL )
@@ -3091,7 +3097,7 @@ static int packet_snd(struct socket *soc }else if (reserve) {skb_reserve (skb, -reserve);if (len < reserve +sizeof (struct ipv6hdr ) && - dev->min_header_len != dev->hard_header_len) + dev->min_header_len != hard_header_len)skb_reset_network_header (skb); }
Greg Kroah-Hartman
maintainerPATCH 20/79 packet: use consistent hard_header_len in TX_RING send path
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qihang Tang <[email protected]>
[ Upstream commit 21b5953e7494c16a42e6cd8cf110e18d13ae4a6b ]
tpacket_snd() reads dev->hard_header_len independently for skb allocation and header construction in tpacket_fill_skb(). Concurrent netdevice reconfiguration can therefore make the reserved headroom smaller than the amount later pushed, or make copylen - hard_header_len negative.
Snapshot hard_header_len once before processing ring frames and use it for the frame limit, headroom allocation, copy length, and skb construction. Pass the snapshot to tpacket_fill_skb().
The separate SOCK_DGRAM consistency problem between hard_header_len and header_ops->create is not addressed here.
--- net/packet/af_packet.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
net/packet/af_packet.c +11 -8
@@ -2624,6 +2624,7 @@ static int packet_snd_vnet_parse(structstatic int tpacket_fill_skb (struct packet_sock *po,struct sk_buff *skb,void *frame,struct net_device *dev,void *data,int tp_len,__be16 proto,unsigned char *addr,int hlen,int copylen, +int hard_header_len,const struct sockcm_cookie *sockc) {union tpacket_uhdr ph;
@@ -2655,8 +2656,8 @@ static int tpacket_fill_skb(struct packe }else if (copylen) {int hdrlen =min_t (int, copylen, tp_len); -skb_push (skb, dev->hard_header_len); -skb_put (skb, copylen - dev->hard_header_len); +skb_push (skb, hard_header_len); +skb_put (skb, copylen - hard_header_len); err =skb_store_bits (skb,0 , data, hdrlen);if (unlikely (err))return err;
@@ -2789,7 +2790,7 @@ static int tpacket_snd(struct packet_socvoid *data;int len_sum =0 ;int status =TP_STATUS_AVAILABLE ; -int hlen, tlen, copylen =0 ; +int hard_header_len, hlen, tlen, copylen =0 ;long timeo;mutex_lock (&po->pg_vec_lock);
@@ -2836,8 +2837,9 @@ static int tpacket_snd(struct packet_soc goto out_put; } + hard_header_len =READ_ONCE (dev->hard_header_len);if (po->sk.sk_socket->type ==SOCK_RAW ) - reserve = dev->hard_header_len; + reserve = hard_header_len; size_max = po->tx_ring.frame_size - (po->tp_hdrlen -sizeof (struct sockaddr_ll ));
@@ -2875,7 +2877,7 @@ static int tpacket_snd(struct packet_soc goto tpacket_error; status =TP_STATUS_SEND_REQUEST ; - hlen =LL_RESERVED_SPACE (dev); + hlen =LL_RESERVED_SPACE_EX (dev, hard_header_len); tlen = dev->needed_tailroom;if (packet_sock_flag (po,PACKET_SOCK_HAS_VNET_HDR )) { data +=sizeof (vnet_hdr);
@@ -2893,10 +2895,10 @@ static int tpacket_snd(struct packet_soc vnet_hdr.hdr_len); has_vnet_hdr = true; } - copylen =max_t (int, copylen, dev->hard_header_len); + copylen =max_t (int, copylen, hard_header_len); skb =sock_alloc_send_skb (&po->sk, hlen + tlen +sizeof (struct sockaddr_ll ) + - (copylen - dev->hard_header_len), + (copylen - hard_header_len), !need_wait, &err);if (unlikely (skb ==NULL )) {
@@ -2906,7 +2908,8 @@ static int tpacket_snd(struct packet_soc goto out_status; } tp_len =tpacket_fill_skb (po, skb, ph, dev, data, tp_len, proto, - addr, hlen, copylen, &sockc); + addr, hlen, copylen, hard_header_len, + &sockc); if (likely(tp_len >= 0) && tp_len > dev->mtu + reserve && !packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR) &&
Greg Kroah-Hartman
maintainerPATCH 21/79 serial: sc16is7xx: fix copy-paste errors in EFR_SWFLOWx_BIT constants
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lech Perczak <[email protected]>
[ Upstream commit eccdb0fd1c340155666533f5c60c5229d370baab ]
Comments attached to bits 0 and 1 incorrectly referenced bits 2 and 3, which don't match the datasheet - fix them.
At the same time remove comments for individual constants, as they add nothing to the definitions themselves.
Stable-dep-of: 246ac114f485 ("serial: sc16is7xx: enable THRI before filling TX FIFO")
--- drivers/tty/serial/sc16is7xx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
drivers/tty/serial/sc16is7xx.c +7 -7
@@ -263,9 +263,9 @@* and writing to IER[7:4], * FCR[5:4], MCR[7:5] */ -#define SC16IS7XX_EFR_SWFLOW3_BIT (1 << 3)/* SWFLOW bit 3 */ -#define SC16IS7XX_EFR_SWFLOW2_BIT (1 << 2)/* SWFLOW bit 2 -* +#define SC16IS7XX_EFR_SWFLOW3_BIT (1 << 3) +#define SC16IS7XX_EFR_SWFLOW2_BIT (1 << 2) +/* * SWFLOW bits 3 & 2 table: * 00 -> no transmitter flow * control
@@ -277,10 +277,10 @@* XON1, XON2, XOFF1 and * XOFF2 */ -#define SC16IS7XX_EFR_SWFLOW1_BIT (1 << 1)/* SWFLOW bit 2 */ -#define SC16IS7XX_EFR_SWFLOW0_BIT (1 << 0)/* SWFLOW bit 3 -* -* SWFLOW bits 3 & 2 table: +#define SC16IS7XX_EFR_SWFLOW1_BIT (1 << 1) +#define SC16IS7XX_EFR_SWFLOW0_BIT (1 << 0) +/* +* SWFLOW bits 1 & 0 table: * 00 -> no received flow * control * 01 -> receiver compares
Greg Kroah-Hartman
maintainerPATCH 22/79 serial: sc16is7xx: convert bitmask definitions to use BIT() macro
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lech Perczak <[email protected]>
[ Upstream commit d2e8590fd1048c5c0dba160edc6a48ee8305a1f0 ]
Now that bit definition comments were cleaned up, convert bitmask definitions to use BIT() macro for clarity.
Convert SC16IS7XX_IIR_ID_MASK to use GENMASK() macro -
- while at that, realign comments.
Compose SC16IS7XX_LSR_BRK_ERROR_MASK using aforementioned constants, instead of open-coding it, and remove now unneeded comments.
Stable-dep-of: 246ac114f485 ("serial: sc16is7xx: enable THRI before filling TX FIFO")
--- drivers/tty/serial/sc16is7xx.c | 176 +++++++++++++++++++++-------------------- 1 file changed, 91 insertions(+), 85 deletions(-)
drivers/tty/serial/sc16is7xx.c +91 -85
@@ -9,6 +9,7 @@#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt#include <linux/bitops.h> +#include <linux/bits.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h>
@@ -73,52 +74,52 @@#define SC16IS7XX_XOFF2_REG (0x07)/* Xoff2 word */ /* IER register bits */ -#define SC16IS7XX_IER_RDI_BIT (1 << 0)/* Enable RX data interrupt */ -#define SC16IS7XX_IER_THRI_BIT (1 << 1)/* Enable TX holding register +#define SC16IS7XX_IER_RDI_BIT BIT(0)/* Enable RX data interrupt */ +#define SC16IS7XX_IER_THRI_BIT BIT(1)/* Enable TX holding register * interrupt */ -#define SC16IS7XX_IER_RLSI_BIT (1 << 2)/* Enable RX line status +#define SC16IS7XX_IER_RLSI_BIT BIT(2)/* Enable RX line status * interrupt */ -#define SC16IS7XX_IER_MSI_BIT (1 << 3)/* Enable Modem status +#define SC16IS7XX_IER_MSI_BIT BIT(3)/* Enable Modem status * interrupt */ /* IER register bits - write only if (EFR[4] == 1) */ -#define SC16IS7XX_IER_SLEEP_BIT (1 << 4)/* Enable Sleep mode */ -#define SC16IS7XX_IER_XOFFI_BIT (1 << 5)/* Enable Xoff interrupt */ -#define SC16IS7XX_IER_RTSI_BIT (1 << 6)/* Enable nRTS interrupt */ -#define SC16IS7XX_IER_CTSI_BIT (1 << 7)/* Enable nCTS interrupt */ +#define SC16IS7XX_IER_SLEEP_BIT BIT(4)/* Enable Sleep mode */ +#define SC16IS7XX_IER_XOFFI_BIT BIT(5)/* Enable Xoff interrupt */ +#define SC16IS7XX_IER_RTSI_BIT BIT(6)/* Enable nRTS interrupt */ +#define SC16IS7XX_IER_CTSI_BIT BIT(7)/* Enable nCTS interrupt */ /* FCR register bits */ -#define SC16IS7XX_FCR_FIFO_BIT (1 << 0)/* Enable FIFO */ -#define SC16IS7XX_FCR_RXRESET_BIT (1 << 1)/* Reset RX FIFO */ -#define SC16IS7XX_FCR_TXRESET_BIT (1 << 2)/* Reset TX FIFO */ -#define SC16IS7XX_FCR_RXLVLL_BIT (1 << 6)/* RX Trigger level LSB */ -#define SC16IS7XX_FCR_RXLVLH_BIT (1 << 7)/* RX Trigger level MSB */ +#define SC16IS7XX_FCR_FIFO_BIT BIT(0)/* Enable FIFO */ +#define SC16IS7XX_FCR_RXRESET_BIT BIT(1)/* Reset RX FIFO */ +#define SC16IS7XX_FCR_TXRESET_BIT BIT(2)/* Reset TX FIFO */ +#define SC16IS7XX_FCR_RXLVLL_BIT BIT(6)/* RX Trigger level LSB */ +#define SC16IS7XX_FCR_RXLVLH_BIT BIT(7)/* RX Trigger level MSB */ /* FCR register bits - write only if (EFR[4] == 1) */ -#define SC16IS7XX_FCR_TXLVLL_BIT (1 << 4)/* TX Trigger level LSB */ -#define SC16IS7XX_FCR_TXLVLH_BIT (1 << 5)/* TX Trigger level MSB */ +#define SC16IS7XX_FCR_TXLVLL_BIT BIT(4)/* TX Trigger level LSB */ +#define SC16IS7XX_FCR_TXLVLH_BIT BIT(5)/* TX Trigger level MSB */ /* IIR register bits */ -#define SC16IS7XX_IIR_NO_INT_BIT (1 << 0)/* No interrupts pending */ -#define SC16IS7XX_IIR_ID_MASK 0x3e/* Mask for the interrupt ID */ -#define SC16IS7XX_IIR_THRI_SRC 0x02/* TX holding register empty */ -#define SC16IS7XX_IIR_RDI_SRC 0x04/* RX data interrupt */ -#define SC16IS7XX_IIR_RLSE_SRC 0x06/* RX line status error */ -#define SC16IS7XX_IIR_RTOI_SRC 0x0c/* RX time-out interrupt */ -#define SC16IS7XX_IIR_MSI_SRC 0x00/* Modem status interrupt -* - only on 75x/76x -*/ -#define SC16IS7XX_IIR_INPIN_SRC 0x30/* Input pin change of state -* - only on 75x/76x -*/ -#define SC16IS7XX_IIR_XOFFI_SRC 0x10/* Received Xoff */ -#define SC16IS7XX_IIR_CTSRTS_SRC 0x20/* nCTS,nRTS change of state -* from active (LOW) -* to inactive (HIGH) -*/ +#define SC16IS7XX_IIR_NO_INT_BIT 0x01/* No interrupts pending */ +#define SC16IS7XX_IIR_ID_MASK GENMASK(5, 1)/* Mask for the interrupt ID */ +#define SC16IS7XX_IIR_THRI_SRC 0x02/* TX holding register empty */ +#define SC16IS7XX_IIR_RDI_SRC 0x04/* RX data interrupt */ +#define SC16IS7XX_IIR_RLSE_SRC 0x06/* RX line status error */ +#define SC16IS7XX_IIR_RTOI_SRC 0x0c/* RX time-out interrupt */ +#define SC16IS7XX_IIR_MSI_SRC 0x00/* Modem status interrupt +* - only on 75x/76x +*/ +#define SC16IS7XX_IIR_INPIN_SRC 0x30/* Input pin change of state +* - only on 75x/76x +*/ +#define SC16IS7XX_IIR_XOFFI_SRC 0x10/* Received Xoff */ +#define SC16IS7XX_IIR_CTSRTS_SRC 0x20/* nCTS,nRTS change of state +* from active (LOW) +* to inactive (HIGH) +*/ /* LCR register bits */ -#define SC16IS7XX_LCR_LENGTH0_BIT (1 << 0)/* Word length bit 0 */ -#define SC16IS7XX_LCR_LENGTH1_BIT (1 << 1)/* Word length bit 1 +#define SC16IS7XX_LCR_LENGTH0_BIT BIT(0)/* Word length bit 0 */ +#define SC16IS7XX_LCR_LENGTH1_BIT BIT(1)/* Word length bit 1 * * Word length bits table: * 00 -> 5 bit words
@@ -126,7 +127,7 @@* 10 -> 7 bit words * 11 -> 8 bit words */ -#define SC16IS7XX_LCR_STOPLEN_BIT (1 << 2)/* STOP length bit +#define SC16IS7XX_LCR_STOPLEN_BIT BIT(2)/* STOP length bit * * STOP length bit table: * 0 -> 1 stop bit
@@ -134,11 +135,11 @@* word length is 5, * 2 stop bits otherwise */ -#define SC16IS7XX_LCR_PARITY_BIT (1 << 3)/* Parity bit enable */ -#define SC16IS7XX_LCR_EVENPARITY_BIT (1 << 4)/* Even parity bit enable */ -#define SC16IS7XX_LCR_FORCEPARITY_BIT (1 << 5)/* 9-bit multidrop parity */ -#define SC16IS7XX_LCR_TXBREAK_BIT (1 << 6)/* TX break enable */ -#define SC16IS7XX_LCR_DLAB_BIT (1 << 7)/* Divisor Latch enable */ +#define SC16IS7XX_LCR_PARITY_BIT BIT(3)/* Parity bit enable */ +#define SC16IS7XX_LCR_EVENPARITY_BIT BIT(4)/* Even parity bit enable */ +#define SC16IS7XX_LCR_FORCEPARITY_BIT BIT(5)/* 9-bit multidrop parity */ +#define SC16IS7XX_LCR_TXBREAK_BIT BIT(6)/* TX break enable */ +#define SC16IS7XX_LCR_DLAB_BIT BIT(7)/* Divisor Latch enable */ #define SC16IS7XX_LCR_WORD_LEN_5 (0x00)#define SC16IS7XX_LCR_WORD_LEN_6 (0x01)#define SC16IS7XX_LCR_WORD_LEN_7 (0x02)
@@ -149,58 +150,63 @@* reg set */ /* MCR register bits */ -#define SC16IS7XX_MCR_DTR_BIT (1 << 0)/* DTR complement +#define SC16IS7XX_MCR_DTR_BIT BIT(0)/* DTR complement * - only on 75x/76x */ -#define SC16IS7XX_MCR_RTS_BIT (1 << 1)/* RTS complement */ -#define SC16IS7XX_MCR_TCRTLR_BIT (1 << 2)/* TCR/TLR register enable */ -#define SC16IS7XX_MCR_LOOP_BIT (1 << 4)/* Enable loopback test mode */ -#define SC16IS7XX_MCR_XONANY_BIT (1 << 5)/* Enable Xon Any +#define SC16IS7XX_MCR_RTS_BIT BIT(1)/* RTS complement */ +#define SC16IS7XX_MCR_TCRTLR_BIT BIT(2)/* TCR/TLR register enable */ +#define SC16IS7XX_MCR_LOOP_BIT BIT(4)/* Enable loopback test mode */ +#define SC16IS7XX_MCR_XONANY_BIT BIT(5)/* Enable Xon Any * - write enabled * if (EFR[4] == 1) */ -#define SC16IS7XX_MCR_IRDA_BIT (1 << 6)/* Enable IrDA mode +#define SC16IS7XX_MCR_IRDA_BIT BIT(6)/* Enable IrDA mode * - write enabled * if (EFR[4] == 1) */ -#define SC16IS7XX_MCR_CLKSEL_BIT (1 << 7)/* Divide clock by 4 +#define SC16IS7XX_MCR_CLKSEL_BIT BIT(7)/* Divide clock by 4 * - write enabled * if (EFR[4] == 1) */ /* LSR register bits */ -#define SC16IS7XX_LSR_DR_BIT (1 << 0)/* Receiver data ready */ -#define SC16IS7XX_LSR_OE_BIT (1 << 1)/* Overrun Error */ -#define SC16IS7XX_LSR_PE_BIT (1 << 2)/* Parity Error */ -#define SC16IS7XX_LSR_FE_BIT (1 << 3)/* Frame Error */ -#define SC16IS7XX_LSR_BI_BIT (1 << 4)/* Break Interrupt */ -#define SC16IS7XX_LSR_BRK_ERROR_MASK 0x1E/* BI, FE, PE, OE bits */ -#define SC16IS7XX_LSR_THRE_BIT (1 << 5)/* TX holding register empty */ -#define SC16IS7XX_LSR_TEMT_BIT (1 << 6)/* Transmitter empty */ -#define SC16IS7XX_LSR_FIFOE_BIT (1 << 7)/* Fifo Error */ +#define SC16IS7XX_LSR_DR_BIT BIT(0)/* Receiver data ready */ +#define SC16IS7XX_LSR_OE_BIT BIT(1)/* Overrun Error */ +#define SC16IS7XX_LSR_PE_BIT BIT(2)/* Parity Error */ +#define SC16IS7XX_LSR_FE_BIT BIT(3)/* Frame Error */ +#define SC16IS7XX_LSR_BI_BIT BIT(4)/* Break Interrupt */ +#define SC16IS7XX_LSR_BRK_ERROR_MASK \ + (SC16IS7XX_LSR_OE_BIT | \ + SC16IS7XX_LSR_PE_BIT | \ + SC16IS7XX_LSR_FE_BIT | \ + SC16IS7XX_LSR_BI_BIT) + +#define SC16IS7XX_LSR_THRE_BIT BIT(5)/* TX holding register empty */ +#define SC16IS7XX_LSR_TEMT_BIT BIT(6)/* Transmitter empty */ +#define SC16IS7XX_LSR_FIFOE_BIT BIT(7)/* Fifo Error */ /* MSR register bits */ -#define SC16IS7XX_MSR_DCTS_BIT (1 << 0)/* Delta CTS Clear To Send */ -#define SC16IS7XX_MSR_DDSR_BIT (1 << 1)/* Delta DSR Data Set Ready +#define SC16IS7XX_MSR_DCTS_BIT BIT(0)/* Delta CTS Clear To Send */ +#define SC16IS7XX_MSR_DDSR_BIT BIT(1)/* Delta DSR Data Set Ready * or (IO4) * - only on 75x/76x */ -#define SC16IS7XX_MSR_DRI_BIT (1 << 2)/* Delta RI Ring Indicator +#define SC16IS7XX_MSR_DRI_BIT BIT(2)/* Delta RI Ring Indicator * or (IO7) * - only on 75x/76x */ -#define SC16IS7XX_MSR_DCD_BIT (1 << 3)/* Delta CD Carrier Detect +#define SC16IS7XX_MSR_DCD_BIT BIT(3)/* Delta CD Carrier Detect * or (IO6) * - only on 75x/76x */ -#define SC16IS7XX_MSR_CTS_BIT (1 << 4)/* CTS */ -#define SC16IS7XX_MSR_DSR_BIT (1 << 5)/* DSR (IO4) +#define SC16IS7XX_MSR_CTS_BIT BIT(4)/* CTS */ +#define SC16IS7XX_MSR_DSR_BIT BIT(5)/* DSR (IO4) * - only on 75x/76x */ -#define SC16IS7XX_MSR_RI_BIT (1 << 6)/* RI (IO7) +#define SC16IS7XX_MSR_RI_BIT BIT(6)/* RI (IO7) * - only on 75x/76x */ -#define SC16IS7XX_MSR_CD_BIT (1 << 7)/* CD (IO6) +#define SC16IS7XX_MSR_CD_BIT BIT(7)/* CD (IO6) * - only on 75x/76x */ #define SC16IS7XX_MSR_DELTA_MASK 0x0F/* Any of the delta bits! */
@@ -236,19 +242,19 @@#define SC16IS7XX_TLR_RX_TRIGGER (words) ((((words) / 4) & 0x0f) << 4)/* IOControl register bits (Only 750/760) */ -#define SC16IS7XX_IOCONTROL_LATCH_BIT (1 << 0)/* Enable input latching */ -#define SC16IS7XX_IOCONTROL_MODEM_A_BIT (1 << 1)/* Enable GPIO[7:4] as modem A pins */ -#define SC16IS7XX_IOCONTROL_MODEM_B_BIT (1 << 2)/* Enable GPIO[3:0] as modem B pins */ -#define SC16IS7XX_IOCONTROL_SRESET_BIT (1 << 3)/* Software Reset */ +#define SC16IS7XX_IOCONTROL_LATCH_BIT BIT(0)/* Enable input latching */ +#define SC16IS7XX_IOCONTROL_MODEM_A_BIT BIT(1)/* Enable GPIO[7:4] as modem A pins */ +#define SC16IS7XX_IOCONTROL_MODEM_B_BIT BIT(2)/* Enable GPIO[3:0] as modem B pins */ +#define SC16IS7XX_IOCONTROL_SRESET_BIT BIT(3)/* Software Reset */ /* EFCR register bits */ -#define SC16IS7XX_EFCR_9BIT_MODE_BIT (1 << 0)/* Enable 9-bit or Multidrop +#define SC16IS7XX_EFCR_9BIT_MODE_BIT BIT(0)/* Enable 9-bit or Multidrop * mode (RS485) */ -#define SC16IS7XX_EFCR_RXDISABLE_BIT (1 << 1)/* Disable receiver */ -#define SC16IS7XX_EFCR_TXDISABLE_BIT (1 << 2)/* Disable transmitter */ -#define SC16IS7XX_EFCR_AUTO_RS485_BIT (1 << 4)/* Auto RS485 RTS direction */ -#define SC16IS7XX_EFCR_RTS_INVERT_BIT (1 << 5)/* RTS output inversion */ -#define SC16IS7XX_EFCR_IRDA_MODE_BIT (1 << 7)/* IrDA mode +#define SC16IS7XX_EFCR_RXDISABLE_BIT BIT(1)/* Disable receiver */ +#define SC16IS7XX_EFCR_TXDISABLE_BIT BIT(2)/* Disable transmitter */ +#define SC16IS7XX_EFCR_AUTO_RS485_BIT BIT(4)/* Auto RS485 RTS direction */ +#define SC16IS7XX_EFCR_RTS_INVERT_BIT BIT(5)/* RTS output inversion */ +#define SC16IS7XX_EFCR_IRDA_MODE_BIT BIT(7)/* IrDA mode * 0 = rate upto 115.2 kbit/s * - Only 750/760 * 1 = rate upto 1.152 Mbit/s
@@ -256,15 +262,15 @@*/ /* EFR register bits */ -#define SC16IS7XX_EFR_AUTORTS_BIT (1 << 6)/* Auto RTS flow ctrl enable */ -#define SC16IS7XX_EFR_AUTOCTS_BIT (1 << 7)/* Auto CTS flow ctrl enable */ -#define SC16IS7XX_EFR_XOFF2_DETECT_BIT (1 << 5)/* Enable Xoff2 detection */ -#define SC16IS7XX_EFR_ENABLE_BIT (1 << 4)/* Enable enhanced functions +#define SC16IS7XX_EFR_AUTORTS_BIT BIT(6)/* Auto RTS flow ctrl enable */ +#define SC16IS7XX_EFR_AUTOCTS_BIT BIT(7)/* Auto CTS flow ctrl enable */ +#define SC16IS7XX_EFR_XOFF2_DETECT_BIT BIT(5)/* Enable Xoff2 detection */ +#define SC16IS7XX_EFR_ENABLE_BIT BIT(4)/* Enable enhanced functions * and writing to IER[7:4], * FCR[5:4], MCR[7:5] */ -#define SC16IS7XX_EFR_SWFLOW3_BIT (1 << 3) -#define SC16IS7XX_EFR_SWFLOW2_BIT (1 << 2) +#define SC16IS7XX_EFR_SWFLOW3_BIT BIT(3) +#define SC16IS7XX_EFR_SWFLOW2_BIT BIT(2)/* * SWFLOW bits 3 & 2 table: * 00 -> no transmitter flow
@@ -277,8 +283,8 @@* XON1, XON2, XOFF1 and * XOFF2 */ -#define SC16IS7XX_EFR_SWFLOW1_BIT (1 << 1) -#define SC16IS7XX_EFR_SWFLOW0_BIT (1 << 0) +#define SC16IS7XX_EFR_SWFLOW1_BIT BIT(1) +#define SC16IS7XX_EFR_SWFLOW0_BIT BIT(0)/* * SWFLOW bits 1 & 0 table: * 00 -> no received flow
@@ -311,9 +317,9 @@ struct sc16is7xx_devtype {int nr_uart; }; -#define SC16IS7XX_RECONF_MD (1 << 0) -#define SC16IS7XX_RECONF_IER (1 << 1) -#define SC16IS7XX_RECONF_RS485 (1 << 2) +#define SC16IS7XX_RECONF_MD BIT(0) +#define SC16IS7XX_RECONF_IER BIT(1) +#define SC16IS7XX_RECONF_RS485 BIT(2)struct sc16is7xx_one_config {unsigned int flags;
Greg Kroah-Hartman
maintainerPATCH 23/79 serial: sc16is7xx: rename EFR mutex with generic name
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hugo Villeneuve <[email protected]>
[ Upstream commit d9b2d7ddbb973b981c20b21e9228581bb156f66f ]
This mutex is used as a lock when accessing registers that share the same address space, not necessarily EFR registers.
For example, address 0x06 is shared by MSR, TCR and XOFF1 registers, independently of EFR.
Rename the mutex with a more generic name to avoid misinterpreting its usage.
Stable-dep-of: 246ac114f485 ("serial: sc16is7xx: enable THRI before filling TX FIFO")
--- drivers/tty/serial/sc16is7xx.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)
drivers/tty/serial/sc16is7xx.c +13 -13
@@ -330,7 +330,7 @@ struct sc16is7xx_one_config {struct sc16is7xx_one {struct uart_port port;struct regmap *regmap; -struct mutex efr_lock;/* EFR registers access */ +struct mutex lock;/* For registers sharing same address space. */ struct kthread_work tx_work;struct kthread_work reg_work;struct kthread_delayed_work ms_work;
@@ -437,7 +437,7 @@ static void sc16is7xx_efr_lock(struct ua {struct sc16is7xx_one *one =to_sc16is7xx_one (port, port); -mutex_lock (&one->efr_lock); +mutex_lock (&one->lock);/* Backup content of LCR. */ one->old_lcr =sc16is7xx_port_read (port,SC16IS7XX_LCR_REG );
@@ -459,7 +459,7 @@ static void sc16is7xx_efr_unlock(struct/* Restore original content of LCR */ sc16is7xx_port_write (port,SC16IS7XX_LCR_REG , one->old_lcr); -mutex_unlock (&one->efr_lock); +mutex_unlock (&one->lock); }static void sc16is7xx_ier_clear (struct uart_port *port,u8 bit)
@@ -594,7 +594,7 @@ static int sc16is7xx_set_baud(struct uar prescaler == 1 ? 0 : SC16IS7XX_MCR_CLKSEL_BIT); -mutex_lock (&one->efr_lock); +mutex_lock (&one->lock);/* Backup LCR and access special register set (DLL/DLH) */ lcr =sc16is7xx_port_read (port,SC16IS7XX_LCR_REG );
@@ -610,7 +610,7 @@ static int sc16is7xx_set_baud(struct uar/* Restore LCR and access to general register set */ sc16is7xx_port_write (port,SC16IS7XX_LCR_REG , lcr); -mutex_unlock (&one->efr_lock); +mutex_unlock (&one->lock);return DIV_ROUND_CLOSEST ((clk / prescaler) /16 , div); }
@@ -768,7 +768,7 @@ static void sc16is7xx_update_mlines(struunsigned long flags;unsigned int status, changed; -lockdep_assert_held_once (&one->efr_lock); +lockdep_assert_held_once (&one->lock); status =sc16is7xx_get_hwmctrl (port); changed = status ^ one->old_mctrl;
@@ -799,7 +799,7 @@ static bool sc16is7xx_port_irq(struct scstruct uart_port *port = &s->p[portno].port;struct sc16is7xx_one *one =to_sc16is7xx_one (port, port); -mutex_lock (&one->efr_lock); +mutex_lock (&one->lock); iir =sc16is7xx_port_read (port,SC16IS7XX_IIR_REG );if (iir &SC16IS7XX_IIR_NO_INT_BIT ) {
@@ -846,7 +846,7 @@ static bool sc16is7xx_port_irq(struct sc } out_port_irq: -mutex_unlock (&one->efr_lock); +mutex_unlock (&one->lock);return rc; }
@@ -878,9 +878,9 @@ static void sc16is7xx_tx_proc(struct kth (port->rs485.delay_rts_before_send > 0))msleep (port->rs485.delay_rts_before_send); -mutex_lock (&one->efr_lock); +mutex_lock (&one->lock);sc16is7xx_handle_tx (port); -mutex_unlock (&one->efr_lock); +mutex_unlock (&one->lock); }static void sc16is7xx_reconf_rs485 (struct uart_port *port)
@@ -947,9 +947,9 @@ static void sc16is7xx_ms_proc(struct kthstruct sc16is7xx_port *s =dev_get_drvdata (one->port.dev);if (one->port.state) { -mutex_lock (&one->efr_lock); +mutex_lock (&one->lock);sc16is7xx_update_mlines (one); -mutex_unlock (&one->efr_lock); +mutex_unlock (&one->lock);kthread_queue_delayed_work (&s->kworker, &one->ms_work,HZ ); }
@@ -1567,7 +1567,7 @@ static int sc16is7xx_probe(struct device s->p[i].old_mctrl =0 ; s->p[i].regmap = regmaps[i]; -mutex_init (&s->p[i].efr_lock); +mutex_init (&s->p[i].lock); ret =uart_get_rs485_mode (&s->p[i].port);if (ret)
Greg Kroah-Hartman
maintainerPATCH 24/79 serial: sc16is7xx: use guards for simple mutex locks
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hugo Villeneuve <[email protected]>
[ Upstream commit 0f4f88bfd7e7bf3f3293045fffdc63586b0a889f ]
Guards can help to make the code more readable, so use them wherever they do so.
In sc16is7xx_port_irq(), labels and 'rc' locals are eliminated completely.
Stable-dep-of: 246ac114f485 ("serial: sc16is7xx: enable THRI before filling TX FIFO")
--- drivers/tty/serial/sc16is7xx.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-)
drivers/tty/serial/sc16is7xx.c +8 -15
@@ -10,6 +10,7 @@#include <linux/bitops.h> #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h>
@@ -794,18 +795,15 @@ static void sc16is7xx_update_mlines(strustatic bool sc16is7xx_port_irq (struct sc16is7xx_port *s,int portno) { -bool rc = true;unsigned int iir, rxlen;struct uart_port *port = &s->p[portno].port;struct sc16is7xx_one *one =to_sc16is7xx_one (port, port); -mutex_lock (&one->lock); +guard (mutex)(&one->lock); iir =sc16is7xx_port_read (port,SC16IS7XX_IIR_REG ); -if (iir &SC16IS7XX_IIR_NO_INT_BIT ) { - rc = false; - goto out_port_irq; - } +if (iir &SC16IS7XX_IIR_NO_INT_BIT ) +return false; iir &=SC16IS7XX_IIR_ID_MASK ;
@@ -845,10 +843,7 @@ static bool sc16is7xx_port_irq(struct scbreak ; } -out_port_irq: -mutex_unlock (&one->lock); - -return rc; +return true; }static irqreturn_t sc16is7xx_irq (int irq,void *dev_id)
@@ -878,9 +873,8 @@ static void sc16is7xx_tx_proc(struct kth (port->rs485.delay_rts_before_send > 0))msleep (port->rs485.delay_rts_before_send); -mutex_lock (&one->lock); +guard (mutex)(&one->lock);sc16is7xx_handle_tx (port); -mutex_unlock (&one->lock); }static void sc16is7xx_reconf_rs485 (struct uart_port *port)
@@ -947,9 +941,8 @@ static void sc16is7xx_ms_proc(struct kthstruct sc16is7xx_port *s =dev_get_drvdata (one->port.dev);if (one->port.state) { -mutex_lock (&one->lock); -sc16is7xx_update_mlines (one); -mutex_unlock (&one->lock); + scoped_guard(mutex, &one->lock) +sc16is7xx_update_mlines (one);kthread_queue_delayed_work (&s->kworker, &one->ms_work,HZ ); }
Greg Kroah-Hartman
maintainerPATCH 25/79 serial: sc16is7xx: enable THRI before filling TX FIFO
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luca Fresi <[email protected]>
[ Upstream commit 246ac114f485c2affb454240f3ea4fabfce22456 ]
sc16is7xx_handle_tx() currently requests the THRI enable only after it has filled the TX FIFO. The request is asynchronous because the IER update is performed later by reg_work.
The SC16IS7xx generates a THRI interrupt when the TX FIFO crosses its trigger level. If the FIFO drains past that level before reg_work enables THRI, the chip does not generate a new interrupt. Characters remain queued indefinitely even though the hardware FIFO is empty.
This was observed on an SC16IS752 while both UART channels were active. During the stall the software TX buffer remained non-empty while TXLVL reported 64 bytes free, LSR reported THR and transmitter empty, IER had THRI enabled, and IIR reported no interrupt pending.
Enable THRI synchronously before filling the FIFO so the threshold crossing cannot be missed.
--- drivers/tty/serial/sc16is7xx.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/tty/serial/sc16is7xx.c +3 -0
@@ -874,6 +874,9 @@ static void sc16is7xx_tx_proc(struct kthmsleep (port->rs485.delay_rts_before_send);guard (mutex)(&one->lock); +sc16is7xx_port_update (port,SC16IS7XX_IER_REG , +SC16IS7XX_IER_THRI_BIT , +SC16IS7XX_IER_THRI_BIT );sc16is7xx_handle_tx (port); }
Greg Kroah-Hartman
maintainerPATCH 26/79 net/packet: convert po->pressure to an atomic flag
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 791a3e9f1a86fe8eb09173c9788493b8b5c957f4 ]
Not only this removes some READ_ONCE()/WRITE_ONCE(), this also removes one integer.
Stable-dep-of: 1a35da325cac ("packet: synchronize pressure clearing with ring reconfiguration")
--- net/packet/af_packet.c | 14 ++++++++------ net/packet/internal.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-)
net/packet/af_packet.c +8 -6
@@ -1358,22 +1358,23 @@ static int __packet_rcv_has_room(const sstatic int packet_rcv_has_room (struct packet_sock *po,struct sk_buff *skb) { -int pressure, ret; +bool pressure; +int ret; ret =__packet_rcv_has_room (po, skb); pressure = ret !=ROOM_NORMAL ; -if (READ_ONCE (po->pressure) != pressure) -WRITE_ONCE (po->pressure, pressure); +if (packet_sock_flag (po,PACKET_SOCK_PRESSURE ) != pressure) +packet_sock_flag_set (po,PACKET_SOCK_PRESSURE , pressure);return ret; }static void packet_rcv_try_clear_pressure (struct packet_sock *po) { -if (READ_ONCE (po->pressure) && +if (packet_sock_flag (po,PACKET_SOCK_PRESSURE ) &&__packet_rcv_has_room (po,NULL ) ==ROOM_NORMAL ) -WRITE_ONCE (po->pressure,0 ); +packet_sock_flag_set (po,PACKET_SOCK_PRESSURE , false); }static void packet_sock_destruct (struct sock *sk)
@@ -1462,7 +1463,8 @@ static unsigned int fanout_demux_rollove i = j =min_t (int, po->rollover->sock, num -1 ); do { po_next = pkt_sk(rcu_dereference(f->arr[i])); - if (po_next != po_skip && !READ_ONCE(po_next->pressure) && + if (po_next != po_skip && + !packet_sock_flag(po_next, PACKET_SOCK_PRESSURE) && packet_rcv_has_room(po_next, skb) == ROOM_NORMAL) { if (i != j) po->rollover->sock = i;
net/packet/internal.h +1 -1
@@ -117,7 +117,6 @@ struct packet_sock {spinlock_t bind_lock;struct mutex pg_vec_lock;unsigned long flags; -int pressure;int ifindex;/* bound device */ __be16 num;struct packet_rollover *rollover;
@@ -146,6 +145,7 @@ enum packet_sock_flags {PACKET_SOCK_TP_LOSS ,PACKET_SOCK_HAS_VNET_HDR ,PACKET_SOCK_RUNNING , + PACKET_SOCK_PRESSURE, }; static inline void packet_sock_flag_set(struct packet_sock *po,
Greg Kroah-Hartman
maintainerPATCH 27/79 packet: synchronize pressure clearing with ring reconfiguration
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zihan Xi <[email protected]>
[ Upstream commit 1a35da325cac4d5bcad76a2aa943408a6f1d9000 ]
packet_set_ring() updates the RX ring state under sk_receive_queue.lock, but used to publish the tpacket receive mode through po->prot_hook.func after releasing that lock. packet_poll() and packet_recvmsg() can then run the pressure clearing path after the ring has been cleared while still seeing tpacket_rcv, causing __packet_rcv_has_room() to dereference stale or NULL ring storage.
Move the existing receive hook assignment into the same sk_receive_queue.lock section as the ring state update. Keep the assignment otherwise unchanged, including on TX ring reconfiguration, to avoid adding behavior changes that are not required for the fix.
Serialize packet_recvmsg() pressure clearing with the same queue lock only after PACKET_SOCK_PRESSURE has been observed. If the flag is clear and the socket has moved away from tpacket_rcv, packet_set_ring() has already detached the socket and waited for synchronize_net(), so no new packet input can set the flag again.
packet_poll() already holds sk_receive_queue.lock, so it uses the new unlocked helper directly.
--- net/packet/af_packet.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-)
net/packet/af_packet.c +16 -4
@@ -1370,13 +1370,25 @@ static int packet_rcv_has_room(struct pareturn ret; } -static void packet_rcv_try_clear_pressure (struct packet_sock *po) +static void __packet_rcv_try_clear_pressure (struct packet_sock *po) {if (packet_sock_flag (po,PACKET_SOCK_PRESSURE ) &&__packet_rcv_has_room (po,NULL ) ==ROOM_NORMAL )packet_sock_flag_set (po,PACKET_SOCK_PRESSURE , false); } +static void packet_rcv_try_clear_pressure (struct packet_sock *po) +{ +struct sock *sk = &po->sk; + +if (!packet_sock_flag (po,PACKET_SOCK_PRESSURE )) +return ; + +spin_lock_bh (&sk->sk_receive_queue.lock); +__packet_rcv_try_clear_pressure (po); +spin_unlock_bh (&sk->sk_receive_queue.lock); +} +static void packet_sock_destruct (struct sock *sk) {skb_queue_purge (&sk->sk_error_queue);
@@ -4342,7 +4354,7 @@ static __poll_t packet_poll(struct file TP_STATUS_KERNEL)) mask |= EPOLLIN | EPOLLRDNORM; } -packet_rcv_try_clear_pressure (po); +__packet_rcv_try_clear_pressure (po);spin_unlock_bh (&sk->sk_receive_queue.lock);spin_lock_bh (&sk->sk_write_queue.lock);if (po->tx_ring.pg_vec) {
@@ -4582,14 +4594,14 @@ static int packet_set_ring(struct sock * rb->frame_max = (req->tp_frame_nr -1 ); rb->head =0 ; rb->frame_size = req->tp_frame_size; + po->prot_hook.func = (po->rx_ring.pg_vec) ? + tpacket_rcv : packet_rcv;spin_unlock_bh (&rb_queue->lock);swap (rb->pg_vec_order, order);swap (rb->pg_vec_len, req->tp_block_nr); rb->pg_vec_pages = req->tp_block_size/PAGE_SIZE ; - po->prot_hook.func = (po->rx_ring.pg_vec) ? - tpacket_rcv : packet_rcv;skb_queue_purge (rb_queue);if (atomic_long_read (&po->mapped)) pr_err("packet_mmap: vma is busy: %ld\n",
Greg Kroah-Hartman
maintainerPATCH 28/79 inet: frags: publish queues before arming timer
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
[ Upstream commit 653d7ddf6cba867777a3d14c4f83ace008c5ad13 ]
inet_frag_create() arms the fragment queue timer before inserting the queue into the fqdir rhashtable. If the namespace fragment timeout is zero or negative, the timer can run before the queue is published.
The timer callback then marks the queue complete, tries to remove a node that is not in the hash table yet, and drops the anticipated hash reference. Creation can subsequently publish the completed queue without restoring that reference, leaving a stale hash node after the caller drops the remaining reference.
Publish the queue first and arm the timer while holding the queue lock. This makes timer expiry wait until the queue is visible in the hash table, so inet_frag_kill() can remove the node and balance the hash reference.
--- net/ipv4/inet_fragment.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
net/ipv4/inet_fragment.c +4 -2
@@ -339,16 +339,18 @@ static struct inet_frag_queue *inet_frag *prev =ERR_PTR (-ENOMEM );return NULL ; } -mod_timer (&q->timer, jiffies + fqdir->timeout); +spin_lock_bh (&q->lock); *prev =rhashtable_lookup_get_insert_key (&fqdir->rhashtable, &q->key, &q->node, f->rhash_params);if (*prev) { q->flags |=INET_FRAG_COMPLETE ; -inet_frag_kill (q); +spin_unlock_bh (&q->lock);inet_frag_destroy (q);return NULL ; } +mod_timer (&q->timer, jiffies + fqdir->timeout); +spin_unlock_bh (&q->lock);return q; }
Greg Kroah-Hartman
maintainerPATCH 29/79 mmc: atmel-mci: Fix use-after-free in atmci_remove due to race condition
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pei Xiao <[email protected]>
[ Upstream commit c125ee35a49a0518521b52b27631eef061b8719a ]
In atmci_probe, &host->bh_work is bound with atmci_work_func, and atmci_interrupt, atmci_timeout_timer and atmci_dma_complete can all queue this work on system_bh_wq.
If we remove the module, atmci_remove makes cleanup and the memory allocated for host with devm_kzalloc() is released after the remove callback returns, while the work mentioned above may still be pending or running. The sequence of operations that may lead to a UAF bug is as follows:
CPU0 CPU1
| atmci_interrupt
| queue_work(system_bh_wq,
| &host->bh_work)
atmci_remove |
atmci_cleanup_slot(...) |
atmci_writel(host, ATMCI_IDR, ~0UL) |
timer_delete_sync(&host->timer) |
dma_release_channel(host->dma.chan) |
free_irq(platform_get_irq(pdev, 0), host) |
| atmci_work_func
| // use host
// devm resources released after |
// remove returns, host is freed |
| // use host (use-after-free)Fix it by canceling the work after all the sources that can schedule it (IRQ handler, timeout timer and DMA completion callback) have been stopped, and before proceeding with the remaining cleanup in atmci_remove.
--- drivers/mmc/host/atmel-mci.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/mmc/host/atmel-mci.c +2 -0
@@ -2628,6 +2628,8 @@ static int atmci_remove(struct platform_free_irq (platform_get_irq (pdev,0 ), host); +cancel_work_sync (&host->bh_work); +clk_disable_unprepare (host->mck);pm_runtime_disable (&pdev->dev);
Greg Kroah-Hartman
maintainerPATCH 30/79 s390/vfio_ccw: Cancel existing workqueues
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Farman <[email protected]>
[ Upstream commit 79c60b2c61105368dcc8444eb45847e21734f7c4 ]
The initialization of the io_work and crw_work workqueues begs the question of whether they should be un-initialized. Add the corresponding cleanup tags in _release_dev to ensure work isn't dispatched after the private struct is free'd.
--- drivers/s390/cio/vfio_ccw_ops.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
drivers/s390/cio/vfio_ccw_ops.c +15 -0
@@ -87,6 +87,13 @@ static void vfio_ccw_mdev_release_dev(st container_of(vdev, struct vfio_ccw_private, vdev);/* +* Ensure these work items are fully drained, so none can +* fire after being released. +*/ +cancel_work_sync (&private->io_work); +cancel_work_sync (&private->crw_work); + +/* * We cannot free vfio_ccw_private here because it includes * parent info which must be free'ed by css driver. *
@@ -162,6 +169,14 @@ static void vfio_ccw_mdev_close_device(s container_of(vdev, struct vfio_ccw_private, vdev);vfio_ccw_fsm_event (private,VFIO_CCW_EVENT_CLOSE ); + +/* +* Ensure these work items are drained, in the event the +* device is re-opened instead of released. +*/ +cancel_work_sync (&private->io_work); +cancel_work_sync (&private->crw_work); +vfio_ccw_unregister_dev_regions (private); }
Greg Kroah-Hartman
maintainerPATCH 31/79 drm/amdgpu: disallow multiple FENCE chunks in one submit
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junrui Luo <[email protected]>
[ Upstream commit 931cd1d1baeae68e8eb2c23bc1f3d8934dca6241 ]
amdgpu_cs_pass1() dispatches on chunk_id once per chunk without rejecting repeated ids. p->uf_bo is a single-slot field, so a submission carrying two AMDGPU_CHUNK_ID_FENCE chunks runs amdgpu_cs_p1_user_fence() twice, and the second run overwrites p->uf_bo with a freshly referenced BO without dropping the reference taken by the first.
amdgpu_cs_parser_fini() only unrefs the final p->uf_bo, so every FENCE chunk but the last leaks a BO reference. The leaked BO outlives handle close and process exit.
Reject duplicate FENCE chunks the same way commit fec5f8e8c6bc ("drm/amdgpu: disallow multiple BO_HANDLES chunks in one submit") did for p->bo_list.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +4 -0
@@ -247,6 +247,10 @@ static int amdgpu_cs_pass1(struct amdgpuif (size <sizeof (struct drm_amdgpu_cs_chunk_fence )) goto free_partial_kdata; +/* Only a single user fence is allowed to simplify handling. */ +if (p->uf_entry.tv.bo) + goto free_partial_kdata; + ret =amdgpu_cs_p1_user_fence (p, p->chunks[i].kdata, &uf_offset);if (ret)
Greg Kroah-Hartman
maintainerPATCH 32/79 s390/vfio_ccw: Move cp cleanup out of not operational
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Farman <[email protected]>
[ Upstream commit 0c11f61a876ed6fcca53d442ed3f33ea8362a0f9 ]
The fsm_notoper() routine is called when the device has been lost, and is (by definition) no longer operational. Since this can happen asynchronously from the normal behavior of the driver, the cleanup may happen when holding other locks in the calling sequence (notably, the cio subchannel lock).
Push the cleanup of the private->cp resources to a workqueue, where it can be done out from under that lock sequence and a future patch can safely manage the locking requirements.
Stable-dep-of: 16b0798024c0 ("s390/vfio_ccw: Implement a crw lock")
--- drivers/s390/cio/vfio_ccw_drv.c | 10 ++++++++++ drivers/s390/cio/vfio_ccw_fsm.c | 3 +-- drivers/s390/cio/vfio_ccw_ops.c | 12 ++++++++++++ drivers/s390/cio/vfio_ccw_private.h | 2 ++ 4 files changed, 25 insertions(+), 2 deletions(-)
drivers/s390/cio/vfio_ccw_drv.c +10 -0
@@ -116,6 +116,15 @@ static void vfio_ccw_crw_todo(struct woreventfd_signal (private->crw_trigger,1 ); } +static void vfio_ccw_notoper_todo (struct work_struct *work) +{ +struct vfio_ccw_private *private; + + private = container_of(work, struct vfio_ccw_private, notoper_work); + +cp_free (&private->cp); +} +/* * Css driver callbacks */
@@ -141,6 +150,7 @@ static struct vfio_ccw_private *vfio_ccwINIT_LIST_HEAD (&private->crw);INIT_WORK (&private->io_work, vfio_ccw_sch_io_todo);INIT_WORK (&private->crw_work, vfio_ccw_crw_todo); +INIT_WORK (&private->notoper_work, vfio_ccw_notoper_todo); private->cp.guest_cp =kcalloc (CCWCHAIN_LEN_MAX ,sizeof (struct ccw1 ),GFP_KERNEL );
drivers/s390/cio/vfio_ccw_fsm.c +1 -2
@@ -176,8 +176,7 @@ static void fsm_notoper(struct vfio_ccw_css_sched_sch_todo (sch,SCH_TODO_UNREG ); private->state =VFIO_CCW_STATE_NOT_OPER ; -/* This is usually handled during CLOSE event */ -cp_free (&private->cp); +queue_work (vfio_ccw_work_q, &private->notoper_work); }/*
drivers/s390/cio/vfio_ccw_ops.c +12 -0
@@ -89,9 +89,16 @@ static void vfio_ccw_mdev_release_dev(st/* * Ensure these work items are fully drained, so none can * fire after being released. +* +* notoper_work should have nothing to do here, because only +* open devices could have channel_program resources in use +* and those would be released during close. Nevertheless, +* call flush here as well to be certain anything that was +* allocated is freed. */ cancel_work_sync (&private->io_work);cancel_work_sync (&private->crw_work); +flush_work (&private->notoper_work);/* * We cannot free vfio_ccw_private here because it includes
@@ -173,9 +180,14 @@ static void vfio_ccw_mdev_close_device(s/* * Ensure these work items are drained, in the event the * device is re-opened instead of released. +* +* notoper_work needs to be given a chance to run if it +* is queued, so any memory associated with the channel +* program can be returned. */ cancel_work_sync (&private->io_work);cancel_work_sync (&private->crw_work); +flush_work (&private->notoper_work);vfio_ccw_unregister_dev_regions (private); }
drivers/s390/cio/vfio_ccw_private.h +2 -0
@@ -88,6 +88,7 @@ struct vfio_ccw_crw {* @req_trigger: eventfd ctx for signaling userspace to return device * @io_work: work for deferral process of I/O handling * @crw_work: work for deferral process of CRW handling +* @notoper_work: work for deferred processing in not-operational state * @release_comp: synchronization helper for vfio device release * @parent: parent data structures for mdevs created */
@@ -114,6 +115,7 @@ struct vfio_ccw_private {struct eventfd_ctx *req_trigger;struct work_struct io_work;struct work_struct crw_work; +struct work_struct notoper_work;struct completion release_comp;
Greg Kroah-Hartman
maintainerPATCH 33/79 s390/vfio_ccw: Selectively expand io_mutex
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Farman <[email protected]>
[ Upstream commit 34f4feff3e90bd09308fad0974e97113b23b812a ]
The io_mutex was defined to serialize the io_regions, but then has also sort of been associated with the I/O themselves because of the close relationship they share.
With the handful of races that are possible, the choices are either to:
A) expand the scope of io_mutex to close these remaining windows, or
B) reduce the scope of io_mutex to just io_region, and introduce a new lock mechanism for the remaining I/O resources
This patch implements A, since B brings with it a lot more interactions that would need to be tracked and kept in a correct hierarchy. It also takes advantage of the workqueue element for cp_free() that now gets called out of fsm_notoper(), which could be invoked out of an interrupt context and thus cannot acquire a mutex itself.
Stable-dep-of: 16b0798024c0 ("s390/vfio_ccw: Implement a crw lock")
--- drivers/s390/cio/vfio_ccw_chp.c | 2 +- drivers/s390/cio/vfio_ccw_cp.c | 8 +++++++- drivers/s390/cio/vfio_ccw_drv.c | 6 ++++-- drivers/s390/cio/vfio_ccw_fsm.c | 5 +++++ drivers/s390/cio/vfio_ccw_private.h | 3 ++- 5 files changed, 19 insertions(+), 5 deletions(-)
drivers/s390/cio/vfio_ccw_chp.c +1 -1
@@ -97,13 +97,13 @@ static ssize_t vfio_ccw_crw_region_read(if (pos + count >sizeof (*region))return -EINVAL ; +mutex_lock (&private->io_mutex); crw =list_first_entry_or_null (&private->crw, struct vfio_ccw_crw, next);if (crw)list_del (&crw->next); -mutex_lock (&private->io_mutex);if (i >= private->num_regions) { ret = -EINVAL ; goto out;
drivers/s390/cio/vfio_ccw_cp.c +7 -1
@@ -904,17 +904,23 @@ void cp_update_scsw(struct channel_progr*/ bool cp_iova_pinned (struct channel_program *cp,u64 iova,u64 length) { +struct vfio_ccw_private *private = + container_of(cp, struct vfio_ccw_private, cp);struct ccwchain *chain;int i;if (!cp->initialized)return false; +mutex_lock (&private->io_mutex);list_for_each_entry (chain, &cp->ccwchain_list, next) {for (i =0 ; i < chain->ch_len; i++) -if (page_array_iova_pinned (chain->ch_pa + i, iova, length)) +if (page_array_iova_pinned (chain->ch_pa + i, iova, length)) { +mutex_unlock (&private->io_mutex);return true; + } } +mutex_unlock (&private->io_mutex);return false; }
drivers/s390/cio/vfio_ccw_drv.c +4 -2
@@ -82,6 +82,7 @@ static void vfio_ccw_sch_io_todo(struct is_final = !(scsw_actl (&irb->scsw) & (SCSW_ACTL_DEVACT |SCSW_ACTL_SCHACT )); +mutex_lock (&private->io_mutex);if (scsw_is_solicited (&irb->scsw)) {cp_update_scsw (&private->cp, &irb->scsw);if (is_final && private->state ==VFIO_CCW_STATE_CP_PENDING ) {
@@ -89,9 +90,7 @@ static void vfio_ccw_sch_io_todo(struct cp_is_finished = true; } } -mutex_lock (&private->io_mutex);memcpy (private->io_region->irb_area, irb,sizeof (*irb)); -mutex_unlock (&private->io_mutex);/* * Reset to IDLE only if processing of a channel program
@@ -101,6 +100,7 @@ static void vfio_ccw_sch_io_todo(struct*/ if (cp_is_finished) private->state =VFIO_CCW_STATE_IDLE ; +mutex_unlock (&private->io_mutex);if (private->io_trigger)eventfd_signal (private->io_trigger,1 );
@@ -122,7 +122,9 @@ static void vfio_ccw_notoper_todo(struct private = container_of(work, struct vfio_ccw_private, notoper_work); +mutex_lock (&private->io_mutex);cp_free (&private->cp); +mutex_unlock (&private->io_mutex); }/*
drivers/s390/cio/vfio_ccw_fsm.c +5 -0
@@ -176,6 +176,7 @@ static void fsm_notoper(struct vfio_ccw_css_sched_sch_todo (sch,SCH_TODO_UNREG ); private->state =VFIO_CCW_STATE_NOT_OPER ; +/* This routine could be called from IRQ context, so defer */ queue_work (vfio_ccw_work_q, &private->notoper_work); }
@@ -412,7 +413,11 @@ static void fsm_close(struct vfio_ccw_pr private->state =VFIO_CCW_STATE_STANDBY ;spin_unlock_irq (sch->lock); + +mutex_lock (&private->io_mutex);cp_free (&private->cp); +mutex_unlock (&private->io_mutex); +return ; err_unlock:
drivers/s390/cio/vfio_ccw_private.h +2 -1
@@ -74,7 +74,8 @@ struct vfio_ccw_crw {* @state: internal state of the device * @completion: synchronization helper of the I/O completion * @io_region: MMIO region to input/output I/O arguments/results -* @io_mutex: protect against concurrent update of I/O regions +* @io_mutex: protect against concurrent update of I/O resources +* and @cp lifecycle * @region: additional regions for other subchannel operations * @cmd_region: MMIO region for asynchronous I/O commands other than START * @schib_region: MMIO region for SCHIB information
Greg Kroah-Hartman
maintainerPATCH 34/79 s390/vfio_ccw: Implement a crw lock
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Farman <[email protected]>
[ Upstream commit 16b0798024c0e9117e395829ddbbe70981c79d9c ]
Unlike the channel_program struct, which covers synchronous I/O submissions and asynchronous interrupts, the CRW region relies exclusively on asynchronous events coming from hardware.
Implement a lock to manage the list of those payloads, to ensure they are read cohesively.
--- drivers/s390/cio/vfio_ccw_chp.c | 26 ++++++++++++++++---------- drivers/s390/cio/vfio_ccw_drv.c | 10 ++++++++++ drivers/s390/cio/vfio_ccw_private.h | 4 ++++ 3 files changed, 30 insertions(+), 10 deletions(-)
drivers/s390/cio/vfio_ccw_chp.c +16 -10
@@ -92,18 +92,13 @@ static ssize_t vfio_ccw_crw_region_read(loff_t pos = *ppos &VFIO_CCW_OFFSET_MASK ;struct ccw_crw_region *region;struct vfio_ccw_crw *crw; +unsigned long flags;int ret;if (pos + count >sizeof (*region))return -EINVAL ;mutex_lock (&private->io_mutex); - crw =list_first_entry_or_null (&private->crw, - struct vfio_ccw_crw, next); - -if (crw) -list_del (&crw->next); -if (i >= private->num_regions) { ret = -EINVAL ; goto out;
@@ -112,6 +107,16 @@ static ssize_t vfio_ccw_crw_region_read( i =array_index_nospec (i, private->num_regions); region = private->region[i].data; +spin_lock_irqsave (&private->crw_lock, flags); + crw =list_first_entry_or_null (&private->crw, + struct vfio_ccw_crw, next); + +if (crw) +list_del (&crw->next); + +/* Drop CRW lock while copying to userspace */ +spin_unlock_irqrestore (&private->crw_lock, flags); +if (crw)memcpy (®ion->crw, &crw->crw,sizeof (region->crw));
@@ -121,15 +126,16 @@ static ssize_t vfio_ccw_crw_region_read( ret = count; region->crw =0 ; - -out: -mutex_unlock (&private->io_mutex); -kfree (crw);/* Notify the guest if more CRWs are on our queue */ +spin_lock_irqsave (&private->crw_lock, flags);if (!list_empty (&private->crw) && private->crw_trigger)eventfd_signal (private->crw_trigger,1 ); +spin_unlock_irqrestore (&private->crw_lock, flags); + +out: +mutex_unlock (&private->io_mutex);return ret; }
drivers/s390/cio/vfio_ccw_drv.c +10 -0
@@ -109,11 +109,14 @@ static void vfio_ccw_sch_io_todo(structstatic void vfio_ccw_crw_todo (struct work_struct *work) {struct vfio_ccw_private *private; +unsigned long flags; private = container_of(work, struct vfio_ccw_private, crw_work); +spin_lock_irqsave (&private->crw_lock, flags);if (!list_empty (&private->crw) && private->crw_trigger)eventfd_signal (private->crw_trigger,1 ); +spin_unlock_irqrestore (&private->crw_lock, flags); }static void vfio_ccw_notoper_todo (struct work_struct *work)
@@ -153,6 +156,7 @@ static struct vfio_ccw_private *vfio_ccwINIT_WORK (&private->io_work, vfio_ccw_sch_io_todo);INIT_WORK (&private->crw_work, vfio_ccw_crw_todo);INIT_WORK (&private->notoper_work, vfio_ccw_notoper_todo); +spin_lock_init (&private->crw_lock); private->cp.guest_cp =kcalloc (CCWCHAIN_LEN_MAX ,sizeof (struct ccw1 ),GFP_KERNEL );
@@ -199,11 +203,14 @@ out_free_private:static void vfio_ccw_free_private (struct vfio_ccw_private *private) {struct vfio_ccw_crw *crw, *temp; +unsigned long flags; +spin_lock_irqsave (&private->crw_lock, flags);list_for_each_entry_safe (crw, temp, &private->crw, next) {list_del (&crw->next);kfree (crw); } +spin_unlock_irqrestore (&private->crw_lock, flags);kmem_cache_free (vfio_ccw_crw_region, private->crw_region);kmem_cache_free (vfio_ccw_schib_region, private->schib_region);
@@ -314,6 +321,7 @@ static void vfio_ccw_queue_crw(struct vf unsigned int rsid) {struct vfio_ccw_crw *crw; +unsigned long flags;/* * If unable to allocate a CRW, just drop the event and
@@ -331,7 +339,9 @@ static void vfio_ccw_queue_crw(struct vf crw->crw.erc = erc; crw->crw.rsid = rsid; +spin_lock_irqsave (&private->crw_lock, flags);list_add_tail (&crw->next, &private->crw); +spin_unlock_irqrestore (&private->crw_lock, flags);queue_work (vfio_ccw_work_q, &private->crw_work); }
drivers/s390/cio/vfio_ccw_private.h +4 -0
@@ -84,6 +84,8 @@ struct vfio_ccw_crw {* @cp: channel program for the current I/O operation * @irb: irb info received from interrupt * @scsw: scsw info +* @crw_lock: serialization of CRW list information +* @crw: list of Channel Report Word elements * @io_trigger: eventfd ctx for signaling userspace I/O results * @crw_trigger: eventfd ctx for signaling userspace CRW information * @req_trigger: eventfd ctx for signaling userspace to return device
@@ -109,6 +111,8 @@ struct vfio_ccw_private {struct channel_program cp;struct irb irb;union scsw scsw; + +spinlock_t crw_lock;struct list_head crw;struct eventfd_ctx *io_trigger;
Greg Kroah-Hartman
maintainerPATCH 35/79 xfs: dont use a xfs_log_iovec for ri_buf in log recovery
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christoph Hellwig <[email protected]>
[ Upstream commit ded74fddcaf685a9440c5612f7831d0c4c1473ca ]
ri_buf just holds a pointer/len pair and is not a log iovec used for writing to the log. Switch to use a kvec instead.
Stable-dep-of: 813f8136a2ce ("xfs: bounds-check buffer log item's dirty bitmap")
--- fs/xfs/libxfs/xfs_log_recover.h | 4 +-- fs/xfs/xfs_attr_item.c | 24 ++++++++++----------- fs/xfs/xfs_bmap_item.c | 18 ++++++++-------- fs/xfs/xfs_buf_item.c | 8 +++---- fs/xfs/xfs_buf_item.h | 2 - fs/xfs/xfs_buf_item_recover.c | 38 ++++++++++++++++----------------- fs/xfs/xfs_dquot_item_recover.c | 20 ++++++++--------- fs/xfs/xfs_extfree_item.c | 45 +++++++++++++++++++++------------------- fs/xfs/xfs_icreate_item.c | 2 - fs/xfs/xfs_inode_item.c | 6 ++--- fs/xfs/xfs_inode_item.h | 4 +-- fs/xfs/xfs_inode_item_recover.c | 26 +++++++++++------------ fs/xfs/xfs_log_recover.c | 16 +++++++------- fs/xfs/xfs_refcount_item.c | 16 +++++++------- fs/xfs/xfs_rmap_item.c | 16 +++++++------- fs/xfs/xfs_trans.h | 1 16 files changed, 124 insertions(+), 122 deletions(-)
fs/xfs/libxfs/xfs_log_recover.h +2 -2
@@ -95,7 +95,7 @@ struct xlog_recover_item {struct list_head ri_list;int ri_cnt;/* count of regions found */ int ri_total;/* total regions */ -struct xfs_log_iovec *ri_buf;/* ptr to regions buffer */ +struct kvec *ri_buf;/* ptr to regions buffer */ const struct xlog_recover_item_ops *ri_ops; };
@@ -108,7 +108,7 @@ struct xlog_recover {struct list_head r_itemq;/* q for items */ }; -#define ITEM_TYPE (i) (*(unsigned short *)(i)->ri_buf[0].i_addr) +#define ITEM_TYPE (i) (*(unsigned short *)(i)->ri_buf[0].iov_base)#define XLOG_RECOVER_CRCPASS 0#define XLOG_RECOVER_PASS1 1
fs/xfs/xfs_attr_item.c +12 -12
@@ -721,13 +721,13 @@ xlog_recover_attri_commit_pass2(/* Validate xfs_attri_log_format before the large memory allocation */ len =sizeof (struct xfs_attri_log_format ); -if (item->ri_buf[i].i_len != len) { +if (item->ri_buf[i].iov_len != len) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, - item->ri_buf[0 ].i_addr, item->ri_buf[0 ].i_len); + item->ri_buf[0 ].iov_base, item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; } - attri_formatp = item->ri_buf[i].i_addr; + attri_formatp = item->ri_buf[i].iov_base;if (!xfs_attri_validate (mp, attri_formatp)) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, attri_formatp, len);
@@ -762,14 +762,14 @@ xlog_recover_attri_commit_pass2( i++;/* Validate the attr name */ -if (item->ri_buf[i].i_len != +if (item->ri_buf[i].iov_len !=xlog_calc_iovec_len (attri_formatp->alfi_name_len)) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, attri_formatp, len);return -EFSCORRUPTED ; } - attr_name = item->ri_buf[i].i_addr; + attr_name = item->ri_buf[i].iov_base;if (!xfs_attr_namecheck (attr_name, attri_formatp->alfi_name_len)) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, attri_formatp, len);
@@ -779,14 +779,14 @@ xlog_recover_attri_commit_pass2(/* Validate the attr value, if present */ if (attri_formatp->alfi_value_len !=0 ) { -if (item->ri_buf[i].i_len !=xlog_calc_iovec_len (attri_formatp->alfi_value_len)) { +if (item->ri_buf[i].iov_len !=xlog_calc_iovec_len (attri_formatp->alfi_value_len)) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, - item->ri_buf[0 ].i_addr, - item->ri_buf[0 ].i_len); + item->ri_buf[0 ].iov_base, + item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; } - attr_value = item->ri_buf[i].i_addr; + attr_value = item->ri_buf[i].iov_base; i++; }
@@ -908,10 +908,10 @@ xlog_recover_attrd_commit_pass2( {struct xfs_attrd_log_format *attrd_formatp; - attrd_formatp = item->ri_buf[0 ].i_addr; -if (item->ri_buf[0 ].i_len !=sizeof (struct xfs_attrd_log_format )) { + attrd_formatp = item->ri_buf[0 ].iov_base; +if (item->ri_buf[0 ].iov_len !=sizeof (struct xfs_attrd_log_format )) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , log->l_mp, - item->ri_buf[0 ].i_addr, item->ri_buf[0 ].i_len); + item->ri_buf[0 ].iov_base, item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; }
fs/xfs/xfs_bmap_item.c +9 -9
@@ -623,24 +623,24 @@ xlog_recover_bui_commit_pass2(struct xfs_bui_log_format *bui_formatp;size_t len; - bui_formatp = item->ri_buf[0 ].i_addr; + bui_formatp = item->ri_buf[0 ].iov_base; -if (item->ri_buf[0 ].i_len <xfs_bui_log_format_sizeof (0 )) { +if (item->ri_buf[0 ].iov_len <xfs_bui_log_format_sizeof (0 )) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, - item->ri_buf[0 ].i_addr, item->ri_buf[0 ].i_len); + item->ri_buf[0 ].iov_base, item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; }if (bui_formatp->bui_nextents !=XFS_BUI_MAX_FAST_EXTENTS ) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, - item->ri_buf[0 ].i_addr, item->ri_buf[0 ].i_len); + item->ri_buf[0 ].iov_base, item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; } len =xfs_bui_log_format_sizeof (bui_formatp->bui_nextents); -if (item->ri_buf[0 ].i_len != len) { +if (item->ri_buf[0 ].iov_len != len) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, - item->ri_buf[0 ].i_addr, item->ri_buf[0 ].i_len); + item->ri_buf[0 ].iov_base, item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; }
@@ -677,10 +677,10 @@ xlog_recover_bud_commit_pass2( {struct xfs_bud_log_format *bud_formatp; - bud_formatp = item->ri_buf[0 ].i_addr; -if (item->ri_buf[0 ].i_len !=sizeof (struct xfs_bud_log_format )) { + bud_formatp = item->ri_buf[0 ].iov_base; +if (item->ri_buf[0 ].iov_len !=sizeof (struct xfs_bud_log_format )) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , log->l_mp, - item->ri_buf[0 ].i_addr, item->ri_buf[0 ].i_len); + item->ri_buf[0 ].iov_base, item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; }
fs/xfs/xfs_buf_item.c +4 -4
@@ -35,16 +35,16 @@ static inline struct xfs_buf_log_item *B/* Is this log iovec plausibly large enough to contain the buffer log format? */ bool xfs_buf_log_check_iovec ( -struct xfs_log_iovec *iovec) +struct kvec *iovec) { -struct xfs_buf_log_format *blfp = iovec->i_addr; +struct xfs_buf_log_format *blfp = iovec->iov_base;char *bmp_end;char *item_end; -if (offsetof(struct xfs_buf_log_format , blf_data_map) > iovec->i_len) +if (offsetof(struct xfs_buf_log_format , blf_data_map) > iovec->iov_len)return false; - item_end = (char *)iovec->i_addr + iovec->i_len; + item_end = (char *)iovec->iov_base + iovec->iov_len; bmp_end = (char *)&blfp->blf_data_map[blfp->blf_map_size];return bmp_end <= item_end; }
fs/xfs/xfs_buf_item.h +1 -1
@@ -67,7 +67,7 @@ static inline void xfs_buf_dquot_io_fail }#endif /* CONFIG_XFS_QUOTA */ void xfs_buf_iodone (struct xfs_buf *); -bool xfs_buf_log_check_iovec (struct xfs_log_iovec *iovec); +bool xfs_buf_log_check_iovec (struct kvec *iovec);extern struct kmem_cache *xfs_buf_item_cache;
fs/xfs/xfs_buf_item_recover.c +19 -19
@@ -154,7 +154,7 @@ STATIC enum xlog_recover_reorderxlog_recover_buf_reorder ( struct xlog_recover_item *item) { -struct xfs_buf_log_format *buf_f = item->ri_buf[0 ].i_addr; +struct xfs_buf_log_format *buf_f = item->ri_buf[0 ].iov_base;if (buf_f->blf_flags &XFS_BLF_CANCEL )return XLOG_REORDER_CANCEL_LIST ;
@@ -168,7 +168,7 @@ xlog_recover_buf_ra_pass2(struct xlog *log, struct xlog_recover_item *item) { - struct xfs_buf_log_format *buf_f = item->ri_buf[0].i_addr; + struct xfs_buf_log_format *buf_f = item->ri_buf[0].iov_base;xlog_buf_readahead (log, buf_f->blf_blkno, buf_f->blf_len,NULL ); }
@@ -182,11 +182,11 @@ xlog_recover_buf_commit_pass1(struct xlog *log, struct xlog_recover_item *item) { - struct xfs_buf_log_format *bf = item->ri_buf[0].i_addr; + struct xfs_buf_log_format *bf = item->ri_buf[0].iov_base;if (!xfs_buf_log_check_iovec (&item->ri_buf[0 ])) { -xfs_err (log->l_mp,"bad buffer log item size (%d)" , - item->ri_buf[0 ].i_len); +xfs_err (log->l_mp,"bad buffer log item size (%zd)" , + item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; }
@@ -467,8 +467,8 @@ xlog_recover_do_reg_buffer( nbits =xfs_contig_bits (buf_f->blf_data_map, buf_f->blf_map_size, bit);ASSERT (nbits >0 ); -ASSERT (item->ri_buf[i].i_addr !=NULL ); -ASSERT (item->ri_buf[i].i_len %XFS_BLF_CHUNK ==0 ); +ASSERT (item->ri_buf[i].iov_base !=NULL ); +ASSERT (item->ri_buf[i].iov_len %XFS_BLF_CHUNK ==0 );ASSERT (BBTOB (bp->b_length) >= ((uint )bit <<XFS_BLF_SHIFT ) + (nbits <<XFS_BLF_SHIFT ));
@@ -480,8 +480,8 @@ xlog_recover_do_reg_buffer(* the log. Hence we need to trim nbits back to the length of * the current region being copied out of the log. */ -if (item->ri_buf[i].i_len < (nbits <<XFS_BLF_SHIFT )) - nbits = item->ri_buf[i].i_len >>XFS_BLF_SHIFT ; +if (item->ri_buf[i].iov_len < (nbits <<XFS_BLF_SHIFT )) + nbits = item->ri_buf[i].iov_len >>XFS_BLF_SHIFT ;/* * Do a sanity check if this is a dquot buffer. Just checking
@@ -491,18 +491,18 @@ xlog_recover_do_reg_buffer( fa =NULL ;if (buf_f->blf_flags & (XFS_BLF_UDQUOT_BUF |XFS_BLF_PDQUOT_BUF |XFS_BLF_GDQUOT_BUF )) { -if (item->ri_buf[i].i_addr ==NULL ) { +if (item->ri_buf[i].iov_base ==NULL ) {xfs_alert (mp,"XFS: NULL dquot in %s." , __func__); goto next; } -if (item->ri_buf[i].i_len < size_disk_dquot) { +if (item->ri_buf[i].iov_len < size_disk_dquot) {xfs_alert (mp, -"XFS: dquot too small (%d) in %s." , - item->ri_buf[i].i_len, __func__); +"XFS: dquot too small (%zd) in %s." , + item->ri_buf[i].iov_len, __func__); goto next; } - fa =xfs_dquot_verify (mp, item->ri_buf[i].i_addr,-1 ); + fa =xfs_dquot_verify (mp, item->ri_buf[i].iov_base,-1 );if (fa) { xfs_alert(mp, "dquot corrupt at %pS trying to replay into block 0x%llx",
@@ -513,7 +513,7 @@ xlog_recover_do_reg_buffer(memcpy (xfs_buf_offset (bp, (uint )bit <<XFS_BLF_SHIFT ),/* dest */ - item->ri_buf[i].i_addr,/* source */ + item->ri_buf[i].iov_base,/* source */ nbits<<XFS_BLF_SHIFT );/* length */ next: i++;
@@ -649,8 +649,8 @@ xlog_recover_do_inode_buffer(if (next_unlinked_offset < reg_buf_offset)continue ; -ASSERT (item->ri_buf[item_index].i_addr !=NULL ); -ASSERT ((item->ri_buf[item_index].i_len %XFS_BLF_CHUNK ) ==0 ); +ASSERT (item->ri_buf[item_index].iov_base !=NULL ); +ASSERT ((item->ri_buf[item_index].iov_len %XFS_BLF_CHUNK ) ==0 );ASSERT ((reg_buf_offset + reg_buf_bytes) <=BBTOB (bp->b_length));/*
@@ -658,7 +658,7 @@ xlog_recover_do_inode_buffer(* current di_next_unlinked field. Extract its value * and copy it to the buffer copy. */ - logged_nextp = item->ri_buf[item_index].i_addr + + logged_nextp = item->ri_buf[item_index].iov_base + next_unlinked_offset - reg_buf_offset;if (XFS_IS_CORRUPT (mp, *logged_nextp ==0 )) { xfs_alert(mp,
@@ -887,7 +887,7 @@ xlog_recover_buf_commit_pass2( struct xlog_recover_item *item, xfs_lsn_t current_lsn) { -struct xfs_buf_log_format *buf_f = item->ri_buf[0 ].i_addr; +struct xfs_buf_log_format *buf_f = item->ri_buf[0 ].iov_base;struct xfs_mount *mp = log->l_mp;struct xfs_buf *bp;int error;
fs/xfs/xfs_dquot_item_recover.c +10 -10
@@ -34,10 +34,10 @@ xlog_recover_dquot_ra_pass2(if (mp->m_qflags ==0 )return ; - recddq = item->ri_buf[1 ].i_addr; + recddq = item->ri_buf[1 ].iov_base;if (recddq ==NULL )return ; -if (item->ri_buf[1 ].i_len <sizeof (struct xfs_disk_dquot )) +if (item->ri_buf[1 ].iov_len <sizeof (struct xfs_disk_dquot ))return ; type = recddq->d_type &XFS_DQTYPE_REC_MASK ;
@@ -45,7 +45,7 @@ xlog_recover_dquot_ra_pass2(if (log->l_quotaoffs_flag & type)return ; - dq_f = item->ri_buf[0 ].i_addr; + dq_f = item->ri_buf[0 ].iov_base;ASSERT (dq_f);ASSERT (dq_f->qlf_len ==1 );
@@ -79,14 +79,14 @@ xlog_recover_dquot_commit_pass2(if (mp->m_qflags ==0 )return 0 ; - recddq = item->ri_buf[1 ].i_addr; + recddq = item->ri_buf[1 ].iov_base;if (recddq ==NULL ) {xfs_alert (log->l_mp,"NULL dquot in %s." , __func__);return -EFSCORRUPTED ; } -if (item->ri_buf[1 ].i_len <sizeof (struct xfs_disk_dquot )) { -xfs_alert (log->l_mp,"dquot too small (%d) in %s." , - item->ri_buf[1 ].i_len, __func__); +if (item->ri_buf[1 ].iov_len <sizeof (struct xfs_disk_dquot )) { +xfs_alert (log->l_mp,"dquot too small (%zd) in %s." , + item->ri_buf[1 ].iov_len, __func__);return -EFSCORRUPTED ; }
@@ -108,7 +108,7 @@ xlog_recover_dquot_commit_pass2(* The other possibility, of course, is that the quota subsystem was * removed since the last mount - ENOSYS. */ - dq_f = item->ri_buf[0 ].i_addr; + dq_f = item->ri_buf[0 ].iov_base;ASSERT (dq_f); fa =xfs_dquot_verify (mp, recddq, dq_f->qlf_id);if (fa) {
@@ -147,7 +147,7 @@ xlog_recover_dquot_commit_pass2( } } -memcpy (ddq, recddq, item->ri_buf[1 ].i_len); +memcpy (ddq, recddq, item->ri_buf[1 ].iov_len);if (xfs_has_crc (mp)) {xfs_update_cksum ((char *)dqb,sizeof (struct xfs_dqblk ),XFS_DQUOT_CRC_OFF );
@@ -192,7 +192,7 @@ xlog_recover_quotaoff_commit_pass1(struct xlog *log, struct xlog_recover_item *item) { - struct xfs_qoff_logformat *qoff_f = item->ri_buf[0].i_addr; + struct xfs_qoff_logformat *qoff_f = item->ri_buf[0].iov_base;ASSERT (qoff_f);/*
fs/xfs/xfs_extfree_item.c +24 -21
92 lines not on this page · raw message
fs/xfs/xfs_icreate_item.c +1 -1
@@ -158,7 +158,7 @@ xlog_recover_icreate_commit_pass2(int nbufs;int i; - icl = (struct xfs_icreate_log *)item->ri_buf[0 ].i_addr; + icl = (struct xfs_icreate_log *)item->ri_buf[0 ].iov_base;if (icl->icl_type !=XFS_LI_ICREATE ) {xfs_warn (log->l_mp,"xlog_recover_do_icreate_trans: bad type" );return -EINVAL ;
fs/xfs/xfs_inode_item.c +3 -3
@@ -1177,12 +1177,12 @@ xfs_iflush_shutdown_abort(*/ int xfs_inode_item_format_convert ( -struct xfs_log_iovec *buf, +struct kvec *buf,struct xfs_inode_log_format *in_f) { -struct xfs_inode_log_format_32 *in_f32 = buf->i_addr; +struct xfs_inode_log_format_32 *in_f32 = buf->iov_base; -if (buf->i_len !=sizeof (*in_f32)) { +if (buf->iov_len !=sizeof (*in_f32)) {XFS_ERROR_REPORT (__func__,XFS_ERRLEVEL_LOW ,NULL );return -EFSCORRUPTED ; }
fs/xfs/xfs_inode_item.h +2 -2
@@ -46,8 +46,8 @@ extern void xfs_inode_item_init(struct xextern void xfs_inode_item_destroy (struct xfs_inode *);extern void xfs_iflush_abort (struct xfs_inode *);extern void xfs_iflush_shutdown_abort (struct xfs_inode *); -extern int xfs_inode_item_format_convert (xfs_log_iovec_t *, -struct xfs_inode_log_format *); +int xfs_inode_item_format_convert (struct kvec *buf, +struct xfs_inode_log_format *in_f);extern struct kmem_cache *xfs_ili_cache;
fs/xfs/xfs_inode_item_recover.c +13 -13
@@ -28,13 +28,13 @@ xlog_recover_inode_ra_pass2( struct xlog *log, struct xlog_recover_item *item) { -if (item->ri_buf[0 ].i_len ==sizeof (struct xfs_inode_log_format )) { -struct xfs_inode_log_format *ilfp = item->ri_buf[0 ].i_addr; +if (item->ri_buf[0 ].iov_len ==sizeof (struct xfs_inode_log_format )) { +struct xfs_inode_log_format *ilfp = item->ri_buf[0 ].iov_base;xlog_buf_readahead (log, ilfp->ilf_blkno, ilfp->ilf_len, &xfs_inode_buf_ra_ops); }else { -struct xfs_inode_log_format_32 *ilfp = item->ri_buf[0 ].i_addr; +struct xfs_inode_log_format_32 *ilfp = item->ri_buf[0 ].iov_base;xlog_buf_readahead (log, ilfp->ilf_blkno, ilfp->ilf_len, &xfs_inode_buf_ra_ops);
@@ -288,8 +288,8 @@ xlog_recover_inode_commit_pass2(int need_free =0 ;xfs_failaddr_t fa; -if (item->ri_buf[0 ].i_len ==sizeof (struct xfs_inode_log_format )) { - in_f = item->ri_buf[0 ].i_addr; +if (item->ri_buf[0 ].iov_len ==sizeof (struct xfs_inode_log_format )) { + in_f = item->ri_buf[0 ].iov_base; }else { in_f =kmem_alloc (sizeof (struct xfs_inode_log_format ),0 ); need_free =1 ;
@@ -327,7 +327,7 @@ xlog_recover_inode_commit_pass2( error = -EFSCORRUPTED ; goto out_release; } - ldip = item->ri_buf[1 ].i_addr; + ldip = item->ri_buf[1 ].iov_base;if (XFS_IS_CORRUPT (mp, ldip->di_magic !=XFS_DINODE_MAGIC )) { xfs_alert(mp, "%s: Bad inode log record, rec ptr "PTR_FMT", ino %lld",
@@ -432,12 +432,12 @@ xlog_recover_inode_commit_pass2( goto out_release; } isize =xfs_log_dinode_size (mp); -if (unlikely (item->ri_buf[1 ].i_len > isize)) { +if (unlikely (item->ri_buf[1 ].iov_len > isize)) {XFS_CORRUPTION_ERROR ("Bad log dinode size" ,XFS_ERRLEVEL_LOW , mp, ldip,sizeof (*ldip));xfs_alert (mp, -"Bad inode 0x%llx log dinode size 0x%x" , - in_f->ilf_ino, item->ri_buf[1 ].i_len); +"Bad inode 0x%llx log dinode size 0x%zx" , + in_f->ilf_ino, item->ri_buf[1 ].iov_len); error = -EFSCORRUPTED ; goto out_release; }
@@ -460,8 +460,8 @@ xlog_recover_inode_commit_pass2(if (in_f->ilf_size ==2 ) goto out_owner_change; - len = item->ri_buf[2 ].i_len; - src = item->ri_buf[2 ].i_addr; + len = item->ri_buf[2 ].iov_len; + src = item->ri_buf[2 ].iov_base;ASSERT (in_f->ilf_size <=4 );ASSERT ((in_f->ilf_size ==3 ) || (fields &XFS_ILOG_AFORK )); ASSERT(!(fields & XFS_ILOG_DFORK) ||
@@ -498,8 +498,8 @@ xlog_recover_inode_commit_pass2( }else { attr_index =2 ; } - len = item->ri_buf[attr_index].i_len; - src = item->ri_buf[attr_index].i_addr; + len = item->ri_buf[attr_index].iov_len; + src = item->ri_buf[attr_index].iov_base;ASSERT (len ==xlog_calc_iovec_len (in_f->ilf_asize));switch (in_f->ilf_fields &XFS_ILOG_AFORK ) {
fs/xfs/xfs_log_recover.c +8 -8
@@ -2088,15 +2088,15 @@ xlog_recover_add_to_cont_trans( item = list_entry(trans->r_itemq.prev, struct xlog_recover_item, ri_list); - old_ptr = item->ri_buf[item->ri_cnt-1 ].i_addr; - old_len = item->ri_buf[item->ri_cnt-1 ].i_len; + old_ptr = item->ri_buf[item->ri_cnt-1 ].iov_base; + old_len = item->ri_buf[item->ri_cnt-1 ].iov_len; ptr =kvrealloc (old_ptr, old_len, len + old_len,GFP_KERNEL );if (!ptr)return -ENOMEM ;memcpy (&ptr[old_len], dp, len); - item->ri_buf[item->ri_cnt-1 ].i_len += len; - item->ri_buf[item->ri_cnt-1 ].i_addr = ptr; + item->ri_buf[item->ri_cnt-1 ].iov_len += len; + item->ri_buf[item->ri_cnt-1 ].iov_base = ptr;trace_xfs_log_recover_item_add_cont (log, trans, item,0 );return 0 ; }
@@ -2181,7 +2181,7 @@ xlog_recover_add_to_trans( item->ri_total = in_f->ilf_size; item->ri_buf = -kmem_zalloc (item->ri_total *sizeof (xfs_log_iovec_t), +kmem_zalloc (item->ri_total *sizeof (*item->ri_buf),0 ); }
@@ -2195,8 +2195,8 @@ xlog_recover_add_to_trans( }/* Description region is ri_buf[0] */ - item->ri_buf[item->ri_cnt].i_addr = ptr; - item->ri_buf[item->ri_cnt].i_len = len; + item->ri_buf[item->ri_cnt].iov_base = ptr; + item->ri_buf[item->ri_cnt].iov_len = len; item->ri_cnt++;trace_xfs_log_recover_item_add (log, trans, item,0 );return 0 ;
@@ -2220,7 +2220,7 @@ xlog_recover_free_trans(/* Free the regions in the item. */ list_del (&item->ri_list);for (i =0 ; i < item->ri_cnt; i++) -kmem_free (item->ri_buf[i].i_addr); +kmem_free (item->ri_buf[i].iov_base);/* Free the item itself */ kmem_free (item->ri_buf);kmem_free (item);
fs/xfs/xfs_refcount_item.c +8 -8
@@ -650,18 +650,18 @@ xlog_recover_cui_commit_pass2(struct xfs_cui_log_format *cui_formatp;size_t len; - cui_formatp = item->ri_buf[0 ].i_addr; + cui_formatp = item->ri_buf[0 ].iov_base; -if (item->ri_buf[0 ].i_len <xfs_cui_log_format_sizeof (0 )) { +if (item->ri_buf[0 ].iov_len <xfs_cui_log_format_sizeof (0 )) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, - item->ri_buf[0 ].i_addr, item->ri_buf[0 ].i_len); + item->ri_buf[0 ].iov_base, item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; } len =xfs_cui_log_format_sizeof (cui_formatp->cui_nextents); -if (item->ri_buf[0 ].i_len != len) { +if (item->ri_buf[0 ].iov_len != len) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, - item->ri_buf[0 ].i_addr, item->ri_buf[0 ].i_len); + item->ri_buf[0 ].iov_base, item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; }
@@ -698,10 +698,10 @@ xlog_recover_cud_commit_pass2( {struct xfs_cud_log_format *cud_formatp; - cud_formatp = item->ri_buf[0 ].i_addr; -if (item->ri_buf[0 ].i_len !=sizeof (struct xfs_cud_log_format )) { + cud_formatp = item->ri_buf[0 ].iov_base; +if (item->ri_buf[0 ].iov_len !=sizeof (struct xfs_cud_log_format )) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , log->l_mp, - item->ri_buf[0 ].i_addr, item->ri_buf[0 ].i_len); + item->ri_buf[0 ].iov_base, item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; }
fs/xfs/xfs_rmap_item.c +8 -8
@@ -664,18 +664,18 @@ xlog_recover_rui_commit_pass2(struct xfs_rui_log_format *rui_formatp;size_t len; - rui_formatp = item->ri_buf[0 ].i_addr; + rui_formatp = item->ri_buf[0 ].iov_base; -if (item->ri_buf[0 ].i_len <xfs_rui_log_format_sizeof (0 )) { +if (item->ri_buf[0 ].iov_len <xfs_rui_log_format_sizeof (0 )) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, - item->ri_buf[0 ].i_addr, item->ri_buf[0 ].i_len); + item->ri_buf[0 ].iov_base, item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; } len =xfs_rui_log_format_sizeof (rui_formatp->rui_nextents); -if (item->ri_buf[0 ].i_len != len) { +if (item->ri_buf[0 ].iov_len != len) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, - item->ri_buf[0 ].i_addr, item->ri_buf[0 ].i_len); + item->ri_buf[0 ].iov_base, item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; }
@@ -712,10 +712,10 @@ xlog_recover_rud_commit_pass2( {struct xfs_rud_log_format *rud_formatp; - rud_formatp = item->ri_buf[0 ].i_addr; -if (item->ri_buf[0 ].i_len !=sizeof (struct xfs_rud_log_format )) { + rud_formatp = item->ri_buf[0 ].iov_base; +if (item->ri_buf[0 ].iov_len !=sizeof (struct xfs_rud_log_format )) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , log->l_mp, - rud_formatp, item->ri_buf[0 ].i_len); + rud_formatp, item->ri_buf[0 ].iov_len);return -EFSCORRUPTED ; }
fs/xfs/xfs_trans.h +0 -1
@@ -15,7 +15,6 @@ struct xfs_efd_log_item;struct xfs_efi_log_item ;struct xfs_inode ;struct xfs_item_ops ; -struct xfs_log_iovec ;struct xfs_mount ;struct xfs_trans ;struct xfs_trans_res ;
Greg Kroah-Hartman
maintainerPATCH 36/79 xfs: bounds-check buffer log items dirty bitmap
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ibrahim Hashimov <[email protected]>
[ Upstream commit 813f8136a2ce1fee266d02a7df73db6e8a541604 ]
xlog_recover_do_reg_buffer() replays each dirty region described by a buffer log item's bitmap into the buffer read for that item:
memcpy(xfs_buf_offset(bp, (uint)bit << XFS_BLF_SHIFT), item->ri_buf[i].iov_base, nbits << XFS_BLF_SHIFT);
The destination offset (bit/nbits, from the logged dirty bitmap) and the buffer size (from the logged blf_len) are both attacker-controlled and otherwise unrelated, yet the only thing bounding the copy is an ASSERT(), which compiles away on production kernels. A crafted image logging a small blf_len together with a bitmap bit past the end of that buffer drives the memcpy() past the buffer's allocation, corrupting adjacent kernel heap during mount-time log recovery. This is reachable by anyone who can get a crafted image mounted -- the malicious-filesystem threat model XFS already guards against elsewhere.
Turn the ASSERT() into a real XFS_IS_CORRUPT() check that aborts recovery of the buffer with -EFSCORRUPTED, consistent with the validate-and-fail idiom already used in xlog_recover_do_inode_buffer() and xfs_dquot_item_recover.c. xlog_recover_do_reg_buffer() therefore becomes STATIC int and its three callers propagate the error.
Found and confirmed with KASAN on a CONFIG_XFS_DEBUG=n build: the crafted image trips a slab-out-of-bounds write before this change and fails recovery cleanly with -EFSCORRUPTED after it.
--- fs/xfs/xfs_buf_item_recover.c | 53 ++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 15 deletions(-)
fs/xfs/xfs_buf_item_recover.c +38 -15
@@ -441,7 +441,7 @@ xlog_recover_validate_buf_type(* given buffer. The bitmap in the buf log format structure indicates * where to place the logged data. */ -STATIC void +STATIC int xlog_recover_do_reg_buffer( struct xfs_mount *mp, struct xlog_recover_item *item,
@@ -469,8 +469,24 @@ xlog_recover_do_reg_buffer(ASSERT (nbits >0 );ASSERT (item->ri_buf[i].iov_base !=NULL );ASSERT (item->ri_buf[i].iov_len %XFS_BLF_CHUNK ==0 ); -ASSERT (BBTOB (bp->b_length) >= - ((uint )bit <<XFS_BLF_SHIFT ) + (nbits <<XFS_BLF_SHIFT )); +/* +* The bitmap is only trustworthy to the extent that it +* describes a region that actually fits inside the buffer we +* read in based on the (attacker-controlled) blf_len. Do not +* rely on an ASSERT() for this -- it compiles away entirely on +* non-DEBUG kernels, which is exactly where this matters, so +* validate it for real and abort recovery of this buffer rather +* than copying past the end of it. +*/ +if (XFS_IS_CORRUPT (mp,BBTOB (bp->b_length) < + ((uint )bit <<XFS_BLF_SHIFT ) + + (nbits <<XFS_BLF_SHIFT ))) { +xfs_alert (mp, +"Bad buffer log item dirty bitmap (bit %d, nbits %d) for %d-byte buffer at daddr 0x%llx." , + bit, nbits,BBTOB (bp->b_length), +xfs_buf_daddr (bp)); +return -EFSCORRUPTED ; + }/* * The dirty regions logged in the buffer, even though
@@ -524,6 +540,7 @@ xlog_recover_do_reg_buffer(ASSERT (i == item->ri_total);xlog_recover_validate_buf_type (mp, bp, buf_f, current_lsn); +return 0 ; }/*
@@ -532,10 +549,10 @@ xlog_recover_do_reg_buffer(* (ie. USR or GRP), then just toss this buffer away; don't recover it. * Else, treat it as a regular buffer and do recovery. * -* Return false if the buffer was tossed and true if we recovered the buffer to -* indicate to the caller if the buffer needs writing. +* Return 0 if the buffer was not recovered (tossed), 1 if it was recovered and +* needs writing, or a negative errno if recovery of the buffer failed. */ -STATIC bool +STATIC int xlog_recover_do_dquot_buffer( struct xfs_mount *mp, struct xlog *log,
@@ -544,6 +561,7 @@ xlog_recover_do_dquot_buffer( struct xfs_buf_log_format *buf_f) {uint type; +int error;trace_xfs_log_recover_buf_dquot_buf (log, buf_f);
@@ -551,7 +569,7 @@ xlog_recover_do_dquot_buffer(* Filesystems are required to send in quota flags at mount time. */ if (!mp->m_qflags) -return false; +return 0 ; type =0 ;if (buf_f->blf_flags &XFS_BLF_UDQUOT_BUF )
@@ -564,10 +582,12 @@ xlog_recover_do_dquot_buffer(* This type of quotas was turned off, so ignore this buffer */ if (log->l_quotaoffs_flag & type) -return false; +return 0 ; -xlog_recover_do_reg_buffer (mp, item, bp, buf_f,NULLCOMMITLSN ); -return true; + error =xlog_recover_do_reg_buffer (mp, item, bp, buf_f,NULLCOMMITLSN ); +if (error) +return error; +return 1 ; }/*
@@ -962,13 +982,16 @@ xlog_recover_buf_commit_pass2( goto out_release; }else if (buf_f->blf_flags & (XFS_BLF_UDQUOT_BUF |XFS_BLF_PDQUOT_BUF |XFS_BLF_GDQUOT_BUF )) { -bool dirty; - - dirty =xlog_recover_do_dquot_buffer (mp, log, item, bp, buf_f); -if (!dirty) + error =xlog_recover_do_dquot_buffer (mp, log, item, bp, buf_f); +if (error <=0 ) goto out_release; +/* write dirty buffer */ + error =0 ; }else { -xlog_recover_do_reg_buffer (mp, item, bp, buf_f, current_lsn); + error =xlog_recover_do_reg_buffer (mp, item, bp, buf_f, + current_lsn); +if (error) + goto out_release; }/*
Greg Kroah-Hartman
maintainerPATCH 37/79 drm/amdgpu: check ASPM on the dGPU host link
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yang Wang <[email protected]>
[ Upstream commit 2a9c5154a5650c09ad44ff5e1dff74754e15a3c6 ]
dGPUs with an internal PCIe switch expose graphics functions below the switch downstream port. The automatic ASPM check uses the display endpoint and evaluates the internal link instead of the host link.
Use the switch upstream port for the check and report the selected link.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 50 ++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +49 -1
@@ -1375,6 +1375,31 @@ bool amdgpu_device_pcie_dynamic_switchinreturn true; } +/* +* Some dGPUs expose their display endpoint below an internal PCIe switch. +* Use the switch upstream port to query the host-facing link. +*/ +static struct pci_dev *amdgpu_device_get_aspm_pdev (struct amdgpu_device *adev) +{ +struct pci_dev *swds, *swus; + + swds =pci_upstream_bridge (adev->pdev); +if (!swds || + (swds->vendor !=PCI_VENDOR_ID_ATI && + swds->vendor !=PCI_VENDOR_ID_AMD ) || +pci_pcie_type (swds) !=PCI_EXP_TYPE_DOWNSTREAM ) +return adev->pdev; + + swus =pci_upstream_bridge (swds); +if (!swus || + (swus->vendor !=PCI_VENDOR_ID_ATI && + swus->vendor !=PCI_VENDOR_ID_AMD ) || +pci_pcie_type (swus) !=PCI_EXP_TYPE_UPSTREAM ) +return adev->pdev; + +return swus; +} +/** * amdgpu_device_should_use_aspm - check if the device should program ASPM *
@@ -1387,6 +1412,9 @@ bool amdgpu_device_pcie_dynamic_switchin*/ bool amdgpu_device_should_use_aspm (struct amdgpu_device *adev) { +struct pci_dev *aspm_pdev, *parent; +bool enabled; +switch (amdgpu_aspm) {case -1 :break ;
@@ -1397,7 +1425,27 @@ bool amdgpu_device_should_use_aspm(strucdefault :return false; } -return pcie_aspm_enabled (adev->pdev); + +/* +* pcie_aspm_enabled() checks the link between its argument and +* the immediate upstream bridge. Use SWUS for dGPUs with an +* internal switch so that this is the host-facing link. +*/ + aspm_pdev =amdgpu_device_get_aspm_pdev (adev); + parent =pci_upstream_bridge (aspm_pdev); +if (!parent) { +dev_dbg (adev->dev,"ASPM: no upstream PCIe link for %s\n" , +pci_name (aspm_pdev)); +return false; + } + + enabled =pcie_aspm_enabled (aspm_pdev); +/* Report the exact link used for the automatic ASPM decision. */ +dev_dbg (adev->dev,"ASPM: link %s <-> %s is %s\n" , +pci_name (parent),pci_name (aspm_pdev), + enabled ?"enabled" :"disabled" ); + +return enabled; }bool amdgpu_device_aspm_support_quirk (void )
Greg Kroah-Hartman
maintainerPATCH 38/79 gpio: ml-ioh: use raw_spinlock_t for the register lock
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junjie Cao <[email protected]>
commit 600411ea1f2443fdf5b1af9b6480f616d7aff9d0 upstream.
ioh_irq_type() is registered as the irq_chip .irq_set_type callback and takes chip->spinlock with spin_lock_irqsave(). This callback is reached from __setup_irq() -> __irq_set_trigger() -> chip->irq_set_type() while the caller holds desc->lock, a raw_spinlock_t, with hardirqs disabled. That context is not sleepable, but on PREEMPT_RT a regular spinlock_t is an rtmutex-backed sleeping lock, so acquiring it there is invalid. ioh_irq_enable() and ioh_irq_disable() take the same lock from the .irq_enable/.irq_disable callbacks, which are likewise invoked with desc->lock held.
Convert the register lock to raw_spinlock_t. The same lock also serializes the GPIO direction/value callbacks and the suspend/resume register save/restore, and those critical sections only perform short sequences of MMIO register accesses (ioread32()/iowrite32()); the .irq_set_type callback additionally emits a dev_warn() on an unsupported type. None of these are sleepable operations, so keeping this register lock non-sleeping is appropriate for the irqchip callbacks and does not change the GPIO-side locking contract.
This is the same fix as commit a02b8950d619 ("gpio: pch: use raw_spinlock_t for the register lock"); this driver shares the same structure as gpio-pch.
--- drivers/gpio/gpio-ml-ioh.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-)
drivers/gpio/gpio-ml-ioh.c +18 -18
@@ -84,7 +84,7 @@ struct ioh_gpio {u32 gpio_use_sel;int ch;int irq_base; -spinlock_t spinlock; +raw_spinlock_t spinlock; };static const int num_ports[] = {6 ,12 ,16 ,16 ,15 ,16 ,16 ,12 };
@@ -95,7 +95,7 @@ static void ioh_gpio_set(struct gpio_chistruct ioh_gpio *chip =gpiochip_get_data (gpio);unsigned long flags; -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags); reg_val =ioread32 (&chip->reg->regs[chip->ch].po);if (val) reg_val |=BIT (nr);
@@ -103,7 +103,7 @@ static void ioh_gpio_set(struct gpio_chi reg_val &= ~BIT (nr);iowrite32 (reg_val, &chip->reg->regs[chip->ch].po); -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags); }static int ioh_gpio_get (struct gpio_chip *gpio,unsigned nr)
@@ -121,7 +121,7 @@ static int ioh_gpio_direction_output(stru32 reg_val;unsigned long flags; -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags); pm =ioread32 (&chip->reg->regs[chip->ch].pm); pm &=BIT (num_ports[chip->ch]) -1 ; pm |=BIT (nr);
@@ -134,7 +134,7 @@ static int ioh_gpio_direction_output(str reg_val &= ~BIT (nr);iowrite32 (reg_val, &chip->reg->regs[chip->ch].po); -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags);return 0 ; }
@@ -145,12 +145,12 @@ static int ioh_gpio_direction_input(struu32 pm;unsigned long flags; -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags); pm =ioread32 (&chip->reg->regs[chip->ch].pm); pm &=BIT (num_ports[chip->ch]) -1 ; pm &= ~BIT (nr);iowrite32 (pm, &chip->reg->regs[chip->ch].pm); -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags);return 0 ; }
@@ -254,7 +254,7 @@ static int ioh_irq_type(struct irq_datadev_dbg (chip->dev,"%s:irq=%d type=%d ch=%d pos=%d type=%d\n" , __func__, irq, type, ch, im_pos, type); -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags);switch (type) {case IRQ_TYPE_EDGE_RISING :
@@ -294,7 +294,7 @@ static int ioh_irq_type(struct irq_data ien =ioread32 (&chip->reg->regs[chip->ch].ien);iowrite32 (ien |BIT (ch), &chip->reg->regs[chip->ch].ien); end: -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags);return 0 ; }
@@ -324,11 +324,11 @@ static void ioh_irq_disable(struct irq_dunsigned long flags;u32 ien; -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags); ien =ioread32 (&chip->reg->regs[chip->ch].ien); ien &= ~BIT (d->irq - chip->irq_base);iowrite32 (ien, &chip->reg->regs[chip->ch].ien); -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags); }static void ioh_irq_enable (struct irq_data *d)
@@ -338,11 +338,11 @@ static void ioh_irq_enable(struct irq_daunsigned long flags;u32 ien; -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags); ien =ioread32 (&chip->reg->regs[chip->ch].ien); ien |=BIT (d->irq - chip->irq_base);iowrite32 (ien, &chip->reg->regs[chip->ch].ien); -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags); }static irqreturn_t ioh_gpio_handler (int irq,void *dev_id)
@@ -438,7 +438,7 @@ static int ioh_gpio_probe(struct pci_dev chip->base = base; chip->reg = chip->base; chip->ch = i; -spin_lock_init (&chip->spinlock); +raw_spin_lock_init (&chip->spinlock);ioh_gpio_setup (chip, num_ports[i]); ret =devm_gpiochip_add_data (dev, &chip->gpio, chip);if (ret) {
@@ -482,9 +482,9 @@ static int __maybe_unused ioh_gpio_suspestruct ioh_gpio *chip =dev_get_drvdata (dev);unsigned long flags; -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags);ioh_gpio_save_reg_conf (chip); -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags);return 0 ; }
@@ -494,11 +494,11 @@ static int __maybe_unused ioh_gpio_resumstruct ioh_gpio *chip =dev_get_drvdata (dev);unsigned long flags; -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags);iowrite32 (0x01 , &chip->reg->srst);iowrite32 (0x00 , &chip->reg->srst);ioh_gpio_restore_reg_conf (chip); -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 39/79 tls: fix lockless read of strp->msg_ready in ->poll
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sabrina Dubroca <[email protected]>
commit 0844370f8945086eb9335739d10205dcea8d707b upstream.
tls_sk_poll is called without locking the socket, and needs to read strp->msg_ready (via tls_strp_msg_ready). Convert msg_ready to a bool and use READ_ONCE/WRITE_ONCE where needed. The remaining reads are only performed when the socket is locked.
Stable-dep-of: 0844370f8945 ("tls: fix lockless read of strp->msg_ready in ->poll")
--- include/net/tls.h | 3 ++- net/tls/tls.h | 2 +- net/tls/tls_strp.c | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-)
include/net/tls.h +2 -1
@@ -122,7 +122,8 @@ struct tls_strparser { u32 stopped : 1; u32 copy_mode : 1; u32 mixed_decrypted : 1; - u32 msg_ready : 1; + +bool msg_ready;struct strp_msg stm;
net/tls/tls.h +1 -1
@@ -167,7 +167,7 @@ static inline struct sk_buff *tls_strp_mstatic inline bool tls_strp_msg_ready (struct tls_sw_context_rx *ctx) { -return ctx->strp.msg_ready; +return READ_ONCE (ctx->strp.msg_ready); }static inline bool tls_strp_msg_mixed_decrypted (struct tls_sw_context_rx *ctx)
net/tls/tls_strp.c +3 -3
@@ -366,7 +366,7 @@ static int tls_strp_copyin(read_descriptif (strp->stm.full_len && strp->stm.full_len == skb->len) { desc->count =0 ; - strp->msg_ready =1 ; +WRITE_ONCE (strp->msg_ready,1 );tls_rx_msg_ready (strp); }
@@ -533,7 +533,7 @@ static int tls_strp_read_sock(struct tlsif (!tls_strp_check_queue_ok (strp))return tls_strp_read_copy (strp, false); - strp->msg_ready =1 ; +WRITE_ONCE (strp->msg_ready,1 );tls_rx_msg_ready (strp);return 0 ;
@@ -585,7 +585,7 @@ void tls_strp_msg_done(struct tls_strparelse tls_strp_flush_anchor_copy (strp); - strp->msg_ready =0 ; +WRITE_ONCE (strp->msg_ready,0 );memset (&strp->stm,0 ,sizeof (strp->stm));tls_strp_check_rcv (strp);
Greg Kroah-Hartman
maintainerPATCH 40/79 tls: handle data disappearing from under the TLS ULP
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
commit 6db015fc4b5d5f63a64a193f65d98da3a7fc811d upstream.
TLS expects that it owns the receive queue of the TCP socket. This cannot be guaranteed in case the reader of the TCP socket entered before the TLS ULP was installed, or uses some non-standard read API (eg. zerocopy ones). Replace the WARN_ON() and a buggy early exit (which leaves anchor pointing to a freed skb) with real error handling. Wipe the parsing state and tell the reader to retry.
We already reload the anchor every time we (re)acquire the socket lock, so the only condition we need to avoid is an out of bounds read (not having enough bytes in the socket for previously parsed record len).
If some data was read from under TLS but there's enough in the queue we'll reload and decrypt what is most likely not a valid TLS record. Leading to some undefined behavior from TLS perspective (corrupting a stream? missing an alert? missing an attack?) but no kernel crash should take place.
--- net/tls/tls.h | 2 +- net/tls/tls_strp.c | 11 ++++++++--- net/tls/tls_sw.c | 3 ++- 3 files changed, 11 insertions(+), 5 deletions(-)
net/tls/tls.h +1 -1
@@ -147,7 +147,7 @@ void tls_strp_msg_done(struct tls_strparint tls_rx_msg_size (struct tls_strparser *strp,struct sk_buff *skb);void tls_rx_msg_ready (struct tls_strparser *strp); -void tls_strp_msg_load (struct tls_strparser *strp,bool force_refresh); +bool tls_strp_msg_load (struct tls_strparser *strp,bool force_refresh);int tls_strp_msg_cow (struct tls_sw_context_rx *ctx);struct sk_buff *tls_strp_msg_detach (struct tls_sw_context_rx *ctx);int tls_strp_msg_hold (struct tls_strparser *strp,struct sk_buff_head *dst);
net/tls/tls_strp.c +8 -3
@@ -481,7 +481,7 @@ static void tls_strp_load_anchor_with_qu strp->stm.offset = offset; } -void tls_strp_msg_load (struct tls_strparser *strp,bool force_refresh) +bool tls_strp_msg_load (struct tls_strparser *strp,bool force_refresh) {struct strp_msg *rxm;struct tls_msg *tlm;
@@ -490,8 +490,11 @@ void tls_strp_msg_load(struct tls_strparDEBUG_NET_WARN_ON_ONCE (!strp->stm.full_len);if (!strp->copy_mode && force_refresh) { -if (WARN_ON (tcp_inq (strp->sk) < strp->stm.full_len)) -return ; +if (unlikely (tcp_inq (strp->sk) < strp->stm.full_len)) { +WRITE_ONCE (strp->msg_ready,0 ); +memset (&strp->stm,0 ,sizeof (strp->stm)); +return false; + }tls_strp_load_anchor_with_queue (strp, strp->stm.full_len); }
@@ -501,6 +504,8 @@ void tls_strp_msg_load(struct tls_strpar rxm->offset = strp->stm.offset; tlm =tls_msg (strp->anchor); tlm->control = strp->mark; + +return true; }/* Called with lock held on lower socket */
net/tls/tls_sw.c +2 -1
@@ -1510,7 +1510,8 @@ tls_rx_rec_wait(struct sock *sk, structreturn sock_intr_errno (timeo); } -tls_strp_msg_load (&ctx->strp, released); +if (unlikely (!tls_strp_msg_load (&ctx->strp, released))) +return tls_rx_rec_wait (sk, psock, nonblock, false);return 1 ; }
Greg Kroah-Hartman
maintainerPATCH 41/79 iomap: adjust read range correctly for non-block-aligned positions
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
commit 7aa6bc3e8766990824f66ca76c19596ce10daf3e upstream.
iomap_adjust_read_range() assumes that the position and length passed in are block-aligned. This is not always the case however, as shown in the syzbot generated case for erofs. This causes too many bytes to be skipped for uptodate blocks, which results in returning the incorrect position and length to read in. If all the blocks are uptodate, this underflows length and returns a position beyond the folio.
Fix the calculation to also take into account the block offset when calculating how many bytes can be skipped for uptodate blocks.
--- fs/iomap/buffered-io.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
fs/iomap/buffered-io.c +13 -6
@@ -107,17 +107,24 @@ static void iomap_adjust_read_range(stru* to avoid reading in already uptodate ranges. */ if (iop) { -unsigned int i; +unsigned int i, blocks_skipped;/* move forward for each leading block marked uptodate */ -for (i = first; i <= last; i++) { +for (i = first; i <= last; i++)if (!test_bit (i, iop->uptodate))break ; - *pos += block_size; - poff += block_size; - plen -= block_size; - first++; + + blocks_skipped = i - first; +if (blocks_skipped) { +unsigned long block_offset = *pos & (block_size -1 ); +unsigned bytes_skipped = + (blocks_skipped << block_bits) - block_offset; + + *pos += bytes_skipped; + poff += bytes_skipped; + plen -= bytes_skipped; } + first = i;/* truncate len if we find any trailing uptodate block(s) */ for ( ; i <= last; i++) {
Greg Kroah-Hartman
maintainerPATCH 42/79 nfc: digital: clamp SENSF_RES length to the destination buffer
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit 344a56d7c8e0f3cbaff0bcb1bcd95a1a1db24b16 upstream.
digital_in_recv_sensf_res() memcpy()s resp->len bytes from a remote NFC-F device response into the NFC_SENSF_RES_MAXSIZE-byte target.sensf_res field without an upper-bound check. A nearby malicious NFC-F device can send an oversized SENSF_RES response to overflow the stack-local struct nfc_target.
Clamp resp->len to NFC_SENSF_RES_MAXSIZE before the copy.
Found by 0sec automated security-research tooling (https://0sec.ai).
--- net/nfc/digital_technology.c | 2 ++ 1 file changed, 2 insertions(+)
net/nfc/digital_technology.c +2 -0
@@ -778,6 +778,8 @@ static void digital_in_recv_sensf_res(st sensf_res = (struct digital_sensf_res *)resp->data; + resp->len = min_t(unsigned int, resp->len, NFC_SENSF_RES_MAXSIZE); +memcpy (target.sensf_res, sensf_res, resp->len); target.sensf_res_len = resp->len;
Greg Kroah-Hartman
maintainerPATCH 43/79 nfc: fdp: bound the device-reported read length and fix an skb leak
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 7ad21dcfeb5181af0c3ee2608808c0c0a5283aa1 upstream.
fdp_nci_i2c_read() takes the next packet length from two device-supplied bytes and never validates it. The value is a u16 used as the i2c_master_recv() count into a 261-byte on-stack buffer: a malicious, counterfeit or malfunctioning controller (or an i2c bus interposer) can drive it far past the buffer for a stack out-of-bounds write that clobbers the canary and return address, or below the minimum frame size (directly, or by truncating the computed sum) so the header/LRC strip and the next length read run past a short receive. Reject a length outside [FDP_NCI_I2C_MIN_PAYLOAD, FDP_NCI_I2C_MAX_PAYLOAD], as a corrupted packet already is, and force resynchronization.
The same loop allocates one data skb per iteration and assumes a length packet followed by a data packet; a device that sends two data packets in one call leaks the first skb when the second allocation overwrites it. Free a previously allocated skb before allocating the next.
--- drivers/nfc/fdp/i2c.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
drivers/nfc/fdp/i2c.c +27 -0
@@ -166,9 +166,36 @@ static int fdp_nci_i2c_read(struct fdp_i/* Packet that contains a length */ if (tmp[0 ] ==0 && tmp[1 ] ==0 ) { phy->next_read_size = (tmp[2 ] <<8 ) + tmp[3 ] +3 ; + +/* +* next_read_size is taken from the device and is used +* as the i2c_master_recv() count for the next packet +* and as the data skb size. A value above the receive +* buffer overflows tmp[]; one below the minimum frame +* size runs the header/LRC strip and the length-field +* read past a short receive. Either way the packet is +* corrupt: drop it and force resynchronization. +*/ +if (phy->next_read_size <FDP_NCI_I2C_MIN_PAYLOAD || + phy->next_read_size >FDP_NCI_I2C_MAX_PAYLOAD ) { +dev_dbg (&client->dev,"%s: corrupted packet\n" , + __func__); + phy->next_read_size =FDP_NCI_I2C_MIN_PAYLOAD ; + goto flush; + } }else { phy->next_read_size =FDP_NCI_I2C_MIN_PAYLOAD ; +/* +* Only one data packet is delivered per call; if the +* device sends another, do not overwrite and leak the +* skb allocated for the previous one. +*/ +if (*skb) { +kfree_skb (*skb); + *skb =NULL ; + } + *skb =alloc_skb (len,GFP_KERNEL );if (*skb ==NULL ) { r = -ENOMEM ;
Greg Kroah-Hartman
maintainerPATCH 44/79 nfc: microread: validate target discovery payload lengths
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
commit 25519469972ef57c3edb1805dabd6c5612b90211 upstream.
microread_target_discovered() parses target discovery payloads from skb->data according to the HCI gate. The fixed field offsets and UID copies were checked only against the destination nfc_target buffers, not against the actual skb length.
Validate that each gate-specific payload contains the fixed fields and UID bytes before reading or copying them.
--- drivers/nfc/microread/microread.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-)
drivers/nfc/microread/microread.c +29 -2
@@ -483,13 +483,19 @@ static void microread_target_discovered(switch (gate) {case MICROREAD_GATE_ID_MREAD_ISO_A : +if (skb->len <=MICROREAD_EMCF_A_LEN ) { + r = -EINVAL ; + goto exit_free; + } + targets->supported_protocols =nfc_hci_sak_to_protocol (skb->data[MICROREAD_EMCF_A_SAK ]); targets->sens_res =be16_to_cpu (*(u16 *)&skb->data[MICROREAD_EMCF_A_ATQA ]); targets->sel_res = skb->data[MICROREAD_EMCF_A_SAK ]; targets->nfcid1_len = skb->data[MICROREAD_EMCF_A_LEN ]; -if (targets->nfcid1_len >sizeof (targets->nfcid1)) { +if (targets->nfcid1_len >sizeof (targets->nfcid1) || + targets->nfcid1_len > skb->len -MICROREAD_EMCF_A_UID ) { r = -EINVAL ; goto exit_free; }
@@ -497,13 +503,19 @@ static void microread_target_discovered( targets->nfcid1_len);break ;case MICROREAD_GATE_ID_MREAD_ISO_A_3 : +if (skb->len <=MICROREAD_EMCF_A3_LEN ) { + r = -EINVAL ; + goto exit_free; + } + targets->supported_protocols =nfc_hci_sak_to_protocol (skb->data[MICROREAD_EMCF_A3_SAK ]); targets->sens_res =be16_to_cpu (*(u16 *)&skb->data[MICROREAD_EMCF_A3_ATQA ]); targets->sel_res = skb->data[MICROREAD_EMCF_A3_SAK ]; targets->nfcid1_len = skb->data[MICROREAD_EMCF_A3_LEN ]; -if (targets->nfcid1_len >sizeof (targets->nfcid1)) { +if (targets->nfcid1_len >sizeof (targets->nfcid1) || + targets->nfcid1_len > skb->len -MICROREAD_EMCF_A3_UID ) { r = -EINVAL ; goto exit_free; }
@@ -511,11 +523,21 @@ static void microread_target_discovered( targets->nfcid1_len);break ;case MICROREAD_GATE_ID_MREAD_ISO_B : +if (skb->len <MICROREAD_EMCF_B_UID +4 ) { + r = -EINVAL ; + goto exit_free; + } + targets->supported_protocols =NFC_PROTO_ISO14443_B_MASK ;memcpy (targets->nfcid1, &skb->data[MICROREAD_EMCF_B_UID ],4 ); targets->nfcid1_len =4 ;break ;case MICROREAD_GATE_ID_MREAD_NFC_T1 : +if (skb->len <MICROREAD_EMCF_T1_UID +4 ) { + r = -EINVAL ; + goto exit_free; + } + targets->supported_protocols =NFC_PROTO_JEWEL_MASK ; targets->sens_res =le16_to_cpu (*(u16 *)&skb->data[MICROREAD_EMCF_T1_ATQA ]);
@@ -523,6 +545,11 @@ static void microread_target_discovered( targets->nfcid1_len =4 ;break ;case MICROREAD_GATE_ID_MREAD_NFC_T3 : +if (skb->len <MICROREAD_EMCF_T3_UID +8 ) { + r = -EINVAL ; + goto exit_free; + } + targets->supported_protocols =NFC_PROTO_FELICA_MASK ;memcpy (targets->nfcid1, &skb->data[MICROREAD_EMCF_T3_UID ],8 ); targets->nfcid1_len =8 ;
Greg Kroah-Hartman
maintainerPATCH 45/79 nfc: llcp: bound the connect_sn TLV walk to the skb
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit 55c68ac93e7dacc0f5f608b9c39dd4ff48cf28e8 upstream.
Commit 27256cdb290e ("nfc: llcp: bound SNL TLV parsing to the skb and add length checks") fixed the unbounded TLV walk in nfc_llcp_recv_snl(), and commit d8bd2dedbde5 ("nfc: llcp: fix OOB read and u8 offset wrap in TLV parsers") subsequently bounded nfc_llcp_parse_gb_tlv() and nfc_llcp_parse_connection_tlv(). One sibling parser sharing the same pattern remains unbounded: nfc_llcp_connect_sn().
nfc_llcp_connect_sn() walks a TLV list, reading a two-byte header (type, length) followed by length bytes of value, without checking that the two header bytes or the declared length stay within the buffer. It returns a pointer to a service name of up to 255 bytes that may point past the end of the skb; it is subsequently consumed by memcmp() in nfc_llcp_sock_from_sn(). In addition tlv_array_len was computed as "skb->len - LLCP_HEADER_SIZE" in size_t, so a CONNECT/CC frame shorter than the LLCP header underflows to a huge length and the walk runs far past the buffer.
nfc_llcp_connect_sn() is reachable from nfc_llcp_recv_connect() and nfc_llcp_recv_cc(), i.e. from received CONNECT and CC PDUs. A nearby NFC device can reach this without authentication; LLCP link activation happens automatically after NFC-DEP, and the nfc_llcp_rx_skb() dispatcher applies no minimum-length guard.
Walk the TLV list by pointer, bounded by skb_tail_pointer(skb), and validate each declared length before use, matching the approach already used for nfc_llcp_recv_snl(). Starting the walk at &skb->data[LLCP_HEADER_SIZE] against the tail pointer also removes the size_t underflow for short frames.
Found by 0sec automated security-research tooling (https://0sec.ai).
--- net/nfc/llcp_core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
net/nfc/llcp_core.c +6 -4
@@ -847,13 +847,16 @@ static struct nfc_llcp_sock *nfc_llcp_sostatic const u8 *nfc_llcp_connect_sn (const struct sk_buff *skb,size_t *sn_len) {u8 type, length; -const u8 *tlv = &skb->data[2 ]; -size_t tlv_array_len = skb->len -LLCP_HEADER_SIZE , offset =0 ; +const u8 *tlv = &skb->data[LLCP_HEADER_SIZE ]; +const u8 *tlv_end =skb_tail_pointer (skb); -while (offset < tlv_array_len) { +while (tlv +2 < tlv_end) { type = tlv[0 ]; length = tlv[1 ]; +if (tlv +2 + length > tlv_end) +break ; +pr_debug ("type 0x%x length %d\n" , type, length);if (type ==LLCP_TLV_SN ) {
@@ -861,7 +864,6 @@ static const u8 *nfc_llcp_connect_sn(conreturn &tlv[2 ]; } - offset += length +2 ; tlv += length +2 ; }
Greg Kroah-Hartman
maintainerPATCH 46/79 nfc: llcp: fix OOB read and u8 offset wrap in TLV parsers
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit 78b20c8eeacd2e44a2d8a4cb5316d3c521d90911 upstream.
nfc_llcp_parse_gb_tlv() and nfc_llcp_parse_connection_tlv() contain three related bugs in their TLV parsing loops:
1. 'offset' is declared u8 but tlv_array_len is u16. When TLV data advances offset past 255 it silently wraps to zero, causing infinite loops or double-processing of buffer data.
2. Before reading tlv[0] (type) and tlv[1] (length) there is no check that offset+2 <= tlv_array_len. A truncated TLV causes an OOB read of one byte past the buffer end.
3. After reading the length field, the value bytes are accessed without checking offset+2+length <= tlv_array_len. A crafted length=0xFF on a short buffer causes up to 255 bytes of OOB read past the buffer end.
Both functions are reachable without authentication via nfc_llcp_set_remote_gb() which feeds remote LLCP general bytes directly into nfc_llcp_parse_gb_tlv() with no additional validation.
Fix all three issues by widening offset from u8 to u16 and adding bounds checks for both the TLV header and value field before each access.
--- net/nfc/llcp_commands.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
net/nfc/llcp_commands.c +16 -2
@@ -193,7 +193,8 @@ int nfc_llcp_parse_gb_tlv(struct nfc_llc const u8 *tlv_array, u16 tlv_array_len) { const u8 *tlv = tlv_array; -u8 type, length, offset =0 ; +u8 type, length; +u16 offset =0 ;pr_debug ("TLV array length %d\n" , tlv_array_len);
@@ -201,9 +202,15 @@ int nfc_llcp_parse_gb_tlv(struct nfc_llcreturn -ENODEV ;while (offset < tlv_array_len) { +if (offset +2 > tlv_array_len) +return -EINVAL ; + type = tlv[0 ]; length = tlv[1 ]; +if (offset +2 + length > tlv_array_len) +return -EINVAL ; +pr_debug ("type 0x%x length %d\n" , type, length);switch (type) {
@@ -243,7 +250,8 @@ int nfc_llcp_parse_connection_tlv(struct const u8 *tlv_array, u16 tlv_array_len) { const u8 *tlv = tlv_array; -u8 type, length, offset =0 ; +u8 type, length; +u16 offset =0 ;pr_debug ("TLV array length %d\n" , tlv_array_len);
@@ -251,9 +259,15 @@ int nfc_llcp_parse_connection_tlv(structreturn -ENOTCONN ;while (offset < tlv_array_len) { +if (offset +2 > tlv_array_len) +return -EINVAL ; + type = tlv[0 ]; length = tlv[1 ]; +if (offset +2 + length > tlv_array_len) +return -EINVAL ; +pr_debug ("type 0x%x length %d\n" , type, length);switch (type) {
Greg Kroah-Hartman
maintainerPATCH 47/79 nfc: llcp: reject PDUs shorter than the LLCP header
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit 95674f506c6376d6722a23144c9acd26609771ed upstream.
Every LLCP PDU begins with a two-byte header (DSAP/SSAP + PTYPE), but the receive path never checked that a frame is at least LLCP_HEADER_SIZE bytes before parsing it.
nfc_llcp_rx_skb() reads the header via nfc_llcp_ptype()/nfc_llcp_dsap()/ nfc_llcp_ssap(), which dereference pdu->data[0] and pdu->data[1], and a CONNECT or CC PDU then computes
tlv_array_len = skb->len - LLCP_HEADER_SIZE;
as a size_t and hands it to the TLV walk. When the frame is shorter than the header the subtraction wraps to a huge value and the walk runs far past the buffer, an out-of-bounds read.
A nearby NFC device can reach this without authentication; LLCP link activation happens automatically after NFC-DEP.
Guard the common receive choke point __nfc_llcp_recv(), shared by both the target (nfc_llcp_data_received()) and initiator (nfc_llcp_recv()) paths, so a short skb is dropped before the rx_work worker parses it. Use pskb_may_pull() rather than a skb->len test so the two header bytes are guaranteed to sit in the skb linear area even for a non-linear skb, matching how the sibling NCI and HCI receive paths validate their headers.
Reproduced with a KFENCE out-of-bounds read via /dev/virtual_nci on linux-next.
Found by 0sec automated security-research tooling (https://0sec.ai).
--- net/nfc/llcp_core.c | 5 +++++ 1 file changed, 5 insertions(+)
net/nfc/llcp_core.c +5 -0
@@ -1552,6 +1552,11 @@ static void nfc_llcp_rx_work(struct workstatic void __nfc_llcp_recv (struct nfc_llcp_local *local,struct sk_buff *skb) { +if (!pskb_may_pull (skb,LLCP_HEADER_SIZE )) { +kfree_skb (skb); +return ; + } + local->rx_pending = skb;del_timer (&local->link_timer);schedule_work (&local->rx_work);
Greg Kroah-Hartman
maintainerPATCH 48/79 nfc: pn533: purge fragmented skbs during cleanup
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
commit 5718fc62198c38c2de5316020a90506f9e75e0bb upstream.
pn53x_common_clean() purges resp_q before freeing the common PN533 state, but it leaves fragment_skb untouched. The fragmentation helpers queue transmit fragments there while sending large initiator or target-mode frames, and those skbs remain owned by the driver until they are sent or discarded.
If the device is removed while fragments are still queued, the common cleanup path frees the PN533 state without releasing the queued fragment skbs, leaking them.
Purge fragment_skb during cleanup alongside resp_q.
--- drivers/nfc/pn533/pn533.c | 1 + 1 file changed, 1 insertion(+)
drivers/nfc/pn533/pn533.c +1 -0
@@ -2803,6 +2803,7 @@ void pn53x_common_clean(struct pn533 *prdestroy_workqueue (priv->wq);skb_queue_purge (&priv->resp_q); +skb_queue_purge (&priv->fragment_skb);list_for_each_entry_safe (cmd, n, &priv->cmd_queue, queue) {list_del (&cmd->queue);
Greg Kroah-Hartman
maintainerPATCH 49/79 nfc: st21nfca: validate ATR_REQ length against the received frame
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit 5cdcca5d62a66eda6b774110a44cba67bc1a8d1d upstream.
st21nfca_tm_recv_atr_req() checks that the received ATR_REQ frame is at least ST21NFCA_ATR_REQ_MIN_SIZE and that the self-declared atr_req->length is at least sizeof(struct st21nfca_atr_req), but never checks that atr_req->length does not exceed the actual received length (skb->len).
st21nfca_tm_send_atr_res() then trusts the declared length:
gb_len = atr_req->length - sizeof(struct st21nfca_atr_req); ... memcpy(atr_res->gbi, atr_req->gbi, gb_len);
so an RF peer that sends a short frame but sets atr_req->length larger than the frame makes gb_len exceed the general bytes actually present, and the memcpy reads out of bounds past the received skb. Those bytes are placed in the ATR_RES and sent back to the peer (kernel-memory disclosure to a proximity attacker); a larger declared length is an out-of-bounds read (DoS).
Reject frames whose declared length exceeds the received length. The adjacent nfc_tm_activated() path in the same function already derives its general-bytes length from skb->len rather than the declared field.
Found by 0sec (https://0sec.ai) using automated source analysis; the missing bound is evident from source. Compile-tested.
--- drivers/nfc/st21nfca/dep.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/nfc/st21nfca/dep.c +3 -0
@@ -207,6 +207,9 @@ static int st21nfca_tm_recv_atr_req(struif (atr_req->length <sizeof (struct st21nfca_atr_req ))return -EPROTO ; +if (atr_req->length > skb->len) +return -EPROTO ; + r =st21nfca_tm_send_atr_res (hdev, atr_req);if (r)return r;
Greg Kroah-Hartman
maintainerPATCH 50/79 nfc: nci: fix out-of-bounds write in nci_target_auto_activated()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Samuel Page <[email protected]>
commit ac200079db50af81e6b04d058b33ec92901d8edd upstream.
nci_target_auto_activated() appends a target to the fixed-size array ndev->targets[NCI_MAX_DISCOVERED_TARGETS] and increments ndev->n_targets without first checking the array is full; unlike its sibling nci_add_new_target(), which bails out when n_targets already equals NCI_MAX_DISCOVERED_TARGETS.
ndev->n_targets is only cleared by nci_clear_target_list(), so an NFCC that repeatedly re-runs discovery (RF_DISCOVER_RSP, which re-enters NCI_DISCOVERY without clearing the target list) and reports an auto-activated target (RF_INTF_ACTIVATED_NTF) drives n_targets past the limit. The append then writes a struct nfc_target past the end of the array (a slab out-of-bounds write), and nfc_targets_found() goes on to walk the array with the inflated count:
BUG: KASAN: slab-out-of-bounds in nci_add_new_protocol+0x94/0x2ac [nci] Write of size 2 at addr ffff0000c7299a18 by task kworker/u8:0/12 Workqueue: nfc0_nci_rx_wq nci_rx_work [nci] Call trace: nci_add_new_protocol+0x94/0x2ac [nci] nci_ntf_packet+0xddc/0x11a0 [nci] nci_rx_work+0x15c/0x1e0 [nci] process_one_work+0x2dc/0x500 worker_thread+0x240/0x460 kthread+0x1c0/0x1d0 ret_from_fork+0x10/0x20 The buggy address belongs to the cache kmalloc-2k of size 2048 The buggy address is located 1024 bytes to the right of allocated 1560-byte region [ffff0000c7299000, ffff0000c7299618)
Guard nci_target_auto_activated() with the same check used by nci_add_new_target().
--- net/nfc/nci/ntf.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/nfc/nci/ntf.c +6 -0
@@ -603,6 +603,12 @@ static void nci_target_auto_activated(ststruct nfc_target *target;int rc; +/* This is a new target, check if we've enough room */ +if (ndev->n_targets ==NCI_MAX_DISCOVERED_TARGETS ) { +pr_debug ("not enough room, ignoring new target...\n" ); +return ; + } + target = &ndev->targets[ndev->n_targets]; rc = nci_add_new_protocol(ndev, target, ntf->rf_protocol,
Greg Kroah-Hartman
maintainerPATCH 51/79 nfc: nci: fix uninit-value in the RF discover/activated NTF handlers
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Samuel Page <[email protected]>
commit 8cbe06c1e699c0a165dae5093a2550e65f914818 upstream.
nci_rf_discover_ntf_packet() and nci_rf_intf_activated_ntf_packet() each parse a notification into an on-stack struct (nci_rf_discover_ntf / nci_rf_intf_activated_ntf) that is not initialised. The RF technology-specific parameters are only extracted when rf_tech_specific_params_len is non-zero, so a notification that reports a zero length leaves the rf_tech_specific_params union uninitialised - and both handlers then pass it to nci_add_new_protocol(), which reads it:
- discover: nci_add_new_target() -> nci_add_new_protocol();
- activated: nci_target_auto_activated() -> nci_add_new_protocol().
nci_add_new_protocol() uses nfca_poll->nfcid1_len as both a branch condition and a memcpy() length and copies nfcid1/sens_res/sel_res into ndev->targets, which is later exposed to user space via NFC_CMD_GET_TARGET.
BUG: KMSAN: uninit-value in nci_add_new_protocol+0x624/0x6c0 nci_add_new_protocol+0x624/0x6c0 nci_ntf_packet+0x25b2/0x3c30 nci_rx_work+0x318/0x5d0 process_scheduled_works+0x84b/0x17a0 worker_thread+0xc10/0x11b0 kthread+0x376/0x500 Local variable ntf.i created at: nci_ntf_packet+0xbc2/0x3c30
Zero-initialise both on-stack notifications so the union reads back as zero when no technology-specific parameters are present.
--- net/nfc/nci/ntf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/nfc/nci/ntf.c +2 -2
@@ -440,7 +440,7 @@ void nci_clear_target_list(struct nci_destatic int nci_rf_discover_ntf_packet (struct nci_dev *ndev,const struct sk_buff *skb) { -struct nci_rf_discover_ntf ntf; +struct nci_rf_discover_ntf ntf = {};const __u8 *data;bool add_target = true;
@@ -672,7 +672,7 @@ static int nci_rf_intf_activated_ntf_pac const struct sk_buff *skb) {struct nci_conn_info *conn_info; -struct nci_rf_intf_activated_ntf ntf; +struct nci_rf_intf_activated_ntf ntf = {};const __u8 *data;int err =NCI_STATUS_OK ;
Greg Kroah-Hartman
maintainerPATCH 52/79 nfc: nci: free destination parameters when closing a connection
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linmao Li <[email protected]>
commit 2e65bafdfd3a8bba972b3d17b6a57816557530fc upstream.
When a connection is closed, nci_core_conn_close_rsp_packet() frees conn_info but not conn_info->dest_params, which is a separate devm allocation. Each connect/close cycle leaks one dest_params until the NFC device is removed. Free dest_params along with conn_info.
--- net/nfc/nci/rsp.c | 1 + 1 file changed, 1 insertion(+)
net/nfc/nci/rsp.c +1 -0
@@ -336,6 +336,7 @@ static void nci_core_conn_close_rsp_packlist_del (&conn_info->list);if (conn_info == ndev->rf_conn_info) ndev->rf_conn_info =NULL ; +devm_kfree (&ndev->nfc_dev->dev, conn_info->dest_params);devm_kfree (&ndev->nfc_dev->dev, conn_info); } }
Greg Kroah-Hartman
maintainerPATCH 53/79 ndisc: ndisc_send_redirect() cleanup
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
commit 0784d83df3bfc977c13252a0599be924f0afa68d upstream.
ndisc_send_redirect() is always called under rcu_read_lock().
It can use dev_net_rcu() and avoid one redundant rcu_read_lock()/rcu_read_unlock() pair.
--- net/ipv6/ndisc.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
net/ipv6/ndisc.c +3 -5
@@ -1671,7 +1671,7 @@ static void ndisc_fill_redirect_hdr_optivoid ndisc_send_redirect (struct sk_buff *skb,const struct in6_addr *target) {struct net_device *dev = skb->dev; -struct net *net =dev_net (dev); +struct net *net =dev_net_rcu (dev);struct sock *sk = net->ipv6.ndisc_sk;int optlen =0 ;struct inet_peer *peer;
@@ -1686,8 +1686,8 @@ void ndisc_send_redirect(struct sk_buff ops_data_buf[NDISC_OPS_REDIRECT_DATA_SPACE ], *ops_data =NULL ;bool ret; -if (netif_is_l3_master (skb->dev)) { - dev =dev_get_by_index_rcu (dev_net (skb->dev),IPCB (skb)->iif); +if (netif_is_l3_master (dev)) { + dev =dev_get_by_index_rcu (net,IPCB (skb)->iif);if (!dev)return ; }
@@ -1725,12 +1725,10 @@ void ndisc_send_redirect(struct sk_buff goto release; } -rcu_read_lock (); peer =inet_getpeer_v6 (net->ipv6.peers, &ipv6_hdr (skb)->saddr);if (!peer) goto release; ret =inet_peer_xrlim_allow (peer,1 *HZ ); -rcu_read_unlock ();if (!ret) goto release;
Greg Kroah-Hartman
maintainerPATCH 54/79 libceph: fix OOB read in decode_watchers() via missing bounds check
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pavitra Jha <[email protected]>
commit 00ead17c7de137a692edee59f2772e6af687e8eb upstream.
ceph_start_decoding() validates that struct_len bytes remain in the buffer after the encoding header, but accepts struct_len=0 as valid:ceph_decode_need(p, end, 0, bad) always passes. When a malicious or compromised OSD sends an obj_list_watch_response_t reply with struct_len=0, ceph_start_decoding() returns success with p == end, leaving zero bytes guaranteed for subsequent reads.
The immediately following ceph_decode_32(p) in decode_watchers() has no preceding bounds check. With p == end this is a 4-byte read past the validated buffer boundary. The garbage value is then passed directly to kzalloc_objs() as the watcher count.
The sibling function decode_watcher() already uses the safe variants (ceph_decode_copy_safe, ceph_decode_64_safe, ceph_decode_skip_32) after its own ceph_start_decoding() call. decode_watchers() is the only site that uses the bare variant, confirming an oversight.
Fix by replacing ceph_decode_32(p) with ceph_decode_32_safe(p, end, *num_watchers, bad), consistent with the established pattern.
Attacker model: a malicious or compromised OSD in a multi-tenant Ceph deployment (e.g. cloud) can trigger this against any kernel client that calls CEPH_OSD_OP_LIST_WATCHERS, without any further privileges beyond OSD session establishment.
[ idryomov: trim changelog ]
--- net/ceph/osd_client.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/ceph/osd_client.c +4 -1
@@ -5001,7 +5001,7 @@ static int decode_watchers(void **p, voiif (ret)return ret; - *num_watchers =ceph_decode_32 (p); +ceph_decode_32_safe (p, end, *num_watchers, bad); *watchers =kcalloc (*num_watchers,sizeof (**watchers),GFP_NOIO );if (!*watchers)return -ENOMEM ;
@@ -5015,6 +5015,9 @@ static int decode_watchers(void **p, voi }return 0 ; + +bad: +return -EINVAL ; }/*
Greg Kroah-Hartman
maintainerPATCH 55/79 ipv4: reject undersized MTUs in ip_do_fragment()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yong Wang <[email protected]>
commit c0726f0caf8c6b3208552949e17d23634a2f3129 upstream.
ip_do_fragment() subtracts the IPv4 header length from the effective MTU and passes the resulting payload MTU to ip_frag_next().
If the effective MTU is smaller than hlen + 8, ip_frag_next() rounds the fragment payload length down to zero. The fragmentation state then never makes forward progress: state->left, state->ptr and state->offset stay unchanged while ip_do_fragment() keeps allocating and transmitting header-only fragments until the softlockup detector fires.
This is reproducible with a route installed using "mtu lock 20", but it is also reproducible without route MTU lock, for example by forwarding a packet to a device whose MTU is 20.
Fix it in ip_do_fragment() by rejecting mtu < hlen + 8 with -EMSGSIZE, matching the existing IPv6 fragmentation check.
--- net/ipv4/ip_output.c | 4 ++++ 1 file changed, 4 insertions(+)
net/ipv4/ip_output.c +4 -0
@@ -795,6 +795,10 @@ int ip_do_fragment(struct net *net, stru*/ hlen = iph->ihl *4 ; +if (mtu < hlen +8 ) { + err = -EMSGSIZE ; + goto fail; + } mtu = mtu - hlen;/* Size of data space */ IPCB (skb)->flags |=IPSKB_FRAG_COMPLETE ; ll_rs =LL_RESERVED_SPACE (rt->dst.dev);
Greg Kroah-Hartman
maintainerPATCH 56/79 ipv6: fix use-after-free in ip6_finish_output2()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luxiao Xu <[email protected]>
commit d0d48d999b0eee6bb176ef4e39d9be868fa80f7e upstream.
ip6_finish_output2() caches a pointer to the IPv6 destination address (daddr) before invoking lwtunnel_xmit(). The LWT-BPF transmit path or other encapsulation operations within lwtunnel_xmit() can reallocate the skb head, freeing the memory that daddr points to. When lwtunnel_xmit() returns LWTUNNEL_XMIT_CONTINUE, the function continues to use the stale daddr pointer to compute the nexthop and to look up or create the neighbour entry. This results in a use-after-free read, which can leak sensitive kernel data, pollute the neighbour table with arbitrary values, misdirect traffic, or crash the system.
Fix this by re-fetching the IPv6 header and the destination address pointer after lwtunnel_xmit() returns LWTUNNEL_XMIT_CONTINUE, ensuring that the subsequent nexthop computation and neighbour lookup operate on valid memory.
--- net/ipv6/ip6_output.c | 2 ++ 1 file changed, 2 insertions(+)
net/ipv6/ip6_output.c +2 -0
@@ -118,6 +118,8 @@ static int ip6_finish_output2(struct netif (res !=LWTUNNEL_XMIT_CONTINUE )return res; + hdr =ipv6_hdr (skb); + daddr = &hdr->daddr; }rcu_read_lock ();
Greg Kroah-Hartman
maintainerPATCH 57/79 nvmet-auth: zero the AUTH_RECEIVE response buffer
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 3ddcfb013322aa37eaa7a0d344b73079c38dfa21 upstream.
nvmet_execute_auth_receive() allocates the response buffer with kmalloc() sized by the host-supplied AUTH_RECEIVE allocation length, but the DH-HMAC-CHAP builders write only a fixed-size message into it. The full allocation length is then copied to the wire by nvmet_copy_to_sgl(), so a remote initiator receives the bytes past the built message -- up to nearly a page of uninitialized slab -- during the pre-authentication handshake.
Allocate the buffer with kzalloc() so the unwritten tail is zeroed before it is sent; conforming responses are unaffected.
--- drivers/nvme/target/fabrics-cmd-auth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/target/fabrics-cmd-auth.c +1 -1
@@ -469,7 +469,7 @@ void nvmet_execute_auth_receive(struct nreturn ; } - d =kmalloc (al,GFP_KERNEL ); + d =kzalloc (al,GFP_KERNEL );if (!d) { status =NVME_SC_INTERNAL ; goto done;
Greg Kroah-Hartman
maintainerPATCH 58/79 nvmet-fc: fix invalid free in LS IOD error path
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiang HongHui <[email protected]>
commit ba98d6796d12258e837ece065d2ecb59d76ce4ff upstream.
nvmet_fc_alloc_ls_iodlist() advances iod while initializing the LS IOD array. If an rqstbuf allocation or response buffer DMA mapping fails, the unwind loop decrements iod past the start of the array. The final kfree(iod) therefore frees an address before the allocated object.
This can be reproduced with nvme-fcloop and failslab by setting fail-nth to 6 before creating a target port. KASAN reports:
BUG: KASAN: invalid-free in nvmet_fc_register_targetport Free of addr ffff88816cf8ff48 by task nvmet_fail_nth/9552
Free the original allocation base stored in tgtport->iod instead. With this fix applied, the same sysfs write with fail-nth=6 returns -ENOMEM without any KASAN report.
--- drivers/nvme/target/fc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/target/fc.c +1 -1
@@ -571,7 +571,7 @@ out_fail:list_del (&iod->ls_rcv_list); } -kfree (iod); +kfree (tgtport->iod);return -EFAULT ; }
Greg Kroah-Hartman
maintainerPATCH 59/79 nvmet-tcp: Do not WARN on remotely-controlled oversized SGL allocations
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Greg Kroah-Hartman <[email protected]>
commit 737a3b535247226f6e1a7988fd9d6e63e7d6fc71 upstream.
When fuzzing the nvme target code, I tripped a kernel warning in nvmet_tcp_map_data() because the length passed into the allocator is controlled by the remote initiator.
A remote initiator that sends a command with an SGL claiming a huge number, can create a scatterlist and iovec allocation of over 1 million entries, which causes the backing kmalloc call to exceed MAX_PAGE_ORDER and then the page allocator will trip on a WARN_ON_ONCE_GFP() message:
WARNING: mm/page_alloc.c:5280 __alloc_frozen_pages_noprof Workqueue: nvmet_tcp_wq nvmet_tcp_io_work ... sgl_alloc_order nvmet_tcp_map_data nvmet_tcp_try_recv_pdu
As it's never good to trip a kernel warning remotely due to many systems having panic-on-warn enabled, let's silence it by just add GFP_NOWARN to the allocation flags.
--- drivers/nvme/target/tcp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/nvme/target/tcp.c +3 -2
@@ -401,14 +401,15 @@ static int nvmet_tcp_map_data(struct nvm } cmd->req.transfer_len += len; - cmd->req.sg =sgl_alloc (len,GFP_KERNEL , &cmd->req.sg_cnt); + cmd->req.sg =sgl_alloc (len,GFP_KERNEL | __GFP_NOWARN, + &cmd->req.sg_cnt);if (!cmd->req.sg)return NVME_SC_INTERNAL ; cmd->cur_sg = cmd->req.sg;if (nvmet_tcp_has_data_in (cmd)) { cmd->iov =kmalloc_array (cmd->req.sg_cnt, -sizeof (*cmd->iov),GFP_KERNEL ); +sizeof (*cmd->iov),GFP_KERNEL | __GFP_NOWARN);if (!cmd->iov) goto err; }
Greg Kroah-Hartman
maintainerPATCH 60/79 Input: byd - synchronize timer deletion before freeing private data
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linmao Li <[email protected]>
[ Upstream commit c83e79c0842ed29860648bcce5022ef0ba5001c6 ]
byd_disconnect() uses timer_delete() before freeing the driver's private data. This does not wait for a running byd_clear_touch() callback, which dereferences the private data and its psmouse pointer. A callback racing with disconnect can therefore access the private data after it has been freed. The timer can also still be re-armed by byd_process_byte() while the disconnect is in progress.
Use timer_shutdown_sync() before freeing the private data: it waits for a running callback and turns any later re-arm attempt into a no-op.
--- drivers/input/mouse/byd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/input/mouse/byd.c +1 -1
@@ -426,7 +426,7 @@ static void byd_disconnect(struct psmousstruct byd_data *priv = psmouse->private;if (priv) { -del_timer (&priv->timer); +timer_shutdown_sync (&priv->timer);kfree (psmouse->private); psmouse->private =NULL ; }
Greg Kroah-Hartman
maintainerPATCH 61/79 ASoC: codecs: lpass-tx-macro: Fix enum kcontrol accesses
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dawid Wróbel <[email protected]>
[ Upstream commit 1ba381759e45d5d0442452cfa5c42e836191a568 ]
The "DEC0 MODE" to "DEC7 MODE" controls are enumerated, but tx_macro_dec_mode_get() and tx_macro_dec_mode_put() access their value through ucontrol->value.integer.value[0] (a long) instead of ucontrol->value.enumerated.item[0] (an unsigned int).
This same pattern was fixed in the sibling drivers by commit bcfe5f76cc40 ("ASoC: codecs: rx-macro: fix accessing array out of bounds for enum type") and
commit 0ea5eff7c606 ("ASoC: codecs: va-macro: fix accessing array out of bounds for enum type"), but tx-macro was missed.
On 64-bit kernels built with CONFIG_SND_CTL_DEBUG, the elem value sanity check catches the 4 bytes written past the enumerated item and every read of these controls fails with -EINVAL:
snd-sm8250 sound: control 2:0:0:DEC0 MODE:0: access overflow
--- sound/soc/codecs/lpass-tx-macro.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
sound/soc/codecs/lpass-tx-macro.c +2 -2
@@ -1015,7 +1015,7 @@ static int tx_macro_dec_mode_get(structstruct soc_enum *e = (struct soc_enum *)kcontrol->private_value;int path = e->shift_l; - ucontrol->value.integer.value[0 ] = tx->dec_mode[path]; + ucontrol->value.enumerated.item[0 ] = tx->dec_mode[path];return 0 ; }
@@ -1024,7 +1024,7 @@ static int tx_macro_dec_mode_put(struct struct snd_ctl_elem_value *ucontrol) {struct snd_soc_component *component =snd_soc_kcontrol_component (kcontrol); -int value = ucontrol->value.integer.value[0 ]; +int value = ucontrol->value.enumerated.item[0 ];struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;int path = e->shift_l;struct tx_macro *tx =snd_soc_component_get_drvdata (component);
Greg Kroah-Hartman
maintainerPATCH 62/79 mptcp: pm: ADD_ADDR rtx: always decrease sk refcount
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Matthieu Baerts (NGI0)" <[email protected]>
[ Upstream commit 9634cb35af17019baec21ca648516ce376fa10e6 ]
When an ADD_ADDR is retransmitted, the sk is held in sk_reset_timer(). It should then be released in all cases at the end.
Some (unlikely) checks were returning directly instead of calling sock_put() to decrease the refcount. Jump to a new 'exit' label to call __sock_put() (which will become sock_put() in the next commit) to fix this potential leak.
While at it, drop the '!msk' check which cannot happen because it is never reset, and explicitly mark the remaining one as "unlikely".
Stable-dep-of: a7aad5b69d3b ("mptcp: pm: fix data race in add_addr timer callback")
--- net/mptcp/pm_netlink.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
net/mptcp/pm_netlink.c +3 -5
@@ -309,11 +309,8 @@ static void mptcp_pm_add_timer(struct tipr_debug ("msk=%p\n" , msk); -if (!msk) -return ; - -if (inet_sk_state_load (sk) ==TCP_CLOSE ) -return ; +if (unlikely (inet_sk_state_load (sk) ==TCP_CLOSE )) + goto exit;bh_lock_sock (sk);if (sock_owned_by_user (sk)) {
@@ -357,6 +354,7 @@ static void mptcp_pm_add_timer(struct ti out:bh_unlock_sock (sk); +exit:__sock_put (sk); }
Greg Kroah-Hartman
maintainerPATCH 63/79 mptcp: pm: ADD_ADDR rtx: free sk if last
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Matthieu Baerts (NGI0)" <[email protected]>
[ Upstream commit b7b9a461569734d33d3259d58d2507adfac107ed ]
When an ADD_ADDR is retransmitted, the sk is held in sk_reset_timer(), and released at the end.
If at that moment, it was the last reference being held, the sk would not be freed. sock_put() should then be called instead of __sock_put().
But that's not enough: if it is the last reference, sock_put() will call sk_free(), which will end up calling sk_stop_timer_sync() on the same timer, and waiting indefinitely to finish. So it is needed to mark that the timer is done at the end of the timer handler when it has not been rescheduled, not to call sk_stop_timer_sync() on "itself".
Stable-dep-of: a7aad5b69d3b ("mptcp: pm: fix data race in add_addr timer callback")
--- net/mptcp/pm_netlink.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-)
net/mptcp/pm_netlink.c +17 -11
@@ -28,6 +28,7 @@ struct mptcp_pm_add_entry {struct timer_list add_timer;struct mptcp_sock *sock;u8 retrans_times; +bool timer_done;struct rcu_head rcu; };
@@ -305,22 +306,22 @@ static void mptcp_pm_add_timer(struct tistruct mptcp_pm_add_entry *entry =from_timer (entry, timer, add_timer);struct mptcp_sock *msk = entry->sock;struct sock *sk = (struct sock *)msk; -unsigned int timeout; +unsigned int timeout =0 ;pr_debug ("msk=%p\n" , msk); +bh_lock_sock (sk);if (unlikely (inet_sk_state_load (sk) ==TCP_CLOSE )) - goto exit; + goto out; -bh_lock_sock (sk);if (sock_owned_by_user (sk)) {/* Try again later. */ -sk_reset_timer (sk, timer, jiffies +HZ /20 ); + timeout =HZ /20 ; goto out; }if (mptcp_pm_should_add_signal_addr (msk)) { -sk_reset_timer (sk, timer, jiffies +HZ ); + timeout =HZ ; goto out; }
@@ -343,9 +344,8 @@ static void mptcp_pm_add_timer(struct ti entry->retrans_times++; } -if (entry->retrans_times <ADD_ADDR_RETRANS_MAX ) -sk_reset_timer (sk, timer, - jiffies + timeout); +if (entry->retrans_times >=ADD_ADDR_RETRANS_MAX ) + timeout =0 ;spin_unlock_bh (&msk->pm.lock);
@@ -353,9 +353,13 @@ static void mptcp_pm_add_timer(struct timptcp_pm_subflow_established (msk); out: +if (timeout) +sk_reset_timer (sk, timer, jiffies + timeout); +else +/* if sock_put calls sk_free: avoid waiting for this timer */ + entry->timer_done = true;bh_unlock_sock (sk); -exit: -__sock_put (sk); +sock_put (sk); } struct mptcp_pm_add_entry *
@@ -423,6 +427,7 @@ bool mptcp_pm_alloc_anno_list(struct mpttimer_setup (&add_entry->add_timer, mptcp_pm_add_timer,0 ); reset_timer: + add_entry->timer_done = false; timeout =mptcp_get_add_addr_timeout (net);if (timeout)sk_reset_timer (sk, &add_entry->add_timer, jiffies + timeout);
@@ -443,7 +448,8 @@ void mptcp_pm_free_anno_list(struct mptcspin_unlock_bh (&msk->pm.lock);list_for_each_entry_safe (entry, tmp, &free_list, list) { -sk_stop_timer_sync (sk, &entry->add_timer); +if (!entry->timer_done) +sk_stop_timer_sync (sk, &entry->add_timer);kfree_rcu (entry, rcu); } }
Greg Kroah-Hartman
maintainerPATCH 64/79 mptcp: pm: fix data race in add_addr timer callback
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qing Luo <[email protected]>
[ Upstream commit a7aad5b69d3bdaec20a3ed9284e184502450c0cd ]
The timer callback reads entry->retrans_times outside pm.lock to decide whether to call mptcp_pm_subflow_established(). Since mptcp_pm_announced_del_timer() can concurrently set retrans_times = ADD_ADDR_RETRANS_MAX under pm.lock, a race condition exists.
I discovered this issue while studying the code. AI tools helped me to verify the issue can potentially happen under race conditions.
Use a local 'retransmit' flag set inside pm.lock to capture whether retransmission is still possible when the lock is taken. This allows to call mptcp_pm_subflow_established() accordingly, and not depending on the situation that can be different when checked outside the pm.lock.
--- net/mptcp/pm_netlink.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
net/mptcp/pm_netlink.c +4 -2
@@ -307,6 +307,7 @@ static void mptcp_pm_add_timer(struct tistruct mptcp_sock *msk = entry->sock;struct sock *sk = (struct sock *)msk;unsigned int timeout =0 ; +bool retransmit;pr_debug ("msk=%p\n" , msk);
@@ -344,12 +345,13 @@ static void mptcp_pm_add_timer(struct ti entry->retrans_times++; } -if (entry->retrans_times >=ADD_ADDR_RETRANS_MAX ) + retransmit = entry->retrans_times <ADD_ADDR_RETRANS_MAX ; +if (!retransmit) timeout =0 ;spin_unlock_bh (&msk->pm.lock); -if (entry->retrans_times ==ADD_ADDR_RETRANS_MAX ) +if (!retransmit)mptcp_pm_subflow_established (msk); out:
Greg Kroah-Hartman
maintainerPATCH 65/79 HID: magicmouse: do not keep a stale msc->input if no input is claimed
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jose Villaseñor Montfort <[email protected]>
commit 0af3b89705688af01aa06025b84fa7a1e06ba6cc upstream.
magicmouse_input_mapping() caches the first hid_input's input_dev in msc->input while the report descriptor is parsed, and the rest of the driver treats a non-NULL msc->input as proof that an input device was registered.
That does not hold on the hid-input error path. If hidinput_connect() fails -- for instance because input_register_device() returns an error -- it unwinds through hidinput_disconnect(), which frees every input_dev it created, including the one cached in msc->input.
The failure does not abort the probe. hid_connect() only skips the claim:
if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev, connect_mask & HID_CONNECT_HIDINPUT_FORCE)) hdev->claimed |= HID_CLAIMED_INPUT;
and the "device has no listeners" bailout below it does not fire for this driver, which sets ->raw_event; on the USB Magic Mouse 2 / Magic Trackpad 2 paths hidraw and hiddev are claimed as well. hid_hw_start() therefore returns 0 and magicmouse_probe() continues with msc->input pointing at freed memory. Being non-NULL, it passes the "input not registered" check in probe and the NULL checks in ->raw_event and ->event, so the next input report dereferences freed memory.
Clear msc->input when the HID core did not claim an input device, so the existing NULL checks cover this case as well.
--- drivers/hid/hid-magicmouse.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/hid/hid-magicmouse.c +10 -0
@@ -862,6 +862,16 @@ static int magicmouse_probe(struct hid_dreturn ret; } +/* +* When hidinput_connect() fails it frees every input device it +* created, but that does not fail hid_hw_start(): the core simply +* does not claim an input. msc->input, cached in ->input_mapping +* while the report descriptor was parsed, would then be a dangling +* pointer that passes every NULL check. Trust the core's claim. +*/ +if (!(hdev->claimed &HID_CLAIMED_INPUT )) + msc->input =NULL ; +if (is_usb_magicmouse2 (id->vendor, id->product) ||is_usb_magictrackpad2 (id->vendor, id->product)) {timer_setup (&msc->battery_timer, magicmouse_battery_timer_tick,0 );
Greg Kroah-Hartman
maintainerPATCH 66/79 HID: magicmouse: Prevent out-of-bounds (OOB) read during DOUBLE_REPORT_ID
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lee Jones <[email protected]>
commit d93ba918a185aca2594da63e92fdc5495b559c0f upstream.
It is currently possible for a malicious or misconfigured USB device to cause an out-of-bounds (OOB) read when submitting reports using DOUBLE_REPORT_ID by specifying a large report length and providing a smaller one.
Let's prevent that by comparing the specified report length with the actual size of the data read in from userspace. If the actual data length ends up being smaller than specified, we'll politely warn the user and prevent any further processing.
--- drivers/hid/hid-magicmouse.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
drivers/hid/hid-magicmouse.c +16 -0
@@ -384,6 +384,10 @@ static int magicmouse_raw_event(struct hstruct input_dev *input = msc->input;int x =0 , y =0 , ii, clicks =0 , npoints; +/* Protect against zero sized recursive calls from DOUBLE_REPORT_ID */ +if (size <1 ) +return 0 ; +switch (data[0 ]) {case TRACKPAD_REPORT_ID :case TRACKPAD2_BT_REPORT_ID :
@@ -484,6 +488,18 @@ static int magicmouse_raw_event(struct h/* Sometimes the trackpad sends two touch reports in one * packet. */ + +/* Ensure that we have at least 2 elements (report type and size) */ +if (size <2 ) +return 0 ; + +if (size < data[1 ] +2 ) { +hid_warn (hdev, +"received report length (%d) was smaller than specified (%d)" , + size, data[1 ] +2 ); +return 0 ; + } +magicmouse_raw_event (hdev, report, data +2 , data[1 ]);magicmouse_raw_event (hdev, report, data +2 + data[1 ], size -2 - data[1 ]);
Greg Kroah-Hartman
maintainerPATCH 67/79 HID: core: fix OOB read of field->usage in hid_set_field()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
commit a13cdb19fcb223ed41bdab3bab42b98dba87e90b upstream.
hid_set_field() hands field->usage + offset to hid_dump_input() before the guard that bounds offset:
hid_dump_input(field->report->device, field->usage + offset, value);
if (offset >= field->report_count) {
hid_err(...);
return -1;
}Under CONFIG_DEBUG_FS hid_dump_input() dereferences that pointer, with buf = hid_resolv_usage(usage->hid, NULL). The usage[] array is allocated inline with the hid_field in hid_register_field() and holds field->maxusage entries, so an offset past it reads off the end of the kvzalloc()ed allocation and into a neighbouring object. Had the guard run first, offset < report_count <= maxusage would already have confined the pointer to the array.
A caller supplies such an offset today. picolcd_fb_send_tile() validates only report->maxfield before issuing hid_set_field(report->field[0], 11 + i, ...) for i = 0..31, so its offsets are fixed at 11..42 and are never checked against the bound field. When the device registers that field with fewer usages, the framebuffer deferred-io work drives the read on every tile. KASAN reports a 4-byte slab-out-of-bounds read in hid_dump_input() below hid_set_field(), and the same boot logs "offset (1) exceeds report_count (1)" from the guard that runs only afterwards.
Move the hid_dump_input() call below the guard. Because field->maxusage >= field->report_count, the guard then establishes that field->usage + offset lies inside the array before it is dereferenced, for every caller and without changing behaviour on the valid path.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- drivers/hid/hid-core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/hid/hid-core.c +3 -2
@@ -1910,13 +1910,14 @@ int hid_set_field(struct hid_field *fiel size = field->report_size; -hid_dump_input (field->report->device, field->usage + offset, value); -if (offset >= field->report_count) {hid_err (field->report->device,"offset (%d) exceeds report_count (%d)\n" , offset, field->report_count);return -1 ; } + +hid_dump_input (field->report->device, field->usage + offset, value); +if (field->logical_minimum <0 ) {if (value !=snto32 (s32ton (value, size), size)) {hid_err (field->report->device,"value %d is out of range\n" , value);
Greg Kroah-Hartman
maintainerPATCH 68/79 xfrm: fix sk_dst_cache double-free in xfrm_user_policy()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei (Microsoft) <[email protected]>
[ Upstream commit c283e9ada7fcb7dd4b10592623086b2e6d2f9925 ]
xfrm_user_policy() clears the socket dst cache with __sk_dst_reset(), i.e. the non-atomic __sk_dst_set(sk, NULL): it reads sk_dst_cache with rcu_dereference_protected(), stores NULL and dst_release()s the old dst. That is only safe if no other thread modifies sk_dst_cache concurrently.
For a connected UDP socket that does not hold: the transmit fast path (udp_sendmsg -> sk_dst_check -> sk_dst_reset) resets the cache locklessly with an atomic xchg(). A per-socket policy change racing a send can make both sides observe the same old dst and each dst_release() it, dropping the socket's single reference twice and freeing the xfrm_dst bundle while it is still referenced:
BUG: KASAN: slab-use-after-free in dst_release Write of size 4 at addr ffff88801897b6c0 by task exploit/155 Call Trace: ... dst_release (... ./include/linux/rcuref.h:109) xfrm_user_policy (./include/net/sock.h:2239 ./include/net/sock.h:2256 net/xfrm/xfrm_state.c:3053) do_ip_setsockopt (net/ipv4/ip_sockglue.c:1347) ip_setsockopt (net/ipv4/ip_sockglue.c:1417) do_sock_setsockopt (net/socket.c:2368) __sys_setsockopt (net/socket.c:2393) __x64_sys_setsockopt (net/socket.c:2396) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
Reachable by an unprivileged user via a user+network namespace.
Use the atomic sk_dst_reset() so the cache is cleared and released with a single xchg(): whichever side wins releases the dst once, the other sees NULL and does nothing. Behaviour is otherwise unchanged.
--- net/xfrm/xfrm_state.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/xfrm/xfrm_state.c +2 -2
@@ -2428,7 +2428,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)if (sockptr_is_null (optval) && !optlen) {xfrm_sk_policy_insert (sk,XFRM_POLICY_IN ,NULL );xfrm_sk_policy_insert (sk,XFRM_POLICY_OUT ,NULL ); -__sk_dst_reset (sk); +sk_dst_reset (sk);return 0 ; }
@@ -2468,7 +2468,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)if (err >=0 ) {xfrm_sk_policy_insert (sk, err, pol);xfrm_pol_put (pol); -__sk_dst_reset (sk); +sk_dst_reset (sk); err =0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 69/79 s390/vfio_ccw: Free all memory if cp_init() fails
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Farman <[email protected]>
[ Upstream commit 74186c2968f8f756ac3226b545b598457c910c75 ]
The routine cp_free() is called to unpin/free any memory once an I/O is completed successfully, or if cp_prefetch() fails. But if cp_init() fails, and cp->initialized is not enabled, the same routine cannot be used to free all the memory.
An attempt to address this exists in ccwchain_handle_ccw(), where a single call to ccwchain_free() is made for the currently-processed CCW segment. But this will leak other segments (created as a result of a Transfer in Channel) that had been allocated as part of the same channel program.
Address this by performing the cleanup outside of the recursive ccwchain_handle_ccw()/ccwchain_loop_tic() logic.
--- drivers/s390/cio/vfio_ccw_cp.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-)
drivers/s390/cio/vfio_ccw_cp.c +18 -4
@@ -490,9 +490,6 @@ static int ccwchain_handle_ccw(u32 cda, struct channel_program *cp)/* Loop for tics on this new chain. */ ret =ccwchain_loop_tic (chain, cp); -if (ret) -ccwchain_free (chain); -return ret; }
@@ -521,6 +518,23 @@ static int ccwchain_loop_tic(struct ccwchain *chain, struct channel_program *cp)return 0 ; } +static int ccwchain_build_ccws (u32 cda,struct channel_program *cp) +{ +struct ccwchain *chain, *temp; +int ret; + + ret =ccwchain_handle_ccw (cda, cp); + +if (ret) { +/* Cleanup if an error occurred */ +list_for_each_entry_safe (chain, temp, &cp->ccwchain_list, next) { +ccwchain_free (chain); + } + } + +return ret; +} +static int ccwchain_fetch_tic (struct ccwchain *chain,int idx,struct channel_program *cp)
@@ -699,7 +713,7 @@ int cp_init(struct channel_program *cp, union orb *orb)memcpy (&cp->orb, orb,sizeof (*orb));/* Build a ccwchain for the first CCW segment */ - ret =ccwchain_handle_ccw (orb->cmd.cpa, cp); + ret =ccwchain_build_ccws (orb->cmd.cpa, cp);if (!ret) { cp->initialized = true;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 70/79 Input: atkbd - skip deactivate for HONOR FMB-Ps internal keyboard
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cryolitia PukNgae <[email protected]>
[ Upstream commit 2aaf33c6e1e82561d7dce2345298a985a2483266 ]
After commit 9cf6e24c9fbf17e52de9fff07f12be7565ea6d61 ("Input: atkbd - do not skip atkbd_deactivate() when skipping ATKBD_CMD_GETID"), HONOR FMB-P, aka HONOR MagicBook Pro 14 2025's internal keyboard stops working. Adding the atkbd_deactivate_fixup quirk fixes it.
DMI: HONOR FMB-P/FMB-P-PCB, BIOS 1.13 05/08/2025
Stable-dep-of: 410c44b10967 ("Input: atkbd - skip deactivate for HONOR ZQC-P")
--- drivers/input/keyboard/atkbd.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/input/keyboard/atkbd.c +7 -0
@@ -1925,6 +1925,13 @@ static const struct dmi_system_id atkbd_ .callback = atkbd_deactivate_fixup, }, { + .matches = { +DMI_MATCH (DMI_SYS_VENDOR ,"HONOR" ), + DMI_MATCH(DMI_PRODUCT_NAME, "FMB-P"), + }, + .callback = atkbd_deactivate_fixup, + }, + {/* Lenovo Yoga Air 14 (83QK) */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
Greg Kroah-Hartman
maintainerPATCH 71/79 Input: atkbd - skip deactivate for HONOR ZQC-P
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Donglin Lyu <[email protected]>
[ Upstream commit 410c44b1096789d0c40fbee706520e981dba7bc1 ]
The internal keyboard on the HONOR ZQC-P (HONOR MagicBook Pro 14 2026) does not work after boot.
Using the kernel command line 'i8042.dumbkbd=1' makes the keyboard functional, but the CapsLock LED does not work. Adding the 'atkbd_deactivate_fixup' quirk fixes the keyboard and CapsLock LED natively without requiring boot parameters.
DMI: HONOR ZQC-P/ZQC-P-PCB, BIOS 1.09 03/19/2026
--- drivers/input/keyboard/atkbd.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-)
drivers/input/keyboard/atkbd.c +13 -5
@@ -1927,22 +1927,30 @@ static const struct dmi_system_id atkbd_ { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), - DMI_MATCH(DMI_PRODUCT_NAME, "FMB-P"), + DMI_MATCH(DMI_PRODUCT_NAME, "BCC-N"), }, .callback = atkbd_deactivate_fixup, }, { - /* Lenovo Yoga Air 14 (83QK) */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "83QK"), + DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), + DMI_MATCH(DMI_PRODUCT_NAME, "FMB-P"), }, .callback = atkbd_deactivate_fixup, }, { + /* HONOR MagicBook Pro 14 2026 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), - DMI_MATCH(DMI_PRODUCT_NAME, "BCC-N"), + DMI_MATCH(DMI_PRODUCT_NAME, "ZQC-P"), + }, + .callback = atkbd_deactivate_fixup, + }, + { + /* Lenovo Yoga Air 14 (83QK) */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83QK"), }, .callback = atkbd_deactivate_fixup, },
Greg Kroah-Hartman
maintainerPATCH 72/79 can: use skb hash instead of private variable in headroom
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Hartkopp <[email protected]>
commit d4fb6514ff8ed6912a71294e6b66a5d59ee88007 upstream.
The can_skb_priv::skbcnt variable is used to identify CAN skbs in the RX path analogue to the skb->hash.
As the skb hash is not filled in CAN skbs move the private skbcnt value to skb->hash and set skb->sw_hash accordingly. The skb->hash is a value used for RPS to identify skbs. Use it as intended.
--- drivers/net/can/dev/skb.c | 2 -- include/linux/can/core.h | 1 + include/linux/can/skb.h | 2 -- net/can/af_can.c | 14 +++++++++++--- net/can/bcm.c | 2 -- net/can/isotp.c | 3 --- net/can/j1939/socket.c | 1 - net/can/j1939/transport.c | 2 -- net/can/raw.c | 7 +++---- 9 files changed, 15 insertions(+), 19 deletions(-)
drivers/net/can/dev/skb.c +0 -2
@@ -202,7 +202,6 @@ static void init_can_skb_reserve(structskb_reset_transport_header (skb);can_skb_reserve (skb); -can_skb_prv (skb)->skbcnt =0 ; }struct sk_buff *alloc_can_skb (struct net_device *dev,struct can_frame **cf)
@@ -312,7 +311,6 @@ static bool can_skb_headroom_valid(strucif (skb->ip_summed ==CHECKSUM_NONE ) {/* init headroom */ can_skb_prv (skb)->ifindex = dev->ifindex; -can_skb_prv (skb)->skbcnt =0 ; skb->ip_summed =CHECKSUM_UNNECESSARY ;
include/linux/can/core.h +1 -0
@@ -58,6 +58,7 @@ extern void can_rx_unregister(struct net void *data);extern int can_send (struct sk_buff *skb,int loop); +void can_set_skb_uid (struct sk_buff *skb);void can_sock_destruct (struct sock *sk);#endif /* !_CAN_CORE_H */
include/linux/can/skb.h +0 -2
@@ -49,13 +49,11 @@ bool can_dropped_invalid_skb(struct net_/** * struct can_skb_priv - private additional data inside CAN sk_buffs * @ifindex: ifindex of the first interface the CAN frame appeared on -* @skbcnt: atomic counter to have an unique id together with skb pointer * @frame_len: length of CAN frame in data link layer * @cf: align to the following CAN frame at skb->data */ struct can_skb_priv {int ifindex; -int skbcnt;unsigned int frame_len;struct can_frame cf[]; };
net/can/af_can.c +11 -3
@@ -640,6 +640,16 @@ static int can_rcv_filter(struct can_devreturn matches; } +void can_set_skb_uid (struct sk_buff *skb) +{ +/* create non-zero unique skb identifier together with *skb */ +while (!(skb->hash)) + skb->hash =atomic_inc_return (&skbcounter); + + skb->sw_hash =1 ; +} +EXPORT_SYMBOL (can_set_skb_uid); +static void can_receive (struct sk_buff *skb,struct net_device *dev) {struct can_dev_rcv_lists *dev_rcv_lists;
@@ -651,9 +661,7 @@ static void can_receive(struct sk_buff *atomic_long_inc (&pkg_stats->rx_frames);atomic_long_inc (&pkg_stats->rx_frames_delta); -/* create non-zero unique skb identifier together with *skb */ -while (!(can_skb_prv (skb)->skbcnt)) -can_skb_prv (skb)->skbcnt =atomic_inc_return (&skbcounter); +can_set_skb_uid (skb);rcu_read_lock ();
net/can/bcm.c +0 -2
@@ -338,7 +338,6 @@ static void bcm_can_tx(struct bcm_op *opcan_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; -can_skb_prv (skb)->skbcnt =0 ;skb_put_data (skb, cf, op->cfsiz);
@@ -1582,7 +1581,6 @@ static int bcm_tx_send(struct msghdr *ms }can_skb_prv (skb)->ifindex = dev->ifindex; -can_skb_prv (skb)->skbcnt =0 ; skb->dev = dev;can_skb_set_owner (skb, sk); err =can_send (skb,1 );/* send with loopback */
net/can/isotp.c +0 -3
@@ -218,7 +218,6 @@ static int isotp_send_fc(struct sock *skcan_skb_reserve (nskb);can_skb_prv (nskb)->ifindex = dev->ifindex; -can_skb_prv (nskb)->skbcnt =0 ; nskb->dev = dev;can_skb_set_owner (nskb, sk);
@@ -773,7 +772,6 @@ static void isotp_send_cframe(struct isocan_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; -can_skb_prv (skb)->skbcnt =0 ; cf = (struct canfd_frame *)skb->data;skb_put_zero (skb, so->ll.mtu);
@@ -1069,7 +1067,6 @@ static int isotp_sendmsg(struct socket *can_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; -can_skb_prv (skb)->skbcnt =0 ; so->tx.len = size; so->tx.idx =0 ;
net/can/j1939/socket.c +0 -1
@@ -889,7 +889,6 @@ static struct sk_buff *j1939_sk_alloc_skcan_skb_reserve (skb);can_skb_prv (skb)->ifindex = ndev->ifindex; -can_skb_prv (skb)->skbcnt =0 ;skb_reserve (skb, offsetof(struct can_frame , data)); ret =memcpy_from_msg (skb_put (skb, size), msg, size);
net/can/j1939/transport.c +0 -2
@@ -613,7 +613,6 @@ sk_buff *j1939_tp_tx_dat_new(struct j193 skb->dev = priv->ndev;can_skb_reserve (skb);can_skb_prv (skb)->ifindex = priv->ndev->ifindex; -can_skb_prv (skb)->skbcnt =0 ;/* reserve CAN header */ skb_reserve (skb, offsetof(struct can_frame , data));
@@ -1553,7 +1552,6 @@ j1939_session *j1939_session_fresh_new(s skb->dev = priv->ndev;can_skb_reserve (skb);can_skb_prv (skb)->ifindex = priv->ndev->ifindex; -can_skb_prv (skb)->skbcnt =0 ; skcb =j1939_skb_to_cb (skb);memcpy (skcb, rel_skcb,sizeof (*skcb));
net/can/raw.c +3 -4
@@ -75,8 +75,8 @@ MODULE_ALIAS("can-proto-1");*/ struct uniqframe { -int skbcnt;const struct sk_buff *skb; +u32 hash;unsigned int join_rx_count; };
@@ -140,7 +140,7 @@ static void raw_rcv(struct sk_buff *oskb/* eliminate multiple filter matches for the same skb */ if (this_cpu_ptr (ro->uniq)->skb == oskb && -this_cpu_ptr (ro->uniq)->skbcnt ==can_skb_prv (oskb)->skbcnt) { +this_cpu_ptr (ro->uniq)->hash == oskb->hash) {if (!ro->join_filters)return ;
@@ -150,7 +150,7 @@ static void raw_rcv(struct sk_buff *oskbreturn ; }else {this_cpu_ptr (ro->uniq)->skb = oskb; -this_cpu_ptr (ro->uniq)->skbcnt =can_skb_prv (oskb)->skbcnt; +this_cpu_ptr (ro->uniq)->hash = oskb->hash;this_cpu_ptr (ro->uniq)->join_rx_count =1 ;/* drop first frame to check all enabled filters? */ if (ro->join_filters && ro->count >1 )
@@ -870,7 +870,6 @@ static int raw_sendmsg(struct socket *socan_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; -can_skb_prv (skb)->skbcnt =0 ;/* fill the skb before testing for valid CAN frames */ err =memcpy_from_msg (skb_put (skb, size), msg, size);
Greg Kroah-Hartman
maintainerPATCH 73/79 can: isotp: fix timer drain order, wakeup handling and tx_gen ordering
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Hartkopp <[email protected]>
commit 050f010f920da17c1044a4f174766ad553e770b6 upstream.
This patch is a follow-up to commit cf070fe33bfb ("can: isotp: serialize TX state transitions under so->rx_lock") which addresses following sashiko-bot findings:
- isotp_sendmsg(): drain so->txfrtimer first so a stale callback can't re-arm echotimer after the claim
- isotp_release(): wake so->wait after forcing ISOTP_SHUTDOWN so a sleeping sendmsg() claim isn't stranded
- isotp_sendmsg(): have both wait_event_interruptible() calls in isotp_sendmsg() also wake on ISOTP_SHUTDOWN and do not return claim to IDLE to avoid corrupting a concurrent isotp_release() process.
- isotp_sendmsg(): handle potential claim of a new transfer when the wait_event_interruptible() call returns in CAN_ISOTP_WAIT_TX_DONE mode. Don't touch timers and states of the new transfer if a new thread incremented so->tx_gen before getting the lock at err_event_drop.
- isotp_sendmsg(): handle a stuck can_send() and omit timer and state changes if a new transfer was claimed. wait_tx_done() returns the error recorded in so->tx_result[], tagged with the caller's own generation.
- isotp_tx_timeout(): on a claimed timeout, record the ECOMM error for the timed-out transfer's own generation in so->tx_result[]; sk->sk_err is raised unconditionally, same as every other error path here.
- isotp_tx_gen_done()/isotp_tx_timeout(): always read tx.state (acquire) before tx_gen - the reverse order let a weakly ordered CPU pair a fresh tx.state with a stale tx_gen/tx_result slot.
- isotp_sendmsg(): wait_tx_done: drain sk_err via sock_error() once we have read the result from so->tx_result[], so an already-reported error doesn't stay latched for a later poll()/SO_ERROR.
Also align the remaining lock-free so->tx.state/rx.state/cfecho accesses and use skb->hash as unique loopback echo frame indicator.
--- net/can/isotp.c | 317 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 230 insertions(+), 87 deletions(-)
net/can/isotp.c +230 -87
@@ -114,6 +114,15 @@ MODULE_ALIAS("can-proto-6");#define ISOTP_FC_TIMEOUT 1/* 1 sec */ #define ISOTP_ECHO_TIMEOUT 2/* 2 secs */ +/* so->tx_result[so->tx_gen % ISOTP_TX_RESULT_SLOTS] holds the packed value +* (err << ISOTP_TX_RESULT_GEN_BITS | gen) for each tx generation slot, so it +* can be handled with a single READ_ONCE()/WRITE_ONCE() access. +*/ +#define ISOTP_TX_RESULT_SLOTS 4 +#define ISOTP_TX_RESULT_GEN_BITS 24 +#define ISOTP_TX_RESULT_GEN_MASK ((1U << ISOTP_TX_RESULT_GEN_BITS) - 1) +#define ISOTP_TX_RESULT_ERR_MASK 0xFF +enum {ISOTP_IDLE =0 ,ISOTP_WAIT_FIRST_FC ,
@@ -150,7 +159,8 @@ struct isotp_sock {u32 force_tx_stmin;u32 force_rx_stmin;u32 cfecho;/* consecutive frame echo tag */ -u32 tx_gen;/* generation, bumped per new tx transfer */ +u32 tx_gen;/* transfer generation, increased per new tx transfer */ +u32 tx_result[ISOTP_TX_RESULT_SLOTS ];/* per-generation result slots */ struct tpcon rx, tx;struct list_head notifier;wait_queue_head_t wait;
@@ -161,6 +171,65 @@ static LIST_HEAD(isotp_notifier_list);static DEFINE_SPINLOCK (isotp_notifier_lock);static struct isotp_sock *isotp_busy_notifier; +/* increase (24 bit) tx generation value */ +static u32 isotp_inc_tx_gen (u32 gen) +{ +return (gen +1 ) &ISOTP_TX_RESULT_GEN_MASK ; +} + +/* store 8 bit error and 24 bit tx generation values in packed u32 element */ +static u32 isotp_pack_tx_result (u32 gen,int err) +{ +return gen | ((u32 )err <<ISOTP_TX_RESULT_GEN_BITS ); +} + +/* get the 24 bit tx generation value from the tx result */ +static u32 isotp_get_tx_gen (u32 gen_err) +{ +return gen_err &ISOTP_TX_RESULT_GEN_MASK ; +} + +/* get the 8 bit error value from the tx result */ +static u32 isotp_get_tx_err (u32 gen_err) +{ +return (gen_err >>ISOTP_TX_RESULT_GEN_BITS ) &ISOTP_TX_RESULT_ERR_MASK ; +} + +/* store transfer result in per-generation%4 so->tx_result[] slot */ +static void isotp_set_tx_result (struct isotp_sock *so,u32 gen,int err) +{ +WRITE_ONCE (so->tx_result[gen %ISOTP_TX_RESULT_SLOTS ], +isotp_pack_tx_result (gen, err)); +} + +/* fetch the result recorded for 'gen', as a (negative) errno (0 for success) */ +static int isotp_get_tx_result (struct isotp_sock *so,u32 gen) +{ +u32 result =READ_ONCE (so->tx_result[gen %ISOTP_TX_RESULT_SLOTS ]); + +if (isotp_get_tx_gen (result) != gen) { +pr_notice_once ("can-isotp: tx_result[] slot reused before read\n" ); + +/* report failure rather than risk a false success */ +return -ECOMM ; + } + +return -(isotp_get_tx_err (result)); +} + +/* true if done, shut down or superseded ('gen' is no longer the active +* transfer). Reads tx.state first (acquire) so tx_gen/tx_result reads +* below see at least what that state write published (common sequence). +*/ +static bool isotp_tx_gen_done (struct isotp_sock *so,u32 gen) +{ +/* read tx.state first for the common sequence */ +u32 state =smp_load_acquire (&so->tx.state); + +return state ==ISOTP_IDLE || state ==ISOTP_SHUTDOWN || +READ_ONCE (so->tx_gen) != gen; +} +static inline struct isotp_sock *isotp_sk (const struct sock *sk) {return (struct isotp_sock *)sk;
@@ -183,7 +252,7 @@ static enum hrtimer_restart isotp_rx_tim rxtimer);struct sock *sk = &so->sk; -if (so->rx.state ==ISOTP_WAIT_DATA ) { +if (READ_ONCE (so->rx.state) ==ISOTP_WAIT_DATA ) {/* we did not get new data frames in time */ /* report 'connection timed out' */
@@ -192,7 +261,7 @@ static enum hrtimer_restart isotp_rx_timsk_error_report (sk);/* reset rx state */ - so->rx.state =ISOTP_IDLE ; +WRITE_ONCE (so->rx.state,ISOTP_IDLE ); }return HRTIMER_NORESTART ;
@@ -349,20 +418,19 @@ static void isotp_send_cframe(struct isostatic int isotp_rcv_fc (struct isotp_sock *so,struct canfd_frame *cf,int ae) {struct sock *sk = &so->sk; +int tx_err =EBADMSG ;/* default for unknown FC status */ -if (so->tx.state !=ISOTP_WAIT_FC && - so->tx.state !=ISOTP_WAIT_FIRST_FC ) +if (READ_ONCE (so->tx.state) !=ISOTP_WAIT_FC && +READ_ONCE (so->tx.state) !=ISOTP_WAIT_FIRST_FC )return 0 ;hrtimer_cancel (&so->txtimer);/* isotp_tx_timeout() may have given up on this job while -* hrtimer_cancel() above waited for it to finish; so->rx_lock -* (held by our caller isotp_rcv()) rules out a concurrent claim, -* so a plain recheck is enough here. +* hrtimer_cancel() above waited for it to finish => recheck */ -if (so->tx.state !=ISOTP_WAIT_FC && - so->tx.state !=ISOTP_WAIT_FIRST_FC ) +if (READ_ONCE (so->tx.state) !=ISOTP_WAIT_FC && +READ_ONCE (so->tx.state) !=ISOTP_WAIT_FIRST_FC )return 1 ; if ((cf->len < ae + FC_CONTENT_SZ) ||
@@ -373,13 +441,15 @@ static int isotp_rcv_fc(struct isotp_socif (!sock_flag (sk,SOCK_DEAD ))sk_error_report (sk); - so->tx.state =ISOTP_IDLE ; +isotp_set_tx_result (so, so->tx_gen,EBADMSG ); +/* set to IDLE after publishing tx_result */ +smp_store_release (&so->tx.state,ISOTP_IDLE );wake_up_interruptible (&so->wait);return 1 ; }/* get communication parameters only from the first FC frame */ -if (so->tx.state ==ISOTP_WAIT_FIRST_FC ) { +if (READ_ONCE (so->tx.state) ==ISOTP_WAIT_FIRST_FC ) { so->txfc.bs = cf->data[ae +1 ]; so->txfc.stmin = cf->data[ae +2 ];
@@ -402,13 +472,13 @@ static int isotp_rcv_fc(struct isotp_soc so->tx_gap =ktime_add_ns (so->tx_gap, (so->txfc.stmin -0xF0 ) *100000 ); - so->tx.state =ISOTP_WAIT_FC ; +WRITE_ONCE (so->tx.state,ISOTP_WAIT_FC ); }switch (cf->data[ae] &0x0F ) {case ISOTP_FC_CTS : so->tx.bs =0 ; - so->tx.state =ISOTP_SENDING ; +WRITE_ONCE (so->tx.state,ISOTP_SENDING );/* send CF frame and enable echo timeout handling */ hrtimer_start (&so->echotimer,ktime_set (ISOTP_ECHO_TIMEOUT ,0 ),HRTIMER_MODE_REL_SOFT );
@@ -423,14 +493,19 @@ static int isotp_rcv_fc(struct isotp_soccase ISOTP_FC_OVFLW :/* overflow on receiver side - report 'message too long' */ - sk->sk_err =EMSGSIZE ; -if (!sock_flag (sk,SOCK_DEAD )) -sk_error_report (sk); + tx_err =EMSGSIZE ; fallthrough;default : -/* stop this tx job */ - so->tx.state =ISOTP_IDLE ; +/* reserved/unknown flow status (tx_err defaults to EBADMSG) */ + + sk->sk_err = tx_err; +if (!sock_flag (sk,SOCK_DEAD )) +sk_error_report (sk); + +isotp_set_tx_result (so, so->tx_gen, tx_err); +/* set to IDLE after publishing tx_result */ +smp_store_release (&so->tx.state,ISOTP_IDLE );wake_up_interruptible (&so->wait); }return 0 ;
@@ -443,7 +518,7 @@ static int isotp_rcv_sf(struct sock *sk,struct sk_buff *nskb;hrtimer_cancel (&so->rxtimer); - so->rx.state =ISOTP_IDLE ; +WRITE_ONCE (so->rx.state,ISOTP_IDLE );if (!len || len > cf->len - pcilen)return 1 ;
@@ -477,7 +552,7 @@ static int isotp_rcv_ff(struct sock *sk,int ff_pci_sz;hrtimer_cancel (&so->rxtimer); - so->rx.state =ISOTP_IDLE ; +WRITE_ONCE (so->rx.state,ISOTP_IDLE );/* get the used sender LL_DL from the (first) CAN frame data length */ so->rx.ll_dl =padlen (cf->len);
@@ -521,7 +596,7 @@ static int isotp_rcv_ff(struct sock *sk,/* initial setup for this pdu reception */ so->rx.sn =1 ; - so->rx.state =ISOTP_WAIT_DATA ; +WRITE_ONCE (so->rx.state,ISOTP_WAIT_DATA );/* no creation of flow control frames */ if (so->opt.flags &CAN_ISOTP_LISTEN_MODE )
@@ -539,7 +614,7 @@ static int isotp_rcv_cf(struct sock *sk,struct sk_buff *nskb;int i; -if (so->rx.state !=ISOTP_WAIT_DATA ) +if (READ_ONCE (so->rx.state) !=ISOTP_WAIT_DATA )return 0 ;/* drop if timestamp gap is less than force_rx_stmin nano secs */
@@ -554,11 +629,9 @@ static int isotp_rcv_cf(struct sock *sk,hrtimer_cancel (&so->rxtimer);/* isotp_rx_timer_handler() may have raced us for so->rx.state -* while hrtimer_cancel() above waited for it to finish, already -* reporting ETIMEDOUT and resetting the reception; don't process -* this CF into a reassembly that has already been given up on. +* while hrtimer_cancel() above waited for it to finish => recheck */ -if (so->rx.state !=ISOTP_WAIT_DATA ) +if (READ_ONCE (so->rx.state) !=ISOTP_WAIT_DATA )return 1 ;/* CFs are never longer than the FF */
@@ -579,7 +652,7 @@ static int isotp_rcv_cf(struct sock *sk,sk_error_report (sk);/* reset rx state */ - so->rx.state =ISOTP_IDLE ; +WRITE_ONCE (so->rx.state,ISOTP_IDLE );return 1 ; } so->rx.sn++;
@@ -593,7 +666,7 @@ static int isotp_rcv_cf(struct sock *sk,if (so->rx.idx >= so->rx.len) {/* we are done */ - so->rx.state =ISOTP_IDLE ; +WRITE_ONCE (so->rx.state,ISOTP_IDLE );if ((so->opt.flags &ISOTP_CHECK_PADDING ) &&check_pad (so, cf, i +1 , so->opt.rxpad_content)) {
@@ -664,8 +737,10 @@ static void isotp_rcv(struct sk_buff *skif (so->opt.flags &CAN_ISOTP_HALF_DUPLEX ) {/* check rx/tx path half duplex expectations */ -if ((so->tx.state !=ISOTP_IDLE && n_pci_type !=N_PCI_FC ) || - (so->rx.state !=ISOTP_IDLE && n_pci_type ==N_PCI_FC )) +if ((READ_ONCE (so->tx.state) !=ISOTP_IDLE && + n_pci_type !=N_PCI_FC ) || + (READ_ONCE (so->rx.state) !=ISOTP_IDLE && + n_pci_type ==N_PCI_FC )) goto out_unlock; }
@@ -759,6 +834,7 @@ static void isotp_send_cframe(struct isostruct canfd_frame *cf;int can_send_ret;int ae = (so->opt.flags &CAN_ISOTP_EXTEND_ADDR ) ?1 :0 ; +u32 old_cfecho; dev =dev_get_by_index (sock_net (sk), so->ifindex);if (!dev)
@@ -773,6 +849,9 @@ static void isotp_send_cframe(struct isocan_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; +/* set uid in tx skb to identify CF echo frames */ +can_set_skb_uid (skb); + cf = (struct canfd_frame *)skb->data;skb_put_zero (skb, so->ll.mtu);
@@ -789,12 +868,15 @@ static void isotp_send_cframe(struct iso skb->dev = dev;can_skb_set_owner (skb, sk); -/* cfecho should have been zero'ed by init/isotp_rcv_echo() */ -if (so->cfecho) -pr_notice_once ("can-isotp: cfecho is %08X != 0\n" , so->cfecho); +/* zero'ed by init/isotp_rcv_echo(); reached lock-free via +* isotp_txfr_timer_handler() too, so use READ_ONCE()/WRITE_ONCE() +*/ + old_cfecho =READ_ONCE (so->cfecho); +if (old_cfecho) +pr_notice_once ("can-isotp: cfecho is %08X != 0\n" , old_cfecho);/* set consecutive frame echo tag */ - so->cfecho = *(u32 *)cf->data; +WRITE_ONCE (so->cfecho, skb->hash);/* send frame with local echo enabled */ can_send_ret =can_send (skb,1 );
@@ -846,7 +928,6 @@ static void isotp_rcv_echo(struct sk_buf {struct sock *sk = (struct sock *)data;struct isotp_sock *so =isotp_sk (sk); -struct canfd_frame *cf = (struct canfd_frame *)skb->data;/* only handle my own local echo CF/SF skb's (no FF!) */ if (skb->sk != sk)
@@ -858,32 +939,35 @@ static void isotp_rcv_echo(struct sk_bufspin_lock (&so->rx_lock);/* so->cfecho may since belong to a new transfer; recheck under lock */ -if (so->cfecho != *(u32 *)cf->data) +if (READ_ONCE (so->cfecho) != skb->hash) goto out_unlock;/* cancel local echo timeout */ hrtimer_cancel (&so->echotimer);/* local echo skb with consecutive frame has been consumed */ - so->cfecho =0 ; +WRITE_ONCE (so->cfecho,0 );/* claiming a transfer also takes so->rx_lock, so a plain recheck * is enough: so->tx.state can't have flipped to ISOTP_SENDING for * a new claim while we're still in here */ -if (so->tx.state !=ISOTP_SENDING ) +if (READ_ONCE (so->tx.state) !=ISOTP_SENDING ) goto out_unlock;if (so->tx.idx >= so->tx.len) {/* we are done */ - so->tx.state =ISOTP_IDLE ; + +isotp_set_tx_result (so, so->tx_gen,0 ); +/* set to IDLE after publishing tx_result */ +smp_store_release (&so->tx.state,ISOTP_IDLE );wake_up_interruptible (&so->wait); goto out_unlock; }if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {/* stop and wait for FC with timeout */ - so->tx.state =ISOTP_WAIT_FC ; +WRITE_ONCE (so->tx.state,ISOTP_WAIT_FC );hrtimer_start (&so->txtimer,ktime_set (ISOTP_FC_TIMEOUT ,0 ),HRTIMER_MODE_REL_SOFT ); goto out_unlock;
@@ -905,16 +989,20 @@ out_unlock:spin_unlock (&so->rx_lock); } -/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get -* cancelled under so->rx_lock elsewhere, so this must stay lock-free to -* avoid deadlocking with that; uses so->tx_gen instead to avoid tainting -* a new transfer with an error from the one that just timed out. +/* isotp_tx_timeout: we did not get any flow control or echo frame in time +* +* Shared by so->txtimer's and so->echotimer's callbacks. Both timers get +* cancelled under so->rx_lock elsewhere, so this must stay lock-free. +* +* tx.state is acquired before tx_gen. Common sequence in isotp_tx_gen_done(). +* cmpxchg() only orders itself, not the two preceding loads. */ static enum hrtimer_restart isotp_tx_timeout (struct isotp_sock *so) {struct sock *sk = &so->sk; +/* read tx.state first for the common sequence */ +u32 old_state =smp_load_acquire (&so->tx.state);u32 gen =READ_ONCE (so->tx_gen); -u32 old_state =READ_ONCE (so->tx.state);/* don't handle timeouts in IDLE or SHUTDOWN state */ if (old_state ==ISOTP_IDLE || old_state ==ISOTP_SHUTDOWN )
@@ -924,14 +1012,14 @@ static enum hrtimer_restart isotp_tx_timif (cmpxchg (&so->tx.state, old_state,ISOTP_IDLE ) != old_state)return HRTIMER_NORESTART ; -/* we did not get any flow control or echo frame in time */ +/* detected timeout: report 'communication error on send' */ -if (READ_ONCE (so->tx_gen) == gen) { -/* report 'communication error on send' */ - sk->sk_err =ECOMM ; -if (!sock_flag (sk,SOCK_DEAD )) -sk_error_report (sk); - } +/* a stale read of this slot by a waiter still falls back to ECOMM */ +isotp_set_tx_result (so, gen,ECOMM ); + + sk->sk_err =ECOMM ; +if (!sock_flag (sk,SOCK_DEAD )) +sk_error_report (sk);wake_up_interruptible (&so->wait);
@@ -966,7 +1054,7 @@ static enum hrtimer_restart isotp_txfr_t HRTIMER_MODE_REL_SOFT);/* cfecho should be consumed by isotp_rcv_echo() here */ -if (so->tx.state ==ISOTP_SENDING && !so->cfecho) +if (READ_ONCE (so->tx.state) ==ISOTP_SENDING && !READ_ONCE (so->cfecho))isotp_send_cframe (so);return HRTIMER_NORESTART ;
@@ -984,10 +1072,12 @@ static int isotp_sendmsg(struct socket *s64 hrtimer_sec =ISOTP_ECHO_TIMEOUT ;struct hrtimer *tx_hrt = &so->echotimer;u32 new_state =ISOTP_SENDING ; +u32 my_gen; +u32 old_cfecho;int off;int err; -if (!so->bound || so->tx.state ==ISOTP_SHUTDOWN ) +if (!so->bound ||READ_ONCE (so->tx.state) ==ISOTP_SHUTDOWN )return -EADDRNOTAVAIL ;/* claim the socket under so->rx_lock: this serializes the claim
@@ -1004,29 +1094,33 @@ static int isotp_sendmsg(struct socket *if (msg->msg_flags &MSG_DONTWAIT )return -EAGAIN ; -if (so->tx.state ==ISOTP_SHUTDOWN ) +if (READ_ONCE (so->tx.state) ==ISOTP_SHUTDOWN )return -EADDRNOTAVAIL ;/* wait for complete transmission of current pdu */ err =wait_event_interruptible (so->wait, - so->tx.state ==ISOTP_IDLE ); +READ_ONCE (so->tx.state) ==ISOTP_IDLE || +READ_ONCE (so->tx.state) ==ISOTP_SHUTDOWN );if (err)return err; } -/* new transfer: bump so->tx_gen and drain the old one's timers, -* still under the so->rx_lock we just claimed the socket with -*/ -WRITE_ONCE (so->tx.state,ISOTP_SENDING ); -WRITE_ONCE (so->tx_gen,READ_ONCE (so->tx_gen) +1 ); +/* txfrtimer's callback re-arms echotimer lock-free: drain it first */ +hrtimer_cancel (&so->txfrtimer);hrtimer_cancel (&so->txtimer);hrtimer_cancel (&so->echotimer); -hrtimer_cancel (&so->txfrtimer); - so->cfecho =0 ; + +/* new transfer: increment so->tx_gen and set tx.state after barrier */ + my_gen =isotp_inc_tx_gen (READ_ONCE (so->tx_gen)); +isotp_set_tx_result (so, my_gen,ECOMM );/* prevent stale slot matching */ +WRITE_ONCE (so->tx_gen, my_gen); +smp_wmb ();/* see smp_load_acquire() in isotp_tx_[timeout|gen_done] */ +WRITE_ONCE (so->tx.state,ISOTP_SENDING ); +WRITE_ONCE (so->cfecho,0 );spin_unlock_bh (&so->rx_lock);/* so->bound is only checked once above - a wakeup may have -* unbound/rebound the socket meanwhile, so re-validate it +* unbound/rebound the socket meanwhile => recheck */ if (!so->bound) { err = -EADDRNOTAVAIL ;
@@ -1068,6 +1162,9 @@ static int isotp_sendmsg(struct socket *can_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; +/* set uid in tx skb to identify CF echo frames */ +can_set_skb_uid (skb); + so->tx.len = size; so->tx.idx =0 ;
@@ -1075,8 +1172,9 @@ static int isotp_sendmsg(struct socket *skb_put_zero (skb, so->ll.mtu);/* cfecho should have been zero'ed by init / former isotp_rcv_echo() */ -if (so->cfecho) -pr_notice_once ("can-isotp: uninit cfecho %08X\n" , so->cfecho); + old_cfecho =READ_ONCE (so->cfecho); +if (old_cfecho) +pr_notice_once ("can-isotp: uninit cfecho %08X\n" , old_cfecho);/* check for single frame transmission depending on TX_DL */ if (size <= so->tx.ll_dl -SF_PCI_SZ4 - ae - off) {
@@ -1104,7 +1202,7 @@ static int isotp_sendmsg(struct socket * cf->data[ae] |= size;/* set CF echo tag for isotp_rcv_echo() (SF-mode) */ - so->cfecho = *(u32 *)cf->data; +WRITE_ONCE (so->cfecho, skb->hash); }else {/* send first frame */
@@ -1121,7 +1219,7 @@ static int isotp_sendmsg(struct socket * so->txfc.bs =0 ;/* set CF echo tag for isotp_rcv_echo() (CF-mode) */ - so->cfecho = *(u32 *)cf->data; +WRITE_ONCE (so->cfecho, skb->hash); }else {/* standard flow control check */ new_state =ISOTP_WAIT_FIRST_FC ;
@@ -1131,12 +1229,12 @@ static int isotp_sendmsg(struct socket * tx_hrt = &so->txtimer;/* no CF echo tag for isotp_rcv_echo() (FF-mode) */ - so->cfecho =0 ; +WRITE_ONCE (so->cfecho,0 ); } }spin_lock_bh (&so->rx_lock); -if (so->tx.state ==ISOTP_SHUTDOWN ) { +if (READ_ONCE (so->tx.state) ==ISOTP_SHUTDOWN ) {/* isotp_release() has since taken over and already drained * our timers - don't send into a socket that's going away */
@@ -1147,7 +1245,7 @@ static int isotp_sendmsg(struct socket *return -EADDRNOTAVAIL ; }/* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */ - so->tx.state = new_state; +WRITE_ONCE (so->tx.state, new_state);hrtimer_start (tx_hrt,ktime_set (hrtimer_sec,0 ),HRTIMER_MODE_REL_SOFT );spin_unlock_bh (&so->rx_lock);
@@ -1164,20 +1262,49 @@ static int isotp_sendmsg(struct socket * __func__, ERR_PTR(err));spin_lock_bh (&so->rx_lock); + +/* new transfer already claimed by a concurrent completion, +* timeout or sendmsg() while we were stuck in can_send()? +*/ +if (READ_ONCE (so->tx_gen) != my_gen) { +/* don't touch timers and state of the new transfer */ +spin_unlock_bh (&so->rx_lock); +return err; + } +/* no transmission -> no timeout monitoring */ hrtimer_cancel (tx_hrt); goto err_out_drop_locked; }if (wait_tx_done) { -/* wait for complete transmission of current pdu */ - err =wait_event_interruptible (so->wait, so->tx.state ==ISOTP_IDLE ); +/* wake up for: +* - concurrent sendmsg() claiming a new transfer +* - complete transmission of current PDU +* - shutdown state change in isotp_release() +* isotp_tx_gen_done() uses common tx.state/tx_gen read sequence +*/ + err =wait_event_interruptible (so->wait, +isotp_tx_gen_done (so, my_gen));if (err) goto err_event_drop; - err =sock_error (sk); -if (err) -return err; +/* still our claim, but isotp_release() force-shut it down */ +if (smp_load_acquire (&so->tx.state) ==ISOTP_SHUTDOWN && +READ_ONCE (so->tx_gen) == my_gen) { + err = -EADDRNOTAVAIL ; + goto err_event_drop; + } + +/* own completion, or tx_gen moved on - either way this is +* what isotp_get_tx_result() recorded for my_gen +*/ + err =isotp_get_tx_result (so, my_gen); + +/* drain to avoid stale error for a later poll()/SO_ERROR */ +sock_error (sk); + +return err ? err : size; }return size;
@@ -1187,15 +1314,26 @@ err_out_drop:spin_lock_bh (&so->rx_lock); goto err_out_drop_locked; err_event_drop: -/* interrupted waiting on our own transfer - drain its timers */ +/* interrupted or shut down while waiting on our own transfer */ spin_lock_bh (&so->rx_lock); + +/* new transfer already started by concurrent sendmsg()? */ +if (READ_ONCE (so->tx_gen) != my_gen) { +/* don't touch timers and states of the new transfer */ +spin_unlock_bh (&so->rx_lock); +return err; + } +hrtimer_cancel (&so->txfrtimer);hrtimer_cancel (&so->txtimer);hrtimer_cancel (&so->echotimer); err_out_drop_locked:/* release the claim; so->rx_lock still held from above */ - so->cfecho =0 ; - so->tx.state =ISOTP_IDLE ; +WRITE_ONCE (so->cfecho,0 ); + +/* only claim to IDLE if isotp_release() has not taken over */ +if (READ_ONCE (so->tx.state) !=ISOTP_SHUTDOWN ) +WRITE_ONCE (so->tx.state,ISOTP_IDLE );spin_unlock_bh (&so->rx_lock);wake_up_interruptible (&so->wait);
@@ -1261,8 +1399,9 @@ static int isotp_release(struct socket */* best-effort: wait for a running pdu to finish, but don't block on * it forever - give up after the first signal */ -while (so->tx.state !=ISOTP_IDLE && -wait_event_interruptible (so->wait, so->tx.state ==ISOTP_IDLE ) ==0 ) +while (READ_ONCE (so->tx.state) !=ISOTP_IDLE && +wait_event_interruptible (so->wait, +READ_ONCE (so->tx.state) ==ISOTP_IDLE ) ==0 ) ;/* claim the socket under so->rx_lock like sendmsg() does, so its
@@ -1270,9 +1409,12 @@ static int isotp_release(struct socket ** unconditionally, even when a signal cut the wait above short */ spin_lock_bh (&so->rx_lock); - so->tx.state =ISOTP_SHUTDOWN ; +WRITE_ONCE (so->tx.state,ISOTP_SHUTDOWN );spin_unlock_bh (&so->rx_lock); - so->rx.state =ISOTP_IDLE ; +WRITE_ONCE (so->rx.state,ISOTP_IDLE ); + +/* forced SHUTDOWN may have skipped IDLE (gave up on a signal) */ +wake_up_interruptible (&so->wait);spin_lock (&isotp_notifier_lock);while (isotp_busy_notifier == so) {
@@ -1387,7 +1529,8 @@ static int isotp_bind(struct socket *soc* with so->bound in the same lock_sock() section above, so there is * no window in which a concurrent isotp_notify() could be missed. */ -if (so->tx.state !=ISOTP_IDLE || so->rx.state !=ISOTP_IDLE ) { +if (READ_ONCE (so->tx.state) !=ISOTP_IDLE || +READ_ONCE (so->rx.state) !=ISOTP_IDLE ) { err = -EAGAIN ; goto out; }
@@ -1421,7 +1564,7 @@ static int isotp_bind(struct socket *soc isotp_rcv, sk, "isotp", sk);/* no consecutive frame echo skb in flight */ - so->cfecho =0 ; +WRITE_ONCE (so->cfecho,0 );/* register for echo skb's */ can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
@@ -1764,7 +1907,7 @@ static __poll_t isotp_poll(struct file *poll_wait (file, &so->wait, wait);/* Check for false positives due to TX state */ -if ((mask &EPOLLWRNORM ) && (so->tx.state !=ISOTP_IDLE )) +if ((mask &EPOLLWRNORM ) && (READ_ONCE (so->tx.state) !=ISOTP_IDLE )) mask &= ~(EPOLLOUT |EPOLLWRNORM );return mask;
Greg Kroah-Hartman
maintainerPATCH 74/79 misc: fastrpc: Fix double free of buf in error path
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sukrut Bellary <[email protected]>
commit e8c276d4dc0e19ee48385f74426aebc855b49aaf upstream.
smatch warning:drivers/misc/fastrpc.c:1926 fastrpc_req_mmap() error: double free of 'buf'
In fastrpc_req_mmap() error path, the fastrpc buffer is freed in fastrpc_req_munmap_impl() if unmap is successful.
But in the end, there is an unconditional call to fastrpc_buf_free(). So the above case triggers the double free of fastrpc buf.
--- drivers/misc/fastrpc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/misc/fastrpc.c +2 -3
@@ -1774,7 +1774,8 @@ static int fastrpc_req_mmap(struct fastr &args[0]);if (err) {dev_err (dev,"mmap error (len 0x%08llx)\n" , buf->size); - goto err_invoke; +fastrpc_buf_free (buf); +return err; }/* update the buffer to be able to deallocate the memory on the DSP */
@@ -1799,8 +1800,6 @@ static int fastrpc_req_mmap(struct fastr err_assign:fastrpc_req_munmap_impl (fl, buf); -err_invoke: -fastrpc_buf_free (buf);return err; }
Greg Kroah-Hartman
maintainerPATCH 75/79 HID: nintendo: fix out-of-bounds read in joycon_ctlr_read_handler()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ibrahim Hashimov <[email protected]>
commit 27b376b945c0aac46fcdfcc950b14a85b874b557 upstream.
joycon_ctlr_read_handler() casts an incoming HID input report to struct joycon_input_report and parses it, guarding the cast only with a 12-byte length check:
if (size >= 12) /* make sure it contains the input report */ joycon_parse_report(ctlr, (struct joycon_input_report *)data);
struct joycon_input_report is 49 bytes: a 13-byte header followed by a union whose IMU arm is 36 bytes. For an IMU report joycon_parse_report() -> joycon_parse_imu_report() walks that union (struct offsets 13..48), so a report of exactly 12 bytes with data[0] == JC_INPUT_IMU_DATA passes the guard yet is read up to 37 bytes past its declared length. The over-read bytes are decoded into accelerometer/gyroscope values and forwarded to userspace through the "(IMU)" input device, leaking driver-internal memory. data[0] and size are fully controlled by a malicious or spoofed Joy-Con/Pro Controller.
Receive buffers are sized to the maximum report length, so this is an over-read within the allocation rather than a slab OOB, but the decoded bytes still reach userspace.
The sibling subcmd path in joycon_ctlr_handle_event() already bounds the same cast correctly:
if (size < sizeof(struct joycon_input_report) || data[0] != JC_INPUT_SUBCMD_REPLY) break;
Use the same sizeof(struct joycon_input_report) bound here.
--- drivers/hid/hid-nintendo.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/hid/hid-nintendo.c +6 -1
@@ -2140,7 +2140,12 @@ static int joycon_ctlr_read_handler(stru {if (data[0 ] ==JC_INPUT_SUBCMD_REPLY || data[0 ] ==JC_INPUT_IMU_DATA || data[0 ] ==JC_INPUT_MCU_DATA ) { -if (size >=12 )/* make sure it contains the input report */ +/* +* The whole struct is cast and parsed below, including the +* IMU/subcmd union, not just the 12-byte partial header this +* used to check for. +*/ +if (size >=sizeof (struct joycon_input_report ))joycon_parse_report (ctlr, (struct joycon_input_report *)data); }
Greg Kroah-Hartman
maintainerPATCH 76/79 HID: core: fix number/pointer type confusion on long items
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jann Horn <[email protected]>
commit 28abce951343fcec26e397610868efa4e1395c3f upstream.
When fetch_item() is called by hid_scan_report() on an item with HID_ITEM_TAG_LONG, it stores a pointer to the item data in item->data.longdata instead of storing a value directly in item->data.{u8/u16/u32}.
When item_udata() or item_sdata() encounters such an item, it incorrectly assumes that the item is in short format, and therefore returns the lower part of a kernel pointer reinterpreted as a number.
When a HID device is connected whose descriptor contains a HID_GLOBAL_ITEM_TAG_REPORT_SIZE encoded in long format with size=4, this causes the lower half of a kernel pointer to be printed into dmesg as a number, like this:
hid (null): invalid report_size 107953555
To fix it, let item_udata() and item_sdata() verify that the item is in short format.
Note that this bug only affects hid_scan_report(), while the main parsing pass hid_parse_collections() will always bail out when encountering a long item.
Sidenote: There are currently no users of data.longdata; maybe we should just remove any parsing of long-format descriptors as a follow-up.
--- drivers/hid/hid-core.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/hid/hid-core.c +6 -0
@@ -349,6 +349,9 @@ static int hid_add_field(struct hid_parsstatic u32 item_udata (struct hid_item *item) { +if (item->format !=HID_ITEM_FORMAT_SHORT ) +return 0 ; +switch (item->size) {case 1 :return item->data.u8;case 2 :return item->data.u16;
@@ -359,6 +362,9 @@ static u32 item_udata(struct hid_item *istatic s32 item_sdata (struct hid_item *item) { +if (item->format !=HID_ITEM_FORMAT_SHORT ) +return 0 ; +switch (item->size) {case 1 :return item->data.s8;case 2 :return item->data.s16;
Greg Kroah-Hartman
maintainerPATCH 77/79 HID: sensor: custom: Fix use-after-free in enable_sensor
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
commit ad8fb82b04422f49530d2aa2753cc81d1c60102c upstream.
enable_sensor_store() can call set_power_report_state(), which dereferences sensor_inst->power_state and sensor_inst->report_state. These pointers refer to entries in sensor_inst->fields.
Create the field attributes before exposing the enable_sensor sysfs attribute, so enable_sensor cannot be accessed before the state it depends on has been initialized.
On remove, delete enable_sensor before freeing the field attributes, so a concurrent sysfs write cannot dereference freed memory through power_state or report_state.
--- drivers/hid/hid-sensor-custom.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
drivers/hid/hid-sensor-custom.c +9 -8
@@ -910,26 +910,26 @@ static int hid_sensor_custom_probe(strucreturn ret; } - ret =sysfs_create_group (&sensor_inst->pdev->dev.kobj, - &enable_sensor_attr_group); + ret =hid_sensor_custom_add_attributes (sensor_inst);if (ret) goto err_remove_callback; - ret =hid_sensor_custom_add_attributes (sensor_inst); + ret =sysfs_create_group (&sensor_inst->pdev->dev.kobj, + &enable_sensor_attr_group);if (ret) - goto err_remove_group; + goto err_remove_attributes; ret =hid_sensor_custom_dev_if_add (sensor_inst);if (ret) - goto err_remove_attributes; + goto err_remove_group;return 0 ; -err_remove_attributes: -hid_sensor_custom_remove_attributes (sensor_inst); err_remove_group:sysfs_remove_group (&sensor_inst->pdev->dev.kobj, &enable_sensor_attr_group); +err_remove_attributes: +hid_sensor_custom_remove_attributes (sensor_inst); err_remove_callback:sensor_hub_remove_callback (hsdev, hsdev->usage);
@@ -947,9 +947,10 @@ static int hid_sensor_custom_remove(stru }hid_sensor_custom_dev_if_remove (sensor_inst); -hid_sensor_custom_remove_attributes (sensor_inst); +/* Remove enable_sensor first as it uses fields via power_state/report_state. */ sysfs_remove_group (&sensor_inst->pdev->dev.kobj, &enable_sensor_attr_group); +hid_sensor_custom_remove_attributes (sensor_inst);sensor_hub_remove_callback (hsdev, hsdev->usage);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 78/79 HID: hyperv: validate initial device info bounds
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 934b7778aa7b7c8f6bb073d2a73ba3674885bae0 upstream.
The Hyper-V synthetic HID host supplies SYNTH_HID_INITIAL_DEVICE_INFO messages that contain a HID descriptor followed by the report descriptor bytes. mousevsc_on_receive_device_info() trusts bLength and wDescriptorLength without checking that the received packet contains both byte ranges.
A malformed host or backend message can therefore make the guest read past the received VMBus packet while copying the report descriptor. Pass the received initial-device-info size into the parser and reject descriptor lengths that exceed the packet.
Impact: A malicious Hyper-V host or backend can crash a guest by sending a short initial device-info message with an oversized HID report descriptor length.
--- drivers/hid/hid-hyperv.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-)
drivers/hid/hid-hyperv.c +24 -3
@@ -179,18 +179,32 @@ static void mousevsc_free_device(struct }static void mousevsc_on_receive_device_info (struct mousevsc_dev *input_device, -struct synthhid_device_info *device_info) +struct synthhid_device_info *device_info, +u32 device_info_size) {int ret =0 ;struct hid_descriptor *desc;struct mousevsc_prt_msg ack; +size_t desc_offset; +size_t desc_size; input_device->dev_info_status = -ENOMEM ; +if (device_info_size <sizeof (*device_info)) { + input_device->dev_info_status = -EINVAL ; + goto cleanup; + } + input_device->hid_dev_info = device_info->hid_dev_info; desc = &device_info->hid_descriptor; + desc_offset = offsetof(struct synthhid_device_info , hid_descriptor); + desc_size = device_info_size - desc_offset;if (desc->bLength ==0 ) goto cleanup; +if (desc->bLength <sizeof (*desc) || desc->bLength > desc_size) { + input_device->dev_info_status = -EINVAL ; + goto cleanup; + }/* The pointer is not NULL when we resume from hibernation */ kfree (input_device->hid_desc);
@@ -205,6 +219,10 @@ static void mousevsc_on_receive_device_i input_device->dev_info_status = -EINVAL ; goto cleanup; } +if (input_device->report_desc_size > desc_size - desc->bLength) { + input_device->dev_info_status = -EINVAL ; + goto cleanup; + }/* The pointer is not NULL when we resume from hibernation */ kfree (input_device->report_desc);
@@ -285,14 +303,17 @@ static void mousevsc_on_receive(struct hbreak ;case SYNTH_HID_INITIAL_DEVICE_INFO : -WARN_ON (pipe_msg->size <sizeof (struct hv_input_dev_info )); +if (WARN_ON_ONCE (pipe_msg->size < +sizeof (struct synthhid_device_info ))) +break ;/* * Parse out the device info into device attr, * hid desc and report desc */ mousevsc_on_receive_device_info (input_dev, - (struct synthhid_device_info *)pipe_msg->data); + (struct synthhid_device_info *)pipe_msg->data, + pipe_msg->size);break ;case SYNTH_HID_INPUT_REPORT : input_report =
Greg Kroah-Hartman
maintainerPATCH 79/79 Bluetooth: hci_event: fix LE list UAF on reset
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengfeng Ye <[email protected]>
commit 33af47e847fe4a28b109673affb5874015d54f5a upstream.
hci_cc_reset() clears the LE accept and resolving lists without taking hdev->lock. Other command-complete handlers serialize updates to these lists with that lock, and the debugfs readers hold it while walking them.
This permits the reset completion and a debugfs read to interleave as follows:
hci_rx_work debugfs reader
----------- --------------
lock hdev->lock
fetch current entry
list_del(entry)
kfree(entry)
read entry fields
The reader then dereferences a freed list entry and may follow its stale next pointer.
KASAN reported:
BUG: KASAN: slab-use-after-free in white_list_show+0x15f/0x180 Read of size 1 at addr ffff8881015dab16 by task poc/95 Call Trace: white_list_show+0x15f/0x180 seq_read_iter+0x3ff/0x1190 seq_read+0x267/0x3d0 vfs_read+0x177/0xa20 ksys_read+0xf7/0x1c0 Allocated by task 91: hci_bdaddr_list_add+0x1a6/0x3a0 hci_cc_le_add_to_accept_list+0xab/0x140 hci_cmd_complete_evt+0x26c/0x9a0 hci_event_packet+0x454/0xb20 hci_rx_work+0x293/0x730 Freed by task 90: kfree+0x131/0x3c0 hci_bdaddr_list_clear+0xd8/0x160 hci_cc_reset+0x28a/0x370 hci_cmd_complete_evt+0x26c/0x9a0 hci_event_packet+0x454/0xb20 hci_rx_work+0x293/0x730
Take hdev->lock around both list clears. This matches the existing mutation and traversal locking convention.
--- net/bluetooth/hci_event.c | 2 ++ 1 file changed, 2 insertions(+)
net/bluetooth/hci_event.c +2 -0
@@ -297,8 +297,10 @@ static u8 hci_cc_reset(struct hci_dev *h hdev->ssp_debug_mode =0 ; +hci_dev_lock (hdev);hci_bdaddr_list_clear (&hdev->le_accept_list);hci_bdaddr_list_clear (&hdev->le_resolv_list); +hci_dev_unlock (hdev);return rp->status; }
Hi!
Quoted 4 lines
This is the start of the stable review cycle for the 6.1.185 release. There are 79 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.
This fails to build in one of configurations:
drivers/mmc/host/atmel-mci.c: In function 'atmci_remove':
4544
15:42:42drivers/mmc/host/atmel-mci.c:2631:31: error: 'struct atmel_mci' has no member named 'bh_work' 4545
15:42:42
2631 | cancel_work_sync(&host->bh_work);
4546
15:42:42
| ^~
4547
15:42:42
make[4]: *** [scripts/Makefile.build:250: drivers/mmc/host/atmel-mci.o] Error 1 4548
15:42:42
make[3]: *** [scripts/Makefile.build:503: drivers/mmc/host] Error 2
4549
15:42:42
make[3]: *** Waiting for unfinished jobs....
https://gitlab.com/cip-project/cip-testing/linux-stable-rc-ci/-/jobs/16096613950
https://gitlab.com/cip-project/cip-testing/linux-stable-rc-ci/-/pipelines/2789507911
Best regards,
Pavel
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.1.185 release. There are 79 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 Thu, 27 Aug 2026 13:25:02 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.1.185-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.1.y
and the diffstat can be found below.thanks,
greg k-h
On ARCH_BRCMSTB using 32-bit and 64-bit ARM kernels, build tested on BMIPS_GENERIC:
Signature · 1 line
Florian
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.1.185 release. There are 79 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 Thu, 27 Aug 2026 13:25:02 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 9 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.1.185-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.1.y
and the diffstat can be found below.thanks,
greg k-h
Compiled and booted on my test system. No dmesg regressions.
thanks,
-- Shuah
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.1.185 release. There are 79 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 Thu, 27 Aug 2026 13:25:02 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.1.185-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.1.y
and the diffstat can be found below.thanks,
greg k-h
Built and booted successfully on RISC-V RV64 (HiFive Unmatched).
# Librecast Test Results
020/020 [ OK ] liblcrq
010/010 [ OK ] libmld
120/120 [ OK ] liblibrecast
CPU/kernel: Linux auntie 6.1.185-rc1-02033-gdd3dc5dea566 #1 SMP PREEMPT_DYNAMIC Wed Aug 26 10:04:13 -00 2026 x86_64 AMD Ryzen 9 9950X 16-Core Processor AuthenticAMD GNU/Linux
Am 25.08.2026 um 15:25 schrieb Greg Kroah-Hartman:
Quoted 4 lines
This is the start of the stable review cycle for the 6.1.185 release. There are 79 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Builds, boots and works on my 2-socket Ivy Bridge Xeon E5-2697 v2 server. No dmesg oddities or regressions found.
Beste Grüße,
Peter Schneider
Signature · 8 lines
Climb the mountain not to plant your flag, but to embrace the challenge, enjoy the air and behold the view. Climb it so you can see the world, not so the world can see you. -- David McCullough Jr. OpenPGP: 0xA3828BD796CCE11A8CADE8866E3A92C92C3FF244 Download: https://www.peters-netzplatz.de/download/pschneider1968_pub.asc https://keys.mailvelope.com/pks/lookup?op=get&[email protected] https://keys.mailvelope.com/pks/lookup?op=get&[email protected]
Quoted 8 lines · On Tue, 25 Aug 2026 15:25:40 +0200 Greg Kroah-Hartman…
This is the start of the stable review cycle for the 6.1.185 release. There are 79 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 Thu, 27 Aug 2026 13:25:02 +0000. Anything received after that time might be too late.
Boot-tested under QEMU for Rust x86_64:
I don't have the Pavel's reported build error on my config.
Thanks!
Cheers,
Miguel
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.1.185 release. There are 79 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 Thu, 27 Aug 2026 13:25:02 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.1.185-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.1.y
and the diffstat can be found below.thanks,
greg k-h
Tested on an amd64 laptop (Lenovo ThinkPad T14 Gen 1). Working well, no regressions observed.
Signature · 1 line
-Barry K. Nathan <[email protected]>