6.18.37-rc1 review
Greg Kroah-Hartman
maintainercover 6.18.37-rc1 review
This is the start of the stable review cycle for the 6.18.37 release. There are 60 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 Sat, 27 Jun 2026 12:54:50 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.37-rc1.gz
or in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git linux-6.18.y
and the diffstat can be found below.
thanks,
greg k-h
-------------
Pseudo-Shortlog of commits:
Greg Kroah-Hartman <[email protected]>
Linux 6.18.37-rc1
Lorenzo Stoakes <[email protected]>
mm: do not copy page tables unnecessarily for VM_UFFD_WP
Miklos Szeredi <[email protected]>
virtiofs: fix UAF on submount umount
Ruslan Valiyev <[email protected]>
media: vidtv: fix NULL pointer dereference in vidtv_mux_push_si
Gil Portnoy <[email protected]>
ksmbd: reject non-VALID session in compound request branch
Georgi Djakov <[email protected]>
drivers/base/memory: set mem->altmap after successful device registration
Viken Dadhaniya <[email protected]>
serial: qcom_geni: Fix RX DMA stall when SE_DMA_RX_LEN_IN is zero
Yi Yang <[email protected]>
vc_screen: fix null-ptr-deref in vcs_notifier() during concurrent vcs_write
Giovanni Cabiddu <[email protected]>
crypto: qat - remove unused character device and IOCTLs
Dmitry Torokhov <[email protected]>
Input: rmi4 - fix bit count in bitmap_copy()
Dmitry Torokhov <[email protected]>
Input: rmi4 - iterative IRQ handler
Dmitry Torokhov <[email protected]>
Input: rmi4 - fix memory leak in rmi_set_attn_data()
Dmitry Torokhov <[email protected]>
Input: rmi4 - fix num_subpackets overflow in register descriptor
Dmitry Torokhov <[email protected]>
Input: rmi4 - fix type overflow in register counts
Dmitry Torokhov <[email protected]>
Input: rmi4 - refactor register descriptor parsing
Dmitry Torokhov <[email protected]>
Input: rmi4 - fix register descriptor address calculation
Sam Daly <[email protected]>
iio: adc: ti-ads1298: add bounds check to pga_settings index
Sam Daly <[email protected]>
iio: light: veml6075: add bounds check to veml6075_it_ms index
Faicker Mo <[email protected]>
net: net_failover: Fix the deadlock in slave register
Mike Marciniszyn (Meta) <[email protected]>
net: export netif_open for self_test usage
Lorenzo Stoakes <[email protected]>
testing/selftests/mm: add soft-dirty merge self-test
Lorenzo Stoakes <[email protected]>
mm: propagate VM_SOFTDIRTY on merge
Lorenzo Stoakes <[email protected]>
mm: set the VM_MAYBE_GUARD flag on guard region install
Lorenzo Stoakes <[email protected]>
mm: introduce copy-on-fork VMAs and make VM_MAYBE_GUARD one
Lorenzo Stoakes <[email protected]>
mm: implement sticky VMA flags
Lorenzo Stoakes <[email protected]>
mm: update vma_modify_flags() to handle residual flags, document
Lorenzo Stoakes <[email protected]>
mm: add atomic VMA flags and set VM_MAYBE_GUARD as such
Lorenzo Stoakes <[email protected]>
mm: introduce VM_MAYBE_GUARD and make visible in /proc/$pid/smaps
Xin Long <[email protected]>
sctp: disable BH before calling udp_tunnel_xmit_skb()
Tudor Ambarus <[email protected]>
firmware: samsung: acpm: Fix cross-thread RX length corruption
Dexuan Cui <[email protected]>
Drivers: hv: vmbus: Improve the logic of reserving fb_mmio on Gen2 VMs
Thorsten Blum <[email protected]>
hv: utils: handle and propagate errors in kvp_register
André Draszik <[email protected]>
regulator: core: fix locking in regulator_resolve_supply() error path
Bernard Pidoux <[email protected]>
rose: don't free fd-owned sockets when reaping in the heartbeat
Bernard Pidoux <[email protected]>
rose: clear neighbour pointer in rose_kill_by_device()
Bernard Pidoux <[email protected]>
rose: cancel neighbour timers in rose_neigh_put() before freeing
Bernard Pidoux <[email protected]>
rose: drop CALL_REQUEST in loopback timer when device is not running
Bernard Pidoux <[email protected]>
rose: release netdev ref and destroy orphaned incoming sockets
Bernard Pidoux <[email protected]>
rose: fix netdev double-hold in rose_make_new()
Bernard Pidoux <[email protected]>
rose: disconnect orphaned STATE_2 sockets when device is gone
Bernard Pidoux <[email protected]>
rose: set SOCK_DESTROY in rose_kill_by_device() for prompt cleanup
Bernard Pidoux <[email protected]>
rose: fix notifier unregistered too early in rose_exit()
Bernard Pidoux <[email protected]>
rose: fix netdev double-hold in rose_rx_call_request()
Bernard Pidoux <[email protected]>
rose: guard rose_neigh_put() against NULL in timer expiry
Bernard Pidoux <[email protected]>
rose: clear neighbour pointer after rose_neigh_put() in state machines
Bernard Pidoux <[email protected]>
rose: fix race between loopback timer and module removal
Bernard Pidoux <[email protected]>
rose: hold loopback neighbour reference across timer callback
Bernard Pidoux <[email protected]>
rose: fix dev_put() leak in rose_loopback_timer()
Yicong Yang <[email protected]>
ACPI: scan: Use async schedule function in acpi_scan_clear_dep_fn()
Mingyu Wang <[email protected]>
agp/amd64: Fix broken error propagation in agp_amd64_probe()
Weiming Shi <[email protected]>
net: qualcomm: rmnet: fix endpoint use-after-free in rmnet_dellink()
Weiming Shi <[email protected]>
i2c: stub: Reject I2C block transfers with invalid length
Lord Ulf Henrik Holmberg <[email protected]>
RDMA/bnxt_re: zero shared page before exposing to userspace
Waiman Long <[email protected]>
debugobjects: Dont call fill_pool() in early boot hardirq context
Helen Koike <[email protected]>
debugobjects: Do not fill_pool() if pi_blocked_on
Sebastian Andrzej Siewior <[email protected]>
debugobjects: Use LD_WAIT_CONFIG instead of LD_WAIT_SLEEP
Sebastian Andrzej Siewior <[email protected]>
debugobjects: Allow to refill the pool before SYSTEM_SCHEDULING
Yang Erkun <[email protected]>
Revert "NFSD: Defer sub-object cleanup in export put callbacks"
Joanne Koong <[email protected]>
fuse: re-lock request before replacing page cache folio
Russell King (Oracle) <[email protected]>
net: stmmac: fix stm32 (and potentially others) resume regression
Gabriel Krisman Bertazi <[email protected]>
io_uring/net: Avoid msghdr on op_connect/op_bind async data
-------------
Diffstat:
Documentation/filesystems/proc.rst | 5 +- Documentation/userspace-api/ioctl/ioctl-number.rst | 1 - Makefile | 4 +- drivers/acpi/scan.c | 41 +-- drivers/base/memory.c | 3 +- drivers/char/agp/amd64-agp.c | 2 +- drivers/crypto/intel/qat/qat_common/adf_cfg.c | 10 - drivers/crypto/intel/qat/qat_common/adf_cfg.h | 1 - .../crypto/intel/qat/qat_common/adf_cfg_common.h | 32 -- drivers/crypto/intel/qat/qat_common/adf_cfg_user.h | 38 -- .../crypto/intel/qat/qat_common/adf_common_drv.h | 3 - drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c | 404 +-------------------- drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c | 70 ---- drivers/firmware/samsung/exynos-acpm.c | 14 +- drivers/hv/hv_kvp.c | 25 +- drivers/hv/vmbus_drv.c | 29 +- drivers/i2c/i2c-stub.c | 5 + drivers/iio/adc/ti-ads1298.c | 7 +- drivers/iio/light/veml6075.c | 8 +- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 2 +- drivers/input/rmi4/rmi_driver.c | 171 +++++---- drivers/input/rmi4/rmi_driver.h | 4 +- drivers/input/rmi4/rmi_f12.c | 7 + drivers/media/test-drivers/vidtv/vidtv_mux.c | 8 +- drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 8 +- drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h | 1 + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 +- drivers/net/net_failover.c | 12 +- drivers/regulator/core.c | 10 +- drivers/tty/serial/qcom_geni_serial.c | 9 +- drivers/tty/vt/vc_screen.c | 2 +- fs/fuse/dev.c | 19 +- fs/fuse/file.c | 8 +- fs/nfsd/export.c | 67 +--- fs/nfsd/export.h | 7 +- fs/nfsd/nfsctl.c | 8 +- fs/proc/task_mmu.c | 1 + fs/smb/server/smb2pdu.c | 5 + include/linux/mm.h | 104 ++++++ include/net/rose.h | 12 + include/trace/events/mmflags.h | 1 + io_uring/net.c | 36 +- io_uring/opdef.c | 4 +- lib/debugobjects.c | 58 ++- mm/khugepaged.c | 71 ++-- mm/madvise.c | 24 +- mm/memory.c | 16 +- mm/mlock.c | 2 +- mm/mprotect.c | 2 +- mm/mseal.c | 7 +- mm/vma.c | 81 +++-- mm/vma.h | 130 +++++-- net/core/dev.c | 1 + net/core/failover.c | 6 +- net/rose/af_rose.c | 49 ++- net/rose/rose_in.c | 6 + net/rose/rose_loopback.c | 61 +++- net/rose/rose_timer.c | 87 ++++- net/sctp/ipv6.c | 2 + net/sctp/protocol.c | 2 + tools/testing/selftests/mm/soft-dirty.c | 127 ++++++- tools/testing/vma/vma.c | 3 +- tools/testing/vma/vma_internal.h | 49 +++ 63 files changed, 1023 insertions(+), 972 deletions(-)
Greg Kroah-Hartman
maintainerPATCH 1/60 io_uring/net: Avoid msghdr on op_connect/op_bind async data
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gabriel Krisman Bertazi <[email protected]>
[ Upstream commit 3979840cd858f30f43ea9f4e7f7f1f56de82d698 ] This fixes a memory leak due to the lack of the cleanup hook for the iovec. The stable backport differs from upstream by dropping the io_connect_bpf_populate hunk, which didn't exist at the time and by fixing the merge conflict due to the introduction of io_bind_file_create.
Both IORING_OP_CONNECT and IORING_OP_BIND reuse the msghdr object just to store the sockaddr. Beyond allocating a much larger object than needed, msghdr can also wrap an iovec, which will be recycled unnecessarily. This uses the sockaddr directly.
--- io_uring/net.c | 36 ++++++++++++++++++------------------ io_uring/opdef.c | 4 ++-- 2 files changed, 20 insertions(+), 20 deletions(-)
io_uring/net.c +18 -18
@@ -1771,7 +1771,7 @@ int io_socket(struct io_kiocb *req, unsigned int issue_flags)int io_connect_prep (struct io_kiocb *req,const struct io_uring_sqe *sqe) { struct io_connect *conn = io_kiocb_to_cmd(req, struct io_connect); -struct io_async_msghdr *io; +struct sockaddr_storage *addr;if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in)return -EINVAL ;
@@ -1780,17 +1780,17 @@ int io_connect_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) conn->addr_len =READ_ONCE (sqe->addr2); conn->in_progress = conn->seen_econnaborted = false; - io =io_msg_alloc_async (req); -if (unlikely (!io)) + addr =io_uring_alloc_async_data (NULL , req); +if (unlikely (!addr))return -ENOMEM ; -return move_addr_to_kernel (conn->addr, conn->addr_len, &io->addr); +return move_addr_to_kernel (conn->addr, conn->addr_len, addr); }int io_connect (struct io_kiocb *req,unsigned int issue_flags) { struct io_connect *connect = io_kiocb_to_cmd(req, struct io_connect); -struct io_async_msghdr *io = req->async_data; +struct sockaddr_storage *addr = req->async_data;unsigned file_flags;int ret;bool force_nonblock = issue_flags &IO_URING_F_NONBLOCK ;
@@ -1804,8 +1804,7 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags) file_flags = force_nonblock ?O_NONBLOCK :0 ; - ret =__sys_connect_file (req->file, &io->addr, connect->addr_len, - file_flags); + ret =__sys_connect_file (req->file, addr, connect->addr_len, file_flags);if ((ret == -EAGAIN || ret == -EINPROGRESS || ret == -ECONNABORTED ) && force_nonblock) {if (ret == -EINPROGRESS ) {
@@ -1834,7 +1833,6 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags) out:if (ret <0 )req_set_fail (req); -io_req_msg_cleanup (req, issue_flags);io_req_set_res (req, ret,0 );return IOU_COMPLETE ; }
@@ -1844,15 +1842,15 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)* which in turn end up in mnt_want_write() which will grab the fs * percpu start write sem. This can trigger a lockdep warning. */ -static int io_bind_file_create (const struct io_async_msghdr *io,int addr_len) +static int io_bind_file_create (const struct sockaddr_storage *addr,int addr_len) {const struct sockaddr_un *sun; -if (io->addr.ss_family !=AF_UNIX ) +if (addr->ss_family !=AF_UNIX )return 0 ;if (addr_len <= offsetof(struct sockaddr_un , sun_path))return 0 ; - sun = (const struct sockaddr_un *) &io->addr; + sun = (const struct sockaddr_un *) addr;return sun->sun_path[0 ] !='\0' ; }
@@ -1860,7 +1858,7 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind);struct sockaddr __user *uaddr; -struct io_async_msghdr *io; +struct sockaddr_storage *addr;int ret;if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in)
@@ -1869,21 +1867,23 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) uaddr =u64_to_user_ptr (READ_ONCE (sqe->addr)); bind->addr_len =READ_ONCE (sqe->addr2); - io =io_msg_alloc_async (req); -if (unlikely (!io)) + addr =io_uring_alloc_async_data (NULL , req); +if (unlikely (!addr))return -ENOMEM ; - ret =move_addr_to_kernel (uaddr, bind->addr_len, &io->addr); + + ret =move_addr_to_kernel (uaddr, bind->addr_len, addr);if (unlikely (ret))return ret; -if (io_bind_file_create (io, bind->addr_len)) +if (io_bind_file_create (addr, bind->addr_len)) req->flags |=REQ_F_FORCE_ASYNC ;return 0 ; } +int io_bind (struct io_kiocb *req,unsigned int issue_flags) { struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind); -struct io_async_msghdr *io = req->async_data; +struct sockaddr_storage *addr = req->async_data;struct socket *sock;int ret;
@@ -1891,7 +1891,7 @@ int io_bind(struct io_kiocb *req, unsigned int issue_flags)if (unlikely (!sock))return -ENOTSOCK ; - ret =__sys_bind_socket (sock, &io->addr, bind->addr_len); + ret =__sys_bind_socket (sock, addr, bind->addr_len);if (ret <0 )req_set_fail (req);io_req_set_res (req, ret,0 );
io_uring/opdef.c +2 -2
@@ -207,7 +207,7 @@ const struct io_issue_def io_issue_defs[] = { .unbound_nonreg_file = 1, .pollout = 1,#if defined(CONFIG_NET ) - .async_size = sizeof(struct io_async_msghdr), + .async_size = sizeof(struct sockaddr_storage), .prep = io_connect_prep, .issue = io_connect,#else
@@ -504,7 +504,7 @@ const struct io_issue_def io_issue_defs[] = { .needs_file = 1, .prep = io_bind_prep, .issue = io_bind, - .async_size = sizeof(struct io_async_msghdr), + .async_size = sizeof(struct sockaddr_storage), #else .prep = io_eopnotsupp_prep,#endif
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 2/60 net: stmmac: fix stm32 (and potentially others) resume regression
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Russell King (Oracle) <[email protected]>
[ Upstream commit dbbec8c5a79f4c7aa8d07da8c0b5a34d76c50699 ]
Marek reported that suspending stm32 causes the following errors when the interface is administratively down:
$ echo devices > /sys/power/pm_test $ echo mem > /sys/power/state ... ck_ker_eth2stp already disabled ... ck_ker_eth2stp already unprepared ...
On suspend, stm32 starts the eth2stp clock in its suspend method, and stops it in the resume method. This is because the blamed commit omits the call to the platform glue ->suspend() method, but does make the call to the platform glue ->resume() method.
This problem affects all other converted drivers as well - e.g. looking at the PCIe drivers, pci_save_state() will not be called, but pci_restore_state() will be. Similar issues affect all other drivers.
Fix this by always calling the ->suspend() method, even when the network interface is down. This fixes all the conversions to the platform glue ->suspend() and ->resume() methods.
--- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +2 -1
@@ -7760,7 +7760,7 @@ int stmmac_suspend(struct device *dev)u32 chan;if (!ndev || !netif_running (ndev)) -return 0 ; + goto suspend_bsp;mutex_lock (&priv->lock);
@@ -7803,6 +7803,7 @@ int stmmac_suspend(struct device *dev)if (stmmac_fpe_supported (priv))ethtool_mmsv_stop (&priv->fpe_cfg.mmsv); +suspend_bsp:if (priv->plat->suspend)return priv->plat->suspend (dev, priv->plat->bsp_priv);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 3/60 fuse: re-lock request before replacing page cache folio
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
commit a078484921052d0badd827fcc2770b5cfc1d4120 upstream.
fuse_try_move_folio() unlocks the request on entry but does not re-lock it on the success path. This means fuse_chan_abort() can end the request and free the fuse_io_args (eg fuse_readpages_end()) while the subsequent copy chain logic after fuse_try_move_folio() accesses the fuse_io_args, leading to use-after-free issues.
Fix this by calling lock_request() before replace_page_cache_folio(). This ensures the request is locked on the success path which will prevent the fuse_io_args from being freed while the later copying logic runs, and also ensures that the ap->folios[i]->mapping is never null since ap->folios[i] will always point to the newfolio after replace_page_cache_folio().
--- fs/fuse/dev.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-)
fs/fuse/dev.c +5 -14
@@ -1037,6 +1037,10 @@ static int fuse_try_move_folio(struct fuif (WARN_ON (folio_test_mlocked (oldfolio))) goto out_fallback_unlock; + err =lock_request (cs->req); +if (err) + goto out_fallback_unlock; +replace_page_cache_folio (oldfolio, newfolio);folio_get (newfolio);
@@ -1050,20 +1054,7 @@ static int fuse_try_move_folio(struct fu*/ pipe_buf_release (cs->pipe, buf); - err =0 ; -spin_lock (&cs->req->waitq.lock); -if (test_bit (FR_ABORTED , &cs->req->flags)) - err = -ENOENT ; -else - *foliop = newfolio; -spin_unlock (&cs->req->waitq.lock); - -if (err) { -folio_unlock (newfolio); -folio_put (newfolio); - goto out_put_old; - } - + *foliop = newfolio;folio_unlock (oldfolio);/* Drop ref for ap->pages[] array */ folio_put (oldfolio);
Greg Kroah-Hartman
maintainerPATCH 4/60 Revert "NFSD: Defer sub-object cleanup in export put callbacks"
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yang Erkun <[email protected]>
commit 516403d4d85607fdef3ca41d4a56b54e5566fa9a upstream.
This reverts commit 48db892356d6cb80f6942885545de4a6dd8d2a29.
Commit 48db892356d6 ("NFSD: Defer sub-object cleanup in export put callbacks") moved path_put() and auth_domain_put() out of svc_export_put() and expkey_put() and behind queue_rcu_work() to close a claimed use-after-free in e_show() and c_show() against ex_path and ex_client->name. Discussion in [1] shows neither the diagnosis nor the remedy survives review.
The downstream teardown of both sub-objects is already RCU-deferred. auth_domain_put() reaches svcauth_unix_domain_release(), which frees the unix_domain and its ->name through call_rcu(). path_put() reaches dentry_free(), which frees the dentry through call_rcu(), and prepend_path() is already structured to tolerate concurrent dentry teardown. A reader in cache_seq_start_rcu() therefore observes both sub-objects through the next grace period regardless of whether svc_export_put() runs synchronously, so the synchronous form was never unsafe.
The crash signature in the report cited by commit 48db892356d6 ("NFSD: Defer sub-object cleanup in export put callbacks") has a different root cause: a /proc/net/rpc cache file held open across network-namespace exit lets cache_destroy_net() free cd->hash_table while a reader is still walking it. The correct fix pins cd->net for the open fd's lifetime and does not require any deferral inside svc_export_put().
Meanwhile, deferring path_put() out of svc_export_put() reintroduces the regression that commit 69d803c40ede ("nfsd: Revert "nfsd:
release svc_expkey/svc_export with rcu_work"") repaired: after "exportfs -r" drops the last cache reference, the mount reference held through ex_path lingers in the workqueue, so a subsequent umount fails with EBUSY.
Restore the synchronous path_put() and auth_domain_put() in svc_export_put() and expkey_put() and the call_rcu()/kfree_rcu() free of the containing structures. The unrelated fix for ex_uuid/ex_stats from commit 2530766492ec ("nfsd: fix UAF when access ex_uuid or ex_stats") is preserved.
--- fs/nfsd/export.c | 63 +++++++------------------------------------------------ fs/nfsd/export.h | 7 +----- fs/nfsd/nfsctl.c | 8 ------ 3 files changed, 12 insertions(+), 66 deletions(-)
fs/nfsd/export.c +9 -54
@@ -36,30 +36,19 @@* second map contains a reference to the entry in the first map. */ -static struct workqueue_struct *nfsd_export_wq; -#define EXPKEY_HASHBITS 8#define EXPKEY_HASHMAX (1 << EXPKEY_HASHBITS)#define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) -static void expkey_release (struct work_struct *work) +static void expkey_put (struct kref *ref) { -struct svc_expkey *key =container_of (to_rcu_work (work), - struct svc_expkey, ek_rwork); + struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref);if (test_bit (CACHE_VALID , &key->h.flags) && !test_bit (CACHE_NEGATIVE , &key->h.flags))path_put (&key->ek_path);auth_domain_put (key->ek_client); -kfree (key); -} - -static void expkey_put (struct kref *ref) -{ - struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); - -INIT_RCU_WORK (&key->ek_rwork, expkey_release); -queue_rcu_work (nfsd_export_wq, &key->ek_rwork); +kfree_rcu (key, ek_rcu); }static int expkey_upcall (struct cache_detail *cd,struct cache_head *h)
@@ -364,13 +353,11 @@ static void export_stats_destroy(struct EXP_STATS_COUNTERS_NUM); } -static void svc_export_release (struct work_struct *work) +static void svc_export_release (struct rcu_head *rcu_head) { -struct svc_export *exp =container_of (to_rcu_work (work), - struct svc_export, ex_rwork); + struct svc_export *exp = container_of(rcu_head, struct svc_export, + ex_rcu); -path_put (&exp->ex_path); -auth_domain_put (exp->ex_client);nfsd4_fslocs_free (&exp->ex_fslocs);export_stats_destroy (exp->ex_stats);kfree (exp->ex_stats);
@@ -382,8 +369,9 @@ static void svc_export_put(struct kref * { struct svc_export *exp = container_of(ref, struct svc_export, h.ref); -INIT_RCU_WORK (&exp->ex_rwork, svc_export_release); -queue_rcu_work (nfsd_export_wq, &exp->ex_rwork); +path_put (&exp->ex_path); +auth_domain_put (exp->ex_client); +call_rcu (&exp->ex_rcu, svc_export_release); }static int svc_export_upcall (struct cache_detail *cd,struct cache_head *h)
@@ -1490,36 +1478,6 @@ const struct seq_operations nfs_exports_ .show = e_show, }; -/** -* nfsd_export_wq_init - allocate the export release workqueue -* -* Called once at module load. The workqueue runs deferred svc_export and -* svc_expkey release work scheduled by queue_rcu_work() in the cache put -* callbacks. -* -* Return values: -* %0: workqueue allocated -* %-ENOMEM: allocation failed -*/ -int nfsd_export_wq_init (void ) -{ - nfsd_export_wq =alloc_workqueue ("nfsd_export" ,WQ_UNBOUND ,0 ); -if (!nfsd_export_wq) -return -ENOMEM ; -return 0 ; -} - -/** -* nfsd_export_wq_shutdown - drain and free the export release workqueue -* -* Called once at module unload. Per-namespace teardown in -* nfsd_export_shutdown() has already drained all deferred work. -*/ -void nfsd_export_wq_shutdown (void ) -{ -destroy_workqueue (nfsd_export_wq); -} -/* * Initialize the exports module. */
@@ -1581,9 +1539,6 @@ nfsd_export_shutdown(struct net *net)cache_unregister_net (nn->svc_expkey_cache, net);cache_unregister_net (nn->svc_export_cache, net); -/* Drain deferred export and expkey release work. */ -rcu_barrier (); -flush_workqueue (nfsd_export_wq);cache_destroy_net (nn->svc_expkey_cache, net);cache_destroy_net (nn->svc_export_cache, net);svcauth_unix_purge (net);
fs/nfsd/export.h +2 -5
@@ -7,7 +7,6 @@#include <linux/sunrpc/cache.h> #include <linux/percpu_counter.h> -#include <linux/workqueue.h> #include <uapi/linux/nfsd/export.h> #include <linux/nfs4.h>
@@ -76,7 +75,7 @@ struct svc_export {u32 ex_layout_types;struct nfsd4_deviceid_map *ex_devid_map;struct cache_detail *cd; -struct rcu_work ex_rwork; +struct rcu_head ex_rcu;unsigned long ex_xprtsec_modes;struct export_stats *ex_stats; };
@@ -93,7 +92,7 @@ struct svc_expkey {u32 ek_fsid[6 ];struct path ek_path; -struct rcu_work ek_rwork; +struct rcu_head ek_rcu; };#define EX_ISSYNC (exp) (!((exp)->ex_flags & NFSEXP_ASYNC))
@@ -111,8 +110,6 @@ __be32 check_nfsd_access(struct svc_expo/* * Function declarations */ -int nfsd_export_wq_init (void ); -void nfsd_export_wq_shutdown (void );int nfsd_export_init (struct net *);void nfsd_export_shutdown (struct net *);void nfsd_export_flush (struct net *);
fs/nfsd/nfsctl.c +1 -7
@@ -2262,12 +2262,9 @@ static int __init init_nfsd(void)if (retval) goto out_free_pnfs;nfsd_lockd_init ();/* lockd->nfsd callbacks */ - retval =nfsd_export_wq_init (); -if (retval) - goto out_free_lockd; retval =register_pernet_subsys (&nfsd_net_ops);if (retval <0 ) - goto out_free_export_wq; + goto out_free_lockd; retval =register_cld_notifier ();if (retval) goto out_free_subsys;
@@ -2296,8 +2293,6 @@ out_free_cld:unregister_cld_notifier (); out_free_subsys:unregister_pernet_subsys (&nfsd_net_ops); -out_free_export_wq: -nfsd_export_wq_shutdown (); out_free_lockd:nfsd_lockd_shutdown ();nfsd_drc_slab_free ();
@@ -2318,7 +2313,6 @@ static void __exit exit_nfsd(void)nfsd4_destroy_laundry_wq ();unregister_cld_notifier ();unregister_pernet_subsys (&nfsd_net_ops); -nfsd_export_wq_shutdown ();nfsd_drc_slab_free ();nfsd_lockd_shutdown ();nfsd4_free_slabs ();
Greg Kroah-Hartman
maintainerPATCH 5/60 debugobjects: Allow to refill the pool before SYSTEM_SCHEDULING
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sebastian Andrzej Siewior <[email protected]>
commit 06e0ae988f6e3499785c407429953ade19c1096b upstream.
The pool of free objects is refilled on several occasions such as object initialisation. On PREEMPT_RT refilling is limited to preemptible sections due to sleeping locks used by the memory allocator. The system boots with disabled interrupts so the pool can not be refilled.
If too many objects are initialized and the pool gets empty then debugobjects disables itself.
Refiling can also happen early in the boot with disabled interrupts as long as the scheduler is not operational. If the scheduler can not preempt a task then a sleeping lock can not be contended.
Allow to additionally refill the pool if the scheduler is not operational.
--- lib/debugobjects.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
lib/debugobjects.c +1 -1
@@ -731,7 +731,7 @@ static void debug_objects_fill_pool(void)* raw_spinlock_t are basically the same type and this lock-type * inversion works just fine. */ -if (!IS_ENABLED (CONFIG_PREEMPT_RT ) ||preemptible ()) { +if (!IS_ENABLED (CONFIG_PREEMPT_RT ) ||preemptible () || system_state <SYSTEM_SCHEDULING ) {/* * Annotate away the spinlock_t inside raw_spinlock_t warning * by temporarily raising the wait-type to WAIT_SLEEP, matching
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 6/60 debugobjects: Use LD_WAIT_CONFIG instead of LD_WAIT_SLEEP
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sebastian Andrzej Siewior <[email protected]>
commit 37de2dbc318ee10577c1c2704de5a803e75e55a2 upstream.
fill_pool_map is used to suppress nesting violations caused by acquiring a spinlock_t (from within the memory allocator) while holding a raw_spinlock_t. The used annotation is wrong.
LD_WAIT_SLEEP is for always sleeping lock types such as mutex_t. LD_WAIT_CONFIG is for lock type which are sleeping while spinning on PREEMPT_RT such as spinlock_t.
Use LD_WAIT_CONFIG as override.
--- lib/debugobjects.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
lib/debugobjects.c +2 -2
@@ -734,10 +734,10 @@ static void debug_objects_fill_pool(void)if (!IS_ENABLED (CONFIG_PREEMPT_RT ) ||preemptible () || system_state <SYSTEM_SCHEDULING ) {/* * Annotate away the spinlock_t inside raw_spinlock_t warning -* by temporarily raising the wait-type to WAIT_SLEEP, matching +* by temporarily raising the wait-type to LD_WAIT_CONFIG, matching * the preemptible() condition above. */ - static DEFINE_WAIT_OVERRIDE_MAP(fill_pool_map, LD_WAIT_SLEEP); + static DEFINE_WAIT_OVERRIDE_MAP(fill_pool_map, LD_WAIT_CONFIG);lock_map_acquire_try (&fill_pool_map);fill_pool ();lock_map_release (&fill_pool_map);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 7/60 debugobjects: Do not fill_pool() if pi_blocked_on
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Helen Koike <[email protected]>
commit 5f41161059fd0f1bbf18c90f3180e38cc45a14eb upstream.
On RT enabled kernels, fill_pool() ends up calling rtlock_lock(), which asserts if current::pi_blocked_on is set, because a task can obviously only block on one lock as otherwise the priority inheritenace chain gets corrupted.
Prevent this by expanding the conditional to take current::pi_blocked_on into account.
--- lib/debugobjects.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
lib/debugobjects.c +14 -4
@@ -711,6 +711,15 @@ static struct debug_obj *lookup_object_or_alloc(void *addr, struct debug_bucketreturn NULL ; } +static inline bool debug_objects_is_pi_blocked_on (void ) +{ +#ifdef CONFIG_RT_MUTEXES +return current->pi_blocked_on !=NULL ; +#else +return false; +#endif +} +static void debug_objects_fill_pool (void ) {if (!static_branch_likely (&obj_cache_enabled))
@@ -727,11 +736,12 @@ static void debug_objects_fill_pool(void)/* * On RT enabled kernels the pool refill must happen in preemptible -* context -- for !RT kernels we rely on the fact that spinlock_t and -* raw_spinlock_t are basically the same type and this lock-type -* inversion works just fine. +* context and not enqueued on an rt_mutex -- for !RT kernels we rely +* on the fact that spinlock_t and raw_spinlock_t are basically the +* same type and this lock-type inversion works just fine. */ -if (!IS_ENABLED (CONFIG_PREEMPT_RT ) ||preemptible () || system_state <SYSTEM_SCHEDULING ) { +if (!IS_ENABLED (CONFIG_PREEMPT_RT ) || system_state <SYSTEM_SCHEDULING || + (preemptible () && !debug_objects_is_pi_blocked_on ())) {/* * Annotate away the spinlock_t inside raw_spinlock_t warning * by temporarily raising the wait-type to LD_WAIT_CONFIG, matching
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 8/60 debugobjects: Dont call fill_pool() in early boot hardirq context
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waiman Long <[email protected]>
commit 0d046ae106255cba5eb83b23f78ee93f3620247d upstream.
When booting a debug PREEMPT_RT kernel on an ARM64 system, a "inconsistent {HARDIRQ-ON-W} -> {IN-HARDIRQ-W} usage" lockdep warning message was reported to the console.
During early boot, interrupts are enabled before the scheduler is enabled. In this window (before SYSTEM_SCHEDULING is set) interrupts can fire and in the hard interrupt context handler attempt to fill the pool
This can lead to a deadlock when the interrupt occurred when the interrupt hits a region which holds a lock that is required to be taken in the allocation path.
Add a new can_fill_pool() helper and reorder the exception rule and forbid this scenario by excluding allocations from hard interrupt context.
--- lib/debugobjects.c | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-)
lib/debugobjects.c +37 -9
@@ -720,6 +720,41 @@ static inline bool debug_objects_is_pi_blocked_on(void)#endif } +static inline bool can_fill_pool (void ) +{ +/* +* On !RT enabled kernels there are no restrictions and spinlock_t and +* raw_spinlock_t are the same types. +*/ +if (!IS_ENABLED (CONFIG_PREEMPT_RT )) +return true; + +/* +* On RT enabled kernels, the task must not be blocked on a lock as +* that could corrupt the PI state when blocking on a lock in the +* allocation path. +*/ +if (debug_objects_is_pi_blocked_on ()) +return false; + +/* +* On RT enabled kernels the pool refill should happen in preemptible +* context. +*/ +if (preemptible ()) +return true; + +/* +* Though during system boot before scheduling is set up, preemption is +* disabled and the pool can get exhausted. Before scheduling is active +* a task cannot be blocked on a sleeping lock, but it might hold a lock +* and if interrupted then hard interrupt context might run into a lock +* inversion. So exclude hard interrupt context from allocations before +* scheduling is active. +*/ +return system_state <SYSTEM_SCHEDULING && !in_hardirq (); +} +static void debug_objects_fill_pool (void ) {if (!static_branch_likely (&obj_cache_enabled))
@@ -734,18 +769,11 @@ static void debug_objects_fill_pool(void)if (likely (!pool_should_refill (&pool_global)))return ; -/* -* On RT enabled kernels the pool refill must happen in preemptible -* context and not enqueued on an rt_mutex -- for !RT kernels we rely -* on the fact that spinlock_t and raw_spinlock_t are basically the -* same type and this lock-type inversion works just fine. -*/ -if (!IS_ENABLED (CONFIG_PREEMPT_RT ) || system_state <SYSTEM_SCHEDULING || - (preemptible () && !debug_objects_is_pi_blocked_on ())) { +if (can_fill_pool ()) {/* * Annotate away the spinlock_t inside raw_spinlock_t warning * by temporarily raising the wait-type to LD_WAIT_CONFIG, matching -* the preemptible() condition above. +* the preemptible() condition in can_fill_pool(). */ static DEFINE_WAIT_OVERRIDE_MAP(fill_pool_map, LD_WAIT_CONFIG);lock_map_acquire_try (&fill_pool_map);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 9/60 RDMA/bnxt_re: zero shared page before exposing to userspace
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lord Ulf Henrik Holmberg <[email protected]>
commit f6b079629becfa977f9c51fe53ad2e6dcc55ef44 upstream.
bnxt_re_alloc_ucontext() allocates uctx->shpg via __get_free_page(GFP_KERNEL). The buddy allocator does not zero pages without __GFP_ZERO, so the page contains stale kernel data from whatever object most recently freed it.
The page is then mapped into userspace via vm_insert_page() under BNXT_RE_MMAP_SH_PAGE in bnxt_re_mmap(). The driver only ever writes 4 bytes (a u32 AVID) at offset BNXT_RE_AVID_OFFT (0x10) inside bnxt_re_create_ah(); the remaining 4092 bytes of the page are exposed to userspace unsanitised, leaking kernel memory contents.
Any user with access to /dev/infiniband/uverbsX on a host with a bnxt_re device (typically rdma group membership) can read this data via a single mmap() at pgoff 0 after IB_USER_VERBS_CMD_GET_CONTEXT.
Other shared pages in the same file already use get_zeroed_page() correctly:
drivers/infiniband/hw/bnxt_re/ib_verbs.c
srq->uctx_srq_page = (void *)get_zeroed_page(GFP_KERNEL);
cq->uctx_cq_page = (void *)get_zeroed_page(GFP_KERNEL);uctx->shpg is the only outlier. Bring it in line with the existing convention by switching to get_zeroed_page().
--- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/infiniband/hw/bnxt_re/ib_verbs.c +1 -1
@@ -4350,7 +4350,7 @@ int bnxt_re_alloc_ucontext(struct ib_uco uctx->rdev = rdev; - uctx->shpg = (void *)__get_free_page (GFP_KERNEL ); + uctx->shpg = (void *)get_zeroed_page (GFP_KERNEL );if (!uctx->shpg) { rc = -ENOMEM ; goto fail;
Greg Kroah-Hartman
maintainerPATCH 10/60 i2c: stub: Reject I2C block transfers with invalid length
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
commit 6036b5067a8199ba7a2dc7b377d4b9dd276d5f9e upstream.
The I2C_SMBUS_I2C_BLOCK_DATA case in stub_xfer() uses data->block[0] as the transfer length. The existing check only clamps it to avoid overrunning the chip->words[256] register array, but does not validate it against I2C_SMBUS_BLOCK_MAX (32), which is the limit of the union i2c_smbus_data.block buffer (34 bytes total). The driver is a development/test tool (CONFIG_I2C_STUB=m, not built by default) that must be loaded with a chip_addr= parameter.
A local user with access to /dev/i2c-* can issue an I2C_SMBUS ioctl with I2C_SMBUS_I2C_BLOCK_DATA and data->block[0] > 32, causing stub_xfer() to read or write past the end of the union i2c_smbus_data.block buffer:
BUG: KASAN: stack-out-of-bounds in stub_xfer (drivers/i2c/i2c-stub.c:223) Read of size 1 at addr ffff88800abcfd92 by task exploit/81 Call Trace: <TASK> stub_xfer (drivers/i2c/i2c-stub.c:223) __i2c_smbus_xfer (drivers/i2c/i2c-core-smbus.c:593) i2c_smbus_xfer (drivers/i2c/i2c-core-smbus.c:536) i2cdev_ioctl_smbus (drivers/i2c/i2c-dev.c:391) i2cdev_ioctl (drivers/i2c/i2c-dev.c:478) __x64_sys_ioctl (fs/ioctl.c:583) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130) </TASK>
The bug exists because i2c-stub implements .smbus_xfer directly, bypassing the I2C_SMBUS_BLOCK_MAX validation in i2c_smbus_xfer_emulated(). The I2C_SMBUS_BLOCK_DATA case in the same function correctly validates against I2C_SMBUS_BLOCK_MAX, but the I2C_SMBUS_I2C_BLOCK_DATA case does not.
Fix by rejecting transfers with data->block[0] == 0 or data->block[0] > I2C_SMBUS_BLOCK_MAX with -EINVAL, consistent with both the I2C_SMBUS_BLOCK_DATA case in the same function and the I2C_SMBUS_I2C_BLOCK_DATA validation in i2c_smbus_xfer_emulated().
--- drivers/i2c/i2c-stub.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/i2c/i2c-stub.c +5 -0
@@ -214,6 +214,11 @@ static s32 stub_xfer(struct i2c_adapter* We ignore banks here, because banked chips don't use I2C * block transfers */ +if (data->block[0 ] ==0 || + data->block[0 ] >I2C_SMBUS_BLOCK_MAX ) { + ret = -EINVAL ; +break ; + }if (data->block[0 ] >256 - command)/* Avoid overrun */ data->block[0 ] =256 - command; len = data->block[0 ];
Greg Kroah-Hartman
maintainerPATCH 11/60 net: qualcomm: rmnet: fix endpoint use-after-free in rmnet_dellink()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
commit d00c953a8f69921f484b629801766da68f27f658 upstream.
rmnet_dellink() removes the endpoint from the hash table with hlist_del_init_rcu() and then immediately frees it with kfree(). However, RCU readers on the receive path (rmnet_rx_handler -> __rmnet_map_ingress_handler) may still hold a reference to the endpoint and dereference ep->egress_dev after the memory has been freed. The endpoint is a kmalloc-32 object, and the stale read at offset 8 corresponds to the egress_dev pointer.
BUG: unable to handle page fault for address: ffffffffde942eef Oops: 0002 [#1] SMP NOPTI CPU: 1 UID: 0 PID: 137 Comm: poc_write Not tainted 7.0.0+ #4 PREEMPTLAZY RIP: 0010:rmnet_vnd_rx_fixup (rmnet_vnd.c:27) Call Trace: <TASK> __rmnet_map_ingress_handler (rmnet_handlers.c:48 rmnet_handlers.c:101) rmnet_rx_handler (rmnet_handlers.c:129 rmnet_handlers.c:235) __netif_receive_skb_core.constprop.0 (net/core/dev.c:6096) __netif_receive_skb_one_core (net/core/dev.c:6208) netif_receive_skb (net/core/dev.c:6467) tun_get_user (drivers/net/tun.c:1955) tun_chr_write_iter (drivers/net/tun.c:2003) vfs_write (fs/read_write.c:688) ksys_write (fs/read_write.c:740) </TASK>
Add an rcu_head field to struct rmnet_endpoint and replace kfree() with kfree_rcu() so the endpoint memory remains valid through the RCU grace period. Also remove the rmnet_vnd_dellink() call and inline only the nr_rmnet_devs decrement, since rmnet_vnd_dellink() would set ep->egress_dev to NULL during the grace period, creating a data race with lockless readers.
--- drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 8 ++++---- drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-)
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +4 -4
@@ -213,8 +213,8 @@ static void rmnet_dellink(struct net_dev ep =rmnet_get_endpoint (real_port, mux_id);if (ep) {hlist_del_init_rcu (&ep->hlnode); -rmnet_vnd_dellink (mux_id, real_port, ep); -kfree (ep); + real_port->nr_rmnet_devs--; +kfree_rcu (ep, rcu); }netdev_upper_dev_unlink (real_dev, dev);
@@ -238,9 +238,9 @@ static void rmnet_force_unassociate_devihash_for_each_safe (port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) {unregister_netdevice_queue (ep->egress_dev, &list);netdev_upper_dev_unlink (real_dev, ep->egress_dev); -rmnet_vnd_dellink (ep->mux_id, port, ep);hlist_del_init_rcu (&ep->hlnode); -kfree (ep); + port->nr_rmnet_devs--; +kfree_rcu (ep, rcu); }rmnet_unregister_real_device (real_dev);unregister_netdevice_many (&list);
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h +1 -0
@@ -18,6 +18,7 @@ struct rmnet_endpoint {u8 mux_id;struct net_device *egress_dev;struct hlist_node hlnode; +struct rcu_head rcu; };struct rmnet_egress_agg_params {
Greg Kroah-Hartman
maintainerPATCH 12/60 agp/amd64: Fix broken error propagation in agp_amd64_probe()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mingyu Wang <[email protected]>
commit b08472db93b1ccff84a7adec5779d47f0e9d3a30 upstream.
A NULL pointer dereference was observed in the AMD64 AGP driver when running in a virtualized environment (e.g. qemu/kvm) without a physical AMD northbridge. The crash occurs in amd64_fetch_size() when attempting to dereference the pointer returned by node_to_amd_nb(0).
The root cause of this crash is broken error propagation in agp_amd64_probe(): When no AMD northbridges are found, cache_nbs() correctly returns -ENODEV. However, the probe function erroneously checks the return value against exactly -1, rather than < 0.
As a result, the hardware absence error is masked, allowing the driver to improperly proceed with initialization. It eventually calls agp_add_bridge(), which invokes amd64_fetch_size(). Since the hardware does not exist, node_to_amd_nb(0) returns NULL, leading to a General Protection Fault (GPF) when accessing its ->misc member.
Fix the issue by correcting the error check in agp_amd64_probe() to abort properly when cache_nbs() returns any negative error code. This prevents the driver from erroneously proceeding without hardware, thereby avoiding the subsequent NULL pointer dereference at its source.
--- drivers/char/agp/amd64-agp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/char/agp/amd64-agp.c +1 -1
@@ -546,7 +546,7 @@ static int agp_amd64_probe(struct pci_de/* Fill in the mode register */ pci_read_config_dword (pdev, bridge->capndx+PCI_AGP_STATUS , &bridge->mode); -if (cache_nbs (pdev, cap_ptr) ==-1 ) { +if (cache_nbs (pdev, cap_ptr) <0 ) {agp_put_bridge (bridge);return -ENODEV ; }
Greg Kroah-Hartman
maintainerPATCH 13/60 ACPI: scan: Use async schedule function in acpi_scan_clear_dep_fn()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yicong Yang <[email protected]>
[ Upstream commit 7cf28b3797a81b616bb7eb3e90cf131afc452919 ]
The device object rescan in acpi_scan_clear_dep_fn() is scheduled on a system workqueue which is not guaranteed to be finished before entering userspace. This may cause some key devices to be missing when userspace init task tries to find them. Two issues observed on RISCV platforms:
- Kernel panic due to userspace init cannot have an opened console.
The console device scanning is queued by acpi_scan_clear_dep_queue() and not finished by the time userspace init process running, thus by the time userspace init runs, no console is present.
- Entering rescue shell due to the lack of root devices (PCIe nvme in our case).
Same reason as above, the PCIe host bridge scanning is queued on a system workqueue and finished after init process runs.
The reason is because both devices (console, PCIe host bridge) depend on riscv-aplic irqchip to serve their interrupts (console's wired interrupt and PCI's INTx interrupts). In order to keep the dependency, these devices are scanned and created after initializing riscv-aplic. The riscv-aplic is initialized in device_initcall() and a device scan work is queued via acpi_scan_clear_dep_queue(), which is close to the time userspace init process is run. Since system_dfl_wq is used in acpi_scan_clear_dep_queue() with no synchronization, the issues will happen if userspace init runs before these devices are ready.
The solution is to wait for the queued work to complete before entering userspace init. One possible way would be to use a dedicated workqueue instead of system_dfl_wq, and explicitly flush it somewhere in the initcall stage before entering userspace. Another way is to use async_schedule_dev_nocall() for scanning these devices. It's designed for asynchronous initialization and will work in the same way as before because it's using a dedicated unbound workqueue as well, but the kernel init code calls async_synchronize_full() right before entering userspace init which will wait for the work to complete.
Compared to a dedicated workqueue, the second approach is simpler because the async schedule framework takes care of all of the details. The ACPI code only needs to focus on its job. A dedicated workqueue for this could also be redundant because some platforms don't need acpi_scan_clear_dep_queue() for their device scanning.
--- drivers/acpi/scan.c | 41 +++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 26 deletions(-)
drivers/acpi/scan.c +15 -26
@@ -5,6 +5,7 @@#define pr_fmt (fmt) "ACPI: " fmt +#include <linux/async.h> #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h>
@@ -2360,46 +2361,34 @@ static int acpi_dev_get_next_consumer_dev_cb(struct acpi_dep_data *dep, void *dareturn 0 ; } -struct acpi_scan_clear_dep_work { -struct work_struct work; -struct acpi_device *adev; -}; - -static void acpi_scan_clear_dep_fn (struct work_struct *work) +static void acpi_scan_clear_dep_fn (void *dev,async_cookie_t cookie) { -struct acpi_scan_clear_dep_work *cdw; - - cdw = container_of(work, struct acpi_scan_clear_dep_work, work); +struct acpi_device *adev =to_acpi_device (dev);acpi_scan_lock_acquire (); -acpi_bus_attach (cdw->adev, (void *)true); +acpi_bus_attach (adev, (void *)true);acpi_scan_lock_release (); -acpi_dev_put (cdw->adev); -kfree (cdw); +acpi_dev_put (adev); }static bool acpi_scan_clear_dep_queue (struct acpi_device *adev) { -struct acpi_scan_clear_dep_work *cdw; -if (adev->dep_unmet)return false; - cdw =kmalloc (sizeof (*cdw),GFP_KERNEL ); -if (!cdw) -return false; - - cdw->adev = adev; -INIT_WORK (&cdw->work, acpi_scan_clear_dep_fn);/* -* Since the work function may block on the lock until the entire -* initial enumeration of devices is complete, put it into the unbound -* workqueue. +* Async schedule the deferred acpi_scan_clear_dep_fn() since: +* - acpi_bus_attach() needs to hold acpi_scan_lock which cannot +* be acquired under acpi_dep_list_lock (held here) +* - the deferred work at boot stage is ensured to be finished +* before userspace init task by the async_synchronize_full() +* barrier +* +* Use _nocall variant since it'll return on failure instead of +* run the function synchronously. */ -queue_work (system_unbound_wq, &cdw->work); - -return true; +return async_schedule_dev_nocall (acpi_scan_clear_dep_fn, &adev->dev); }static void acpi_scan_delete_dep_data (struct acpi_dep_data *dep)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 14/60 rose: fix dev_put() leak in rose_loopback_timer()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit ff91adc54db2b62c7cdf063ff761eceb5adf2215 upstream.
rose_rx_call_request() always consumes or returns the skb but never releases the device reference obtained from rose_dev_get(). When rose_rx_call_request() succeeds (returns non-zero) dev_put() was never called, leaking one reference per loopback CALL_REQUEST.
Move dev_put() outside the conditional so it is called unconditionally after rose_rx_call_request() in all cases.
Also remove the dead check (!rose_loopback_neigh->dev && !rose_loopback_neigh->loopback) that immediately precedes it: the loopback neighbour always has loopback=1 so this condition can never be true.
--- net/rose/rose_loopback.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-)
net/rose/rose_loopback.c +2 -9
@@ -96,22 +96,15 @@ static void rose_loopback_timer(struct t }if (frametype ==ROSE_CALL_REQUEST ) { -if (!rose_loopback_neigh->dev && - !rose_loopback_neigh->loopback) { -kfree_skb (skb); -continue ; - } - dev =rose_dev_get (dest);if (!dev) {kfree_skb (skb);continue ; } -if (rose_rx_call_request (skb, dev, rose_loopback_neigh, lci_o) ==0 ) { -dev_put (dev); +if (rose_rx_call_request (skb, dev, rose_loopback_neigh, lci_o) ==0 )kfree_skb (skb); - } +dev_put (dev); }else {kfree_skb (skb); }
Greg Kroah-Hartman
maintainerPATCH 15/60 rose: hold loopback neighbour reference across timer callback
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit d270a7a5793af84555c40dd1eb80f1d497fdf53c upstream.
rose_loopback_timer() dereferences rose_loopback_neigh throughout its body but holds no reference on it. A concurrent rose_loopback_clear() followed by rose_add_loopback_neigh() could free and reallocate the neighbour while the timer body is running, causing a use-after-free.
Take a reference with rose_neigh_hold() at the start of the callback (bailing out if the pointer is already NULL) and release it with rose_neigh_put() at the single exit point. The neigh cannot be freed while the callback holds a reference.
--- net/rose/rose_loopback.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
net/rose/rose_loopback.c +10 -1
@@ -66,10 +66,15 @@ static void rose_loopback_timer(struct tunsigned int lci_i, lci_o;int count; +if (rose_loopback_neigh) +rose_neigh_hold (rose_loopback_neigh); +else +return ; +for (count =0 ; count <ROSE_LOOPBACK_LIMIT ; count++) { skb =skb_dequeue (&loopback_queue);if (!skb) -return ; + goto out;if (skb->len <ROSE_MIN_LEN ) {kfree_skb (skb);continue ;
@@ -109,6 +114,10 @@ static void rose_loopback_timer(struct tkfree_skb (skb); } } + +out: +rose_neigh_put (rose_loopback_neigh); +if (!skb_queue_empty (&loopback_queue))mod_timer (&loopback_timer, jiffies +1 ); }
Greg Kroah-Hartman
maintainerPATCH 16/60 rose: fix race between loopback timer and module removal
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit 47dd6ec1a77d77895afb00aa2e68373a48289108 upstream.
rose_loopback_clear() called timer_delete() which returns immediately without waiting for any running callback to complete. If the timer fired concurrently with module removal, rose_loopback_timer() could re-arm the timer after timer_delete() returned and then access rose_loopback_neigh after it was freed.
Two complementary changes close the race:
1. Add a loopback_stopping atomic flag. rose_loopback_timer() checks it at entry (before acquiring a reference) and again inside the loop; when set it drains the queue and exits without re-arming the timer.
2. Switch rose_loopback_clear() to timer_delete_sync() so it blocks until any in-flight callback has returned before freeing resources.
The smp_mb() between setting the flag and calling timer_delete_sync() ensures the flag is visible to any callback that is about to run.
--- net/rose/rose_loopback.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-)
net/rose/rose_loopback.c +24 -7
@@ -12,13 +12,15 @@#include <net/rose.h> #include <linux/init.h> -static struct sk_buff_head loopback_queue;#define ROSE_LOOPBACK_LIMIT 1000 -static struct timer_list loopback_timer; +static struct timer_list loopback_timer; +static struct sk_buff_head loopback_queue;static void rose_set_loopback_timer (void );static void rose_loopback_timer (struct timer_list *unused); +static atomic_t loopback_stopping =ATOMIC_INIT (0 ); +void rose_loopback_init (void ) {skb_queue_head_init (&loopback_queue);
@@ -66,6 +68,9 @@ static void rose_loopback_timer(struct tunsigned int lci_i, lci_o;int count; +if (atomic_read (&loopback_stopping)) +return ; +if (rose_loopback_neigh)rose_neigh_hold (rose_loopback_neigh);else
@@ -75,6 +80,13 @@ static void rose_loopback_timer(struct t skb =skb_dequeue (&loopback_queue);if (!skb) goto out; + +if (atomic_read (&loopback_stopping)) { +kfree_skb (skb); +skb_queue_purge (&loopback_queue); + goto out; + } +if (skb->len <ROSE_MIN_LEN ) {kfree_skb (skb);continue ;
@@ -118,7 +130,7 @@ static void rose_loopback_timer(struct t out:rose_neigh_put (rose_loopback_neigh); -if (!skb_queue_empty (&loopback_queue)) +if (!atomic_read (&loopback_stopping) && !skb_queue_empty (&loopback_queue))mod_timer (&loopback_timer, jiffies +1 ); }
@@ -126,10 +138,15 @@ void __exit rose_loopback_clear(void) {struct sk_buff *skb; -timer_delete (&loopback_timer); +atomic_set (&loopback_stopping,1 ); +/* Pairs with atomic_read() in rose_loopback_timer(): ensure the +* stopping flag is visible before we cancel, so a concurrent +* callback aborts its loop early rather than re-arming the timer. +*/ +smp_mb (); + +timer_delete_sync (&loopback_timer); -while ((skb =skb_dequeue (&loopback_queue)) !=NULL ) { - skb->sk =NULL ; +while ((skb =skb_dequeue (&loopback_queue)) !=NULL )kfree_skb (skb); - } }
Greg Kroah-Hartman
maintainerPATCH 17/60 rose: clear neighbour pointer after rose_neigh_put() in state machines
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit e8eb0c6faa8849ba7769516c1a8c84d9f612acf6 upstream.
After calling rose_neigh_put() in rose_state1_machine() through rose_state5_machine(), rose->neighbour was left pointing at the potentially freed neighbour structure. A subsequent timer expiry or concurrent teardown path could dereference the stale pointer, causing a use-after-free.
Set rose->neighbour to NULL immediately after each rose_neigh_put() call in the state machine functions.
--- net/rose/rose_in.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/rose/rose_in.c +6 -0
@@ -57,6 +57,7 @@ static int rose_state1_machine(struct sorose_write_internal (sk,ROSE_CLEAR_CONFIRMATION );rose_disconnect (sk,ECONNREFUSED , skb->data[3 ], skb->data[4 ]);rose_neigh_put (rose->neighbour); + rose->neighbour =NULL ;break ;default :
@@ -80,11 +81,13 @@ static int rose_state2_machine(struct sorose_write_internal (sk,ROSE_CLEAR_CONFIRMATION );rose_disconnect (sk,0 , skb->data[3 ], skb->data[4 ]);rose_neigh_put (rose->neighbour); + rose->neighbour =NULL ;break ;case ROSE_CLEAR_CONFIRMATION :rose_disconnect (sk,0 ,-1 ,-1 );rose_neigh_put (rose->neighbour); + rose->neighbour =NULL ;break ;default :
@@ -122,6 +125,7 @@ static int rose_state3_machine(struct sorose_write_internal (sk,ROSE_CLEAR_CONFIRMATION );rose_disconnect (sk,0 , skb->data[3 ], skb->data[4 ]);rose_neigh_put (rose->neighbour); + rose->neighbour =NULL ;break ;case ROSE_RR :
@@ -235,6 +239,7 @@ static int rose_state4_machine(struct sorose_write_internal (sk,ROSE_CLEAR_CONFIRMATION );rose_disconnect (sk,0 , skb->data[3 ], skb->data[4 ]);rose_neigh_put (rose->neighbour); + rose->neighbour =NULL ;break ;default :
@@ -255,6 +260,7 @@ static int rose_state5_machine(struct sorose_write_internal (sk,ROSE_CLEAR_CONFIRMATION );rose_disconnect (sk,0 , skb->data[3 ], skb->data[4 ]);rose_neigh_put (rose_sk (sk)->neighbour); +rose_sk (sk)->neighbour =NULL ; }return 0 ;
Greg Kroah-Hartman
maintainerPATCH 18/60 rose: guard rose_neigh_put() against NULL in timer expiry
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit 2b67342c6ff899a0b83359517146a5b7b243af97 upstream.
In rose_timer_expiry(), the ROSE_STATE_2 branch calls rose_neigh_put(rose->neighbour) without first checking whether the pointer is NULL. After commit 5de7665e0a07 ("net: rose: fix timer races against user threads") the timer is re-armed when the socket is owned by a user thread; between the re-arm and the next firing, a device-down event or concurrent teardown via rose_kill_by_device() can set rose->neighbour to NULL, leading to a NULL-pointer dereference inside rose_neigh_put().
Add a NULL check before the put and clear the pointer afterwards.
--- net/rose/rose_timer.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/rose/rose_timer.c +4 -1
@@ -180,7 +180,10 @@ static void rose_timer_expiry(struct timbreak ;case ROSE_STATE_2 :/* T3 */ -rose_neigh_put (rose->neighbour); +if (rose->neighbour) { +rose_neigh_put (rose->neighbour); + rose->neighbour =NULL ; + }rose_disconnect (sk,ETIMEDOUT ,-1 ,-1 );break ;
Greg Kroah-Hartman
maintainerPATCH 19/60 rose: fix netdev double-hold in rose_rx_call_request()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit c675277c3ba0d2310e0825577d58308c39931e14 upstream.
rose_rx_call_request() used netdev_tracker_alloc() after assigning make_rose->device, intending to take ownership of the reference passed by the caller. But every caller -- rose_route_frame() and rose_loopback_timer() -- already calls dev_put() for its own hold after the function returns, so the socket ended up with a tracker entry pointing at a reference that had already been released.
The result was spurious refcount_t warnings ("saturated", "decrement hit 0") on every incoming CALL_REQUEST, leading to refcount corruption and eventual silent freeze.
Replace netdev_tracker_alloc() with netdev_hold() so that rose_rx_call_request() acquires its own independent reference. Each caller retains its own hold from rose_dev_get() and releases it via dev_put() as before; socket cleanup releases the socket's separate hold via netdev_put().
--- net/rose/af_rose.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
net/rose/af_rose.c +5 -3
@@ -1078,9 +1078,11 @@ int rose_rx_call_request(struct sk_buff make_rose->source_digis[n] = facilities.source_digis[n]; make_rose->neighbour = neigh; make_rose->device = dev; -/* Caller got a reference for us. */ -netdev_tracker_alloc (make_rose->device, &make_rose->dev_tracker, -GFP_ATOMIC ); +/* Take an independent reference for this socket; callers keep their +* own reference (from rose_dev_get / dev_hold) and will release it +* themselves via dev_put(). +*/ +netdev_hold (make_rose->device, &make_rose->dev_tracker,GFP_ATOMIC ); make_rose->facilities = facilities;rose_neigh_hold (make_rose->neighbour);
Greg Kroah-Hartman
maintainerPATCH 20/60 rose: fix notifier unregistered too early in rose_exit()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit f71a8a1edc14dba746edde38adddd654ba202b4d upstream.
rose_exit() called unregister_netdevice_notifier() before the loop that calls unregister_netdev() on each ROSE virtual device. As a result, the NETDEV_DOWN event fired by unregister_netdev() was never delivered to rose_device_event(), so rose_kill_by_device() never ran.
Every socket whose rose->device pointed at a ROSE device therefore kept its netdev_tracker entry live until free_netdev() destroyed the ref_tracker_dir, at which point the kernel reported all of them as leaked references (165 entries in a typical FPAC setup). Worse, those sockets retained stale device pointers and live timers that could fire into freed module text after module unload, causing a silent system freeze with no kernel panic logged.
Fix by moving unregister_netdevice_notifier() to after the device- unregistration loop. unregister_netdev() then delivers NETDEV_DOWN while the notifier is still registered, rose_kill_by_device() runs for each device, releases all netdev references held by open sockets, and calls rose_disconnect() which stops the per-socket timers.
--- net/rose/af_rose.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
net/rose/af_rose.c +11 -2
@@ -1669,19 +1669,28 @@ static void __exit rose_exit(void)#ifdef CONFIG_SYSCTL rose_unregister_sysctl ();#endif -unregister_netdevice_notifier (&rose_dev_notifier); -sock_unregister (PF_ROSE );for (i =0 ; i < rose_ndevs; i++) {struct net_device *dev = dev_rose[i];if (dev) { +/* unregister_netdev() fires NETDEV_DOWN, which -- while the +* notifier is still registered below -- invokes +* rose_kill_by_device(dev). That releases every socket's +* netdev reference and disconnects all active circuits. +* Unregistering the notifier before this loop was the +* original bug: NETDEV_DOWN was never delivered, leaving +* 165 netdev_tracker entries leaked and stale timers live. +*/ unregister_netdev (dev);free_netdev (dev); } } +/* Now safe to remove the notifier -- all ROSE devices are gone. */ +unregister_netdevice_notifier (&rose_dev_notifier); +kfree (dev_rose);proto_unregister (&rose_proto); }
Greg Kroah-Hartman
maintainerPATCH 21/60 rose: set SOCK_DESTROY in rose_kill_by_device() for prompt cleanup
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit 741a4863ad570889c75f7a8e404567d8f3e46335 upstream.
When rose_kill_by_device() is called (via NETDEV_DOWN on module exit or interface removal), it calls rose_disconnect() which transitions sockets to ROSE_STATE_0 and sets SOCK_DEAD. However, rose_heartbeat_expiry() only calls rose_destroy_socket() at ROSE_STATE_0 if SOCK_DESTROY is set -- the SOCK_DEAD path is reserved for TCP_LISTEN sockets. Without SOCK_DESTROY, orphaned sockets in ROSE_STATE_2 (clearing) loop indefinitely in the heartbeat without ever being freed, keeping the module use-count elevated and blocking modprobe -r rose until the T1 timer (up to 200 s) expires.
Set SOCK_DESTROY immediately after rose_disconnect() so the heartbeat destroys the socket at its next tick (within 5 s), allowing clean module unload.
--- net/rose/af_rose.c | 5 +++++ 1 file changed, 5 insertions(+)
net/rose/af_rose.c +5 -0
@@ -211,6 +211,11 @@ start:spin_lock_bh (&rose_list_lock);if (rose->device == dev) {rose_disconnect (sk,ENETUNREACH ,ROSE_OUT_OF_ORDER ,0 ); +/* Mark for destruction so rose_heartbeat_expiry() +* cleans up the socket at its next tick rather than +* looping forever in ROSE_STATE_0 with no owner. +*/ +sock_set_flag (sk,SOCK_DESTROY );if (rose->neighbour)rose_neigh_put (rose->neighbour);netdev_put (rose->device, &rose->dev_tracker);
Greg Kroah-Hartman
maintainerPATCH 22/60 rose: disconnect orphaned STATE_2 sockets when device is gone
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit d4f4cf9f09a3f5fafa8f09110a7c1b5d10f2f261 upstream.
When ax25stop brings down ROSE interfaces, sockets in ROSE_STATE_2 (awaiting CLEAR CONFIRM) whose device pointer is already NULL are not reached by rose_kill_by_device() and wait for T3 (up to 180s) before self-cleaning via rose_timer_expiry(). This keeps the rose module usecount at 1, blocking rmmod for the full T3 duration.
In rose_heartbeat_expiry(), detect ROSE_STATE_2 sockets with no device, cancel T3, release the neighbour reference, and call rose_disconnect()
+ sock_set_flag(SOCK_DESTROY). The next heartbeat tick (<=5s) then destroys the socket via the existing ROSE_STATE_0/SOCK_DESTROY path, allowing clean module unload within 10s instead of up to 180s.
--- net/rose/rose_timer.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
net/rose/rose_timer.c +14 -0
@@ -139,6 +139,20 @@ static void rose_heartbeat_expiry(struct }break ; +case ROSE_STATE_2 : +/* Device gone before CLEAR CONFIRM arrived: stop waiting for T3 +* and disconnect now instead of blocking rmmod for up to 180s. */ +if (!rose->device) { +rose_stop_timer (sk); +if (rose->neighbour) { +rose_neigh_put (rose->neighbour); + rose->neighbour =NULL ; + } +rose_disconnect (sk,ENETDOWN ,-1 ,-1 ); +sock_set_flag (sk,SOCK_DESTROY ); + } +break ; +case ROSE_STATE_3 :/* * Check for the state of the receive buffer.
Greg Kroah-Hartman
maintainerPATCH 23/60 rose: fix netdev double-hold in rose_make_new()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit b9fb21ceb4f0d043767a1eba60786ec84809033b upstream.
rose_make_new() copies orose->device from the listener socket and calls netdev_hold(), storing the tracker in rose->dev_tracker. The only caller, rose_rx_call_request(), then overwrites both make_rose->device and make_rose->dev_tracker with a fresh netdev_hold() for the actual incoming-call device.
This orphans the tracker allocated by rose_make_new(): it remains in the device's refcount_tracker list but no pointer exists to free it via netdev_put(). The result is one spurious outstanding reference per accepted CALL_REQUEST, visible at rmmod time as:
ref_tracker: netdev@X has 2/2 users at
rose_rx_call_request+0xba3/0x1d50 [rose]
rose_loopback_timer+0x3eb/0x670 [rose]The second entry is the orphaned tracker from rose_make_new(); the first is the correctly-managed socket reference from rose_rx_call_request().
Fix: initialise rose->device to NULL in rose_make_new() and let rose_rx_call_request() -- the sole caller -- assign the correct device and take the sole netdev_hold() as it already does.
--- net/rose/af_rose.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
net/rose/af_rose.c +1 -3
@@ -631,9 +631,7 @@ static struct sock *rose_make_new(struct rose->hb = orose->hb; rose->idle = orose->idle; rose->defer = orose->defer; - rose->device = orose->device; -if (rose->device) -netdev_hold (rose->device, &rose->dev_tracker,GFP_ATOMIC ); + rose->device =NULL ;/* rose_rx_call_request() sets this */ rose->qbitincl = orose->qbitincl;return sk;
Greg Kroah-Hartman
maintainerPATCH 24/60 rose: release netdev ref and destroy orphaned incoming sockets
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit df12be096302d2c947388acc25764456c7f18cc1 upstream.
Two related cleanup gaps left the module unremovable after a loopback session:
1. rose_destroy_socket() did not release the device reference. When an unaccepted incoming socket (created by rose_rx_call_request()) is destroyed via rose_heartbeat_expiry(), it is removed from rose_list before rose_kill_by_device() can find it, so the netdev_hold() taken in rose_rx_call_request() was never matched by netdev_put(). Add the release at the top of rose_destroy_socket() guarded by a NULL check so that rose_release() and rose_kill_by_device(), which already call netdev_put() and set device = NULL, are not affected.
2. rose_heartbeat_expiry() STATE_0 cleanup required TCP_LISTEN in addition to SOCK_DEAD. Unaccepted incoming sockets are TCP_ESTABLISHED, so the condition was never true and those sockets lingered forever, holding the module use count above zero and blocking rmmod. Drop the TCP_LISTEN restriction: any STATE_0 + SOCK_DEAD socket is orphaned and should be destroyed.
Together with the earlier rose_make_new() double-hold fix these three patches allow clean rmmod after loopback sessions.
--- net/rose/af_rose.c | 9 +++++++++ net/rose/rose_timer.c | 9 +++++---- 2 files changed, 14 insertions(+), 4 deletions(-)
net/rose/af_rose.c +9 -0
@@ -363,6 +363,7 @@ static void rose_destroy_timer(struct ti*/ void rose_destroy_socket (struct sock *sk) { +struct rose_sock *rose =rose_sk (sk);struct sk_buff *skb;rose_remove_socket (sk);
@@ -370,6 +371,14 @@ void rose_destroy_socket(struct sock *skrose_stop_idletimer (sk);rose_stop_timer (sk); +/* Drop any device reference not already released by rose_kill_by_device() +* or rose_release() -- e.g. incoming sockets that were never accepted. +*/ +if (rose->device) { +netdev_put (rose->device, &rose->dev_tracker); + rose->device =NULL ; + } +rose_clear_queues (sk);/* Flush the queues */ while ((skb =skb_dequeue (&sk->sk_receive_queue)) !=NULL ) {
net/rose/rose_timer.c +5 -4
@@ -128,10 +128,11 @@ static void rose_heartbeat_expiry(struct }switch (rose->state) {case ROSE_STATE_0 : -/* Magic here: If we listen() and a new link dies before it -is accepted() it isn't 'dead' so doesn't get removed. */ -if (sock_flag (sk,SOCK_DESTROY ) || - (sk->sk_state ==TCP_LISTEN &&sock_flag (sk,SOCK_DEAD ))) { +/* Destroy any orphaned STATE_0 socket: either explicitly +* flagged SOCK_DESTROY, or SOCK_DEAD (covers both unaccepted +* incoming connections and listening sockets whose link died). +*/ +if (sock_flag (sk,SOCK_DESTROY ) ||sock_flag (sk,SOCK_DEAD )) {bh_unlock_sock (sk);rose_destroy_socket (sk);sock_put (sk);
Greg Kroah-Hartman
maintainerPATCH 25/60 rose: drop CALL_REQUEST in loopback timer when device is not running
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit cf5567a2652e44866eae8987dff4c1ea507680df upstream.
When ax25stop brings down rose0 while the loopback timer has pending CALL_REQUEST frames, rose_loopback_timer() calls rose_dev_get() and finds the device still registered (unregister_netdevice waits for refs to drop), then calls rose_rx_call_request() which takes a netdev_hold() for the new socket.
But NETDEV_DOWN fires only once: rose_kill_by_device() already ran before this timer tick, so the new socket is never cleaned up. The stuck reference prevents unregister_netdevice from completing, and the orphan socket's timers eventually fire on freed memory (KASAN slab-use-after-free in __run_timers).
The kernel clears IFF_UP via dev_close() before sending NETDEV_DOWN, so checking netif_running() after rose_dev_get() is sufficient: if the device is no longer running, the CALL_REQUEST is silently dropped and no socket is created. This closes the race without touching the module-exit path (which already stops the timer via loopback_stopping).
Tested: unregister_netdevice completes immediately after ax25stop with active loopback connections; no ref_tracker warnings, no KASAN.
--- net/rose/rose_loopback.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
net/rose/rose_loopback.c +10 -0
@@ -118,6 +118,16 @@ static void rose_loopback_timer(struct tkfree_skb (skb);continue ; } +/* rose_kill_by_device() runs on NETDEV_DOWN (IFF_UP cleared) +* before the device is unregistered. If we create a new +* socket here after that cleanup, the ref never gets released +* because NETDEV_DOWN fires only once. Drop the call instead. +*/ +if (!netif_running (dev)) { +dev_put (dev); +kfree_skb (skb); +continue ; + }if (rose_rx_call_request (skb, dev, rose_loopback_neigh, lci_o) ==0 )kfree_skb (skb);
Greg Kroah-Hartman
maintainerPATCH 26/60 rose: cancel neighbour timers in rose_neigh_put() before freeing
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit 9b222cb1d23ff210975e9df5ebab7b011acb6fad upstream.
rose_neigh_put() kfree()s the neighbour but never cancels its ftimer and t0timer. Until now every caller that dropped the final reference first called rose_remove_neigh(), which deletes those timers. The socket heartbeat reaping path drops the last reference directly, so a neighbour could be freed with t0timer still armed -- it re-arms itself in rose_t0timer_expiry() -- leading to a use-after-free write in enqueue_timer().
Cancel both timers with timer_delete_sync() (the synchronous variant, to wait out a concurrently running, self-rearming handler) in the refcount-zero branch of rose_neigh_put().
--- include/net/rose.h | 12 ++++++++++++ 1 file changed, 12 insertions(+)
include/net/rose.h +12 -0
@@ -160,6 +160,18 @@ static inline void rose_neigh_hold(strucstatic inline void rose_neigh_put (struct rose_neigh *rose_neigh) {if (refcount_dec_and_test (&rose_neigh->use)) { +/* We are dropping the last reference, so we are about to free the +* neighbour. Its timers may still be armed -- t0timer in particular +* re-arms itself in rose_t0timer_expiry(). rose_remove_neigh() +* cancels them before its own put, but callers that drop the final +* reference without first calling rose_remove_neigh() (the socket +* heartbeat reaping path) would otherwise kfree() a neighbour with a +* live timer -> use-after-free. timer_delete_sync() (not the async +* variant) is required: it waits out a concurrently running handler +* and loops until the self-rearming timer stays stopped. +*/ +timer_delete_sync (&rose_neigh->ftimer); +timer_delete_sync (&rose_neigh->t0timer);if (rose_neigh->ax25)ax25_cb_put (rose_neigh->ax25);kfree (rose_neigh->digipeat);
Greg Kroah-Hartman
maintainerPATCH 27/60 rose: clear neighbour pointer in rose_kill_by_device()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit 606e42d195b467480d4d405f8814c48d1651a76a upstream.
rose_kill_by_device() drops the neighbour reference but leaves rose->neighbour pointing at it, unlike every other rose_neigh_put() site (see "rose: clear neighbour pointer after rose_neigh_put() in state machines"). The heartbeat STATE_0 reaping path then puts the same neighbour a second time, causing a rose_neigh refcount underflow and a use-after-free.
Set rose->neighbour = NULL after the put, restoring the invariant.
--- net/rose/af_rose.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
net/rose/af_rose.c +9 -1
@@ -216,8 +216,16 @@ start:* looping forever in ROSE_STATE_0 with no owner. */ sock_set_flag (sk,SOCK_DESTROY ); -if (rose->neighbour) +if (rose->neighbour) {rose_neigh_put (rose->neighbour); +/* Clear the pointer after dropping the reference, as +* every other rose_neigh_put() site does. Otherwise +* rose_heartbeat_expiry() (STATE_0 reaping) sees a stale +* rose->neighbour and puts it a second time -> rose_neigh +* refcount underflow / use-after-free. +*/ + rose->neighbour =NULL ; + }netdev_put (rose->device, &rose->dev_tracker); rose->device =NULL ; }
Greg Kroah-Hartman
maintainerPATCH 28/60 rose: dont free fd-owned sockets when reaping in the heartbeat
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Pidoux <[email protected]>
commit 56576518920edd7b6c3479477d8d490fe2ebdaaa upstream.
The heartbeat reaps orphaned ROSE sockets after their bound device goes down. A socket still attached to a struct socket (sk->sk_socket != NULL -- e.g. an incoming connection an fpad client has accepted and kept open) is owned by that userspace fd: rose_release() frees it on close(). Freeing it from the heartbeat left the fd dangling, so the eventual close() touched freed memory -- slab-use-after-free in rose_release().
Reap only sockets with sk->sk_socket == NULL (unaccepted incoming connections and post-close orphans). For an fd-owned socket whose device went down, disconnect it and fall through to the switch so close() does the teardown. Also release the neighbour reference held by orphaned incoming sockets before tearing them down.
--- net/rose/rose_timer.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-)
net/rose/rose_timer.c +56 -1
@@ -126,13 +126,68 @@ static void rose_heartbeat_expiry(structsk_reset_timer (sk, &sk->sk_timer, jiffies +HZ /20 ); goto out; } + +/* The bound device went down while we still hold a reference on it. +* This catches the narrow race where rose_loopback_timer() created a +* socket in the window after rose_kill_by_device()'s NETDEV_DOWN sweep +* but before rose_insert_socket() -- leaving a STATE_3 socket that no +* other branch reaps. A down device means the link is dead, so tear +* the socket down regardless of state. rose_destroy_socket() releases +* the held netdev reference (rose->device still set). +*/ +if (rose->device && !netif_running (rose->device)) { +if (rose->neighbour) { +rose_neigh_put (rose->neighbour); + rose->neighbour =NULL ; + } +rose_disconnect (sk,ENETDOWN ,-1 ,-1 ); + +/* Only reap the socket if userspace no longer holds it. A socket +* still attached to a struct socket (sk->sk_socket != NULL -- e.g. +* a connection an fpad client has accepted and kept open) is owned +* by that fd: rose_release() will destroy it on close(). Dropping +* the last reference here leaves the open fd dangling, so the +* eventual close() touches freed memory -> slab-use-after-free in +* rose_release(). Unaccepted incoming sockets and post-close +* orphans have sk->sk_socket == NULL and stay safe to reap here. +*/ +if (!sk->sk_socket) { +sock_set_flag (sk,SOCK_DESTROY ); +bh_unlock_sock (sk); +rose_destroy_socket (sk); +sock_put (sk); +return ; + } + +/* Owned by userspace: the link is down and the socket is now +* disconnected (rose_disconnect() moved it to STATE_0). Fall +* through to the switch, which re-arms the heartbeat; the close() +* will tear the socket down. */ + } +switch (rose->state) {case ROSE_STATE_0 :/* Destroy any orphaned STATE_0 socket: either explicitly * flagged SOCK_DESTROY, or SOCK_DEAD (covers both unaccepted * incoming connections and listening sockets whose link died). */ -if (sock_flag (sk,SOCK_DESTROY ) ||sock_flag (sk,SOCK_DEAD )) { +if ((sock_flag (sk,SOCK_DESTROY ) ||sock_flag (sk,SOCK_DEAD )) && + !sk->sk_socket) { +/* Reap only orphaned sockets (sk->sk_socket == NULL). A +* socket still owned by a userspace fd reaches here via the +* STATE_2 device-gone branch, which sets SOCK_DESTROY without +* knowing about the fd; freeing it would race rose_release() +* at close() -> use-after-free. Leave it for close(). +* +* Orphaned incoming sockets (rose_rx_call_request) hold a +* neighbour reference; release it before teardown, as the +* STATE_2 and device-down branches do. rose_destroy_socket() +* does not drop it. +*/ +if (rose->neighbour) { +rose_neigh_put (rose->neighbour); + rose->neighbour =NULL ; + }bh_unlock_sock (sk);rose_destroy_socket (sk);sock_put (sk);
Greg Kroah-Hartman
maintainerPATCH 29/60 regulator: core: fix locking in regulator_resolve_supply() error path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: André Draszik <[email protected]>
commit 497330b203d2c59c5ff3fa4c34d14494d7203bc3 upstream.
If late enabling of a supply regulator fails in regulator_resolve_supply(), the code currently triggers a lockdep warning:
WARNING: drivers/regulator/core.c:2649 at _regulator_put+0x80/0xa0, CPU#6: kworker/u32:4/596
...
Call trace:
_regulator_put+0x80/0xa0 (P)
regulator_resolve_supply+0x7cc/0xbe0
regulator_register_resolve_supply+0x28/0xb8as the regulator_list_mutex must be held when calling _regulator_put().
To solve this, simply switch to using regulator_put().
While at it, we should also make sure that no concurrent access happens to our rdev while we clear out the supply pointer. Add appropriate locking to ensure that.
While the code in question will be removed altogether in a follow-up commit, I believe it is still beneficial to have this corrected before removal for future reference.
--- drivers/regulator/core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/regulator/core.c +9 -1
@@ -2159,8 +2159,16 @@ static int regulator_resolve_supply(struif (rdev->use_count) { ret =regulator_enable (rdev->supply);if (ret <0 ) { -_regulator_put (rdev->supply); +struct regulator *supply; + +regulator_lock_two (rdev, rdev->supply->rdev, &ww_ctx); + + supply = rdev->supply; rdev->supply =NULL ; + +regulator_unlock_two (rdev, supply->rdev, &ww_ctx); + +regulator_put (supply); goto out; } }
Greg Kroah-Hartman
maintainerPATCH 30/60 hv: utils: handle and propagate errors in kvp_register
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit 3fcf923302a8f5c0dc3af3d2ca2657cb5fae4297 ]
Make kvp_register() return an error code instead of silently ignoring failures, and propagate the error from kvp_handle_handshake() instead of returning success.
This propagates both kzalloc_obj() and hvutil_transport_send() failures to kvp_handle_handshake() and thus to kvp_on_msg().
--- drivers/hv/hv_kvp.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-)
drivers/hv/hv_kvp.c +14 -13
@@ -93,7 +93,7 @@ static void kvp_send_key(struct work_strstatic void kvp_respond_to_host (struct hv_kvp_msg *msg,int error);static void kvp_timeout_func (struct work_struct *dummy);static void kvp_host_handshake_func (struct work_struct *dummy); -static void kvp_register (int ); +static int kvp_register (int ); static DECLARE_DELAYED_WORK(kvp_timeout_work, kvp_timeout_func); static DECLARE_DELAYED_WORK(kvp_host_handshake_work, kvp_host_handshake_func);
@@ -127,24 +127,26 @@ static void kvp_register_done(void)hv_poll_channel (kvp_transaction.recv_channel, kvp_poll_wrapper); } -static void +static int kvp_register (int reg_value) {struct hv_kvp_msg *kvp_msg;char *version; +int ret; kvp_msg =kzalloc (sizeof (*kvp_msg),GFP_KERNEL ); +if (!kvp_msg) +return -ENOMEM ; -if (kvp_msg) { - version = kvp_msg->body.kvp_register.version; - kvp_msg->kvp_hdr.operation = reg_value; -strcpy (version,HV_DRV_VERSION ); - -hvutil_transport_send (hvt, kvp_msg,sizeof (*kvp_msg), - kvp_register_done); -kfree (kvp_msg); - } + version = kvp_msg->body.kvp_register.version; + kvp_msg->kvp_hdr.operation = reg_value; +strcpy (version,HV_DRV_VERSION ); + + ret =hvutil_transport_send (hvt, kvp_msg,sizeof (*kvp_msg), + kvp_register_done); +kfree (kvp_msg); +return ret; }static void kvp_timeout_func (struct work_struct *dummy)
@@ -186,9 +188,8 @@ static int kvp_handle_handshake(struct h*/ pr_debug ("KVP: userspace daemon ver. %d connected\n" , msg->kvp_hdr.operation); -kvp_register (dm_reg_value); -return 0 ; +return kvp_register (dm_reg_value); }
Greg Kroah-Hartman
maintainerPATCH 31/60 Drivers: hv: vmbus: Improve the logic of reserving fb_mmio on Gen2 VMs
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dexuan Cui <[email protected]>
[ Upstream commit 016a25e4b0df4d77e7c258edee4aaf982e4ee809 ]
If vmbus_reserve_fb() in the kdump/kexec kernel fails to properly reserve the framebuffer MMIO range (which is below 4GB) due to a Gen2 VM's screen.lfb_base being zero [1], there is an MMIO conflict between the drivers hyperv-drm and pci-hyperv: when the driver pci-hyperv's hv_allocate_config_window() calls vmbus_allocate_mmio() to get an MMIO range, typically it gets a 32-bit MMIO range that overlaps with the framebuffer MMIO range, and later hv_pci_enter_d0() fails with an error message "PCI Pass-through VSP failed D0 Entry with status" since the host thinks that PCI devices must not use MMIO space that the host has assigned to the framebuffer.
This is especially an issue if pci-hyperv is built-in and hyperv-drm is built as a module. Consequently, the kdump/kexec kernel fails to detect PCI devices via pci-hyperv, and may fail to mount the root file system, which may reside in a NVMe disk. The issue described here has existed for SR-IOV VF NICs since day one of the pci-hyperv driver, and has been worked around on x64 when possible. With the recent introduction of ARM64 VMs that boot from NVMe, there is no workaround, so we need a formal fix.
On Gen2 VMs, if the screen.lfb_base is 0 in the kdump/kexec kernel [1], fall back to the low MMIO base, which should be equal to the framebuffer MMIO base [2] (the statement is true according to my testing on x64 Windows Server 2016, and on x64 and ARM64 Windows Server 2025 and on Azure. I checked with the Hyper-V team and they said the statement should continue to be true for Gen2 VMs). In the first kernel, screen.lfb_base is not 0; if the user specifies a very high resolution, it's not enough to only reserve 8MB: let's always reserve half of the space below 4GB, but cap the reservation to 128MB, which is the required framebuffer size of the highest resolution 7680*4320 supported by Hyper-V.
While at it, fix the comparison "end > VTPM_BASE_ADDRESS" by changing the > to >=. Here the 'end' is an inclusive end (typically, it's 0xFFFF_FFFF for the low MMIO range).
Note: vmbus_reserve_fb() now also reserves an MMIO range at the beginning of the low MMIO range on CVMs, which have no framebuffers (the 'screen.lfb_base' in vmbus_reserve_fb() is 0 for CVMs), just in case the host might treat the beginning of the low MMIO range specially [3]. BTW, the OpenHCL kernel is not affected by the change, because that kernel boots with DeviceTree rather than ACPI (so vmbus_reserve_fb() won't run there), and there is no framebuffer device for that kernel.
Note: normally Gen1 VMs don't have the MMIO conflict issue because the framebuffer MMIO range (which is hardcoded to base=4GB-128MB and size=64MB for Gen1 VMs by the host) is always reported via the legacy PCI graphics device's BAR, so the kdump/kexec kernel can reserve the 64MB MMIO range; however, if the VM is configured to use a very high resolution and the required framebuffer size exceeds 64MB (AFAIK, in practice, this isn't a typical configuration by users), the hyperv-drm driver may need to allocate an MMIO range above 4GB and change the framebuffer MMIO location to the allocated MMIO range -- in this case, there can still be issues [4] which can't be easily fixed: any possible affected Gen1 users would have to use a resolution whose framebuffer size is <= 64MB, or switch to Gen2 VMs.
[1] https://lore.kernel.org/all/SA1PR21MB692176C1BC53BFC9EAE5CF8EBF51A@SA1PR21MB6921.namprd21.prod.outlook.com/
[2] https://lore.kernel.org/all/SA1PR21MB69218F955B62DFF62E3E88D2BF222@SA1PR21MB6921.namprd21.prod.outlook.com/
[3] https://lore.kernel.org/all/SN6PR02MB415726B17D5A6027CD1717E8D4342@SN6PR02MB4157.namprd02.prod.outlook.com/
[4] https://lore.kernel.org/all/SA1PR21MB69213486F821CA5A2C793C81BF342@SA1PR21MB6921.namprd21.prod.outlook.com/
--- drivers/hv/vmbus_drv.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-)
drivers/hv/vmbus_drv.c +26 -3
@@ -2291,8 +2291,8 @@ static acpi_status vmbus_walk_resources(return AE_NO_MEMORY ;/* If this range overlaps the virtual TPM, truncate it. */ -if (end >VTPM_BASE_ADDRESS && start <VTPM_BASE_ADDRESS ) - end =VTPM_BASE_ADDRESS ; +if (end >=VTPM_BASE_ADDRESS && start <VTPM_BASE_ADDRESS ) + end =VTPM_BASE_ADDRESS -1 ; new_res->name ="hyperv mmio" ; new_res->flags =IORESOURCE_MEM ;
@@ -2359,6 +2359,7 @@ static void vmbus_mmio_remove(void)static void __maybe_unused vmbus_reserve_fb (void ) {resource_size_t start =0 , size; +resource_size_t low_mmio_base;struct pci_dev *pdev;if (efi_enabled (EFI_BOOT )) {
@@ -2366,6 +2367,24 @@ static void __maybe_unused vmbus_reserveif (IS_ENABLED (CONFIG_SYSFB )) { start = screen_info.lfb_base; size =max_t (__u32, screen_info.lfb_size,0x800000 ); + + low_mmio_base = hyperv_mmio->start; +if (!low_mmio_base ||upper_32_bits (low_mmio_base) || + (start && start < low_mmio_base)) { +pr_warn ("Unexpected low mmio base %pa\n" , &low_mmio_base); + }else { +/* +* If the kdump/kexec or CVM kernel's lfb_base +* is 0, fall back to the low mmio base. +*/ +if (!start) + start = low_mmio_base; +/* +* Reserve half of the space below 4GB for high +* resolutions, but cap the reservation to 128MB. +*/ + size =min ((SZ_4G - start) /2 ,SZ_128M ); + } } }else {/* Gen1 VM: get FB base from PCI */
@@ -2386,8 +2405,10 @@ static void __maybe_unused vmbus_reservepci_dev_put (pdev); } -if (!start) +if (!start) { +pr_warn ("Unexpected framebuffer mmio base of zero\n" );return ; + }/* * Make a claim for the frame buffer in the resource tree under the
@@ -2397,6 +2418,8 @@ static void __maybe_unused vmbus_reserve*/ for (; !fb_mmio && (size >=0x100000 ); size >>=1 ) fb_mmio =__request_region (hyperv_mmio, start, size, fb_mmio_name,0 ); + +pr_info ("hv_mmio=%pR,%pR fb=%pR\n" , hyperv_mmio, hyperv_mmio->sibling, fb_mmio); }/**
Greg Kroah-Hartman
maintainerPATCH 32/60 firmware: samsung: acpm: Fix cross-thread RX length corruption
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tudor Ambarus <[email protected]>
[ Upstream commit f133bd4b5daf71bccdde0ad1a4f47fac76a6bfb1 ]
Sashiko identified a cross-thread RX length corruption bug when reviewing the thermal addition to ACPM [1].
When multiple threads concurrently send IPC requests, the ACPM polling mechanism can encounter responses belonging to other threads. To drain the queue, the driver saves these concurrent responses into an internal cache (rx_data->cmd) to be retrieved later by the owning thread.
Previously, the driver incorrectly used xfer->rxcnt (the expected receive length of the *current* polling thread) when copying data for *other* threads into this cache. If the threads expected responses of different lengths, this resulted in buffer underflows (leading to reads of uninitialized memory) or potential buffer overflows.
Fix this by replacing the boolean response flag in struct acpm_rx_data with rxcnt, caching the exact expected receive length for each specific transaction during transfer preparation. Use this cached length when saving concurrent responses.
Consequently, ensure that xfer->rxcnt is explicitly zeroed in driver helpers (e.g., acpm_dvfs_set_xfer) for fire-and-forget messages to prevent uninitialized stack garbage from being interpreted as a massive expected receive length.
--- drivers/firmware/samsung/exynos-acpm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
drivers/firmware/samsung/exynos-acpm.c +7 -7
@@ -103,12 +103,12 @@ struct acpm_queue {* * @cmd: pointer to where the data shall be saved. * @n_cmd: number of 32-bit commands. -* @response: true if the client expects the RX data. +* @rxcnt: expected length of the response in 32-bit words. */ struct acpm_rx_data {u32 *cmd;size_t n_cmd; -bool response; +size_t rxcnt; };#define ACPM_SEQNUM_MAX 64
@@ -196,7 +196,7 @@ static void acpm_get_saved_rx(struct acpconst struct acpm_rx_data *rx_data = &achan->rx_data[tx_seqnum -1 ];u32 rx_seqnum; -if (!rx_data->response) +if (!rx_data->rxcnt)return ; rx_seqnum =FIELD_GET (ACPM_PROTOCOL_SEQNUM , rx_data->cmd[0 ]);
@@ -253,7 +253,7 @@ static int acpm_get_rx(struct acpm_chan seqnum = rx_seqnum -1 ; rx_data = &achan->rx_data[seqnum]; -if (rx_data->response) { +if (rx_data->rxcnt) {if (rx_seqnum == tx_seqnum) {__ioread32_copy (xfer->rxd, addr, xfer->rxlen /4 );
@@ -267,7 +267,7 @@ static int acpm_get_rx(struct acpm_chan* after the response is copied to the request. */ __ioread32_copy (rx_data->cmd, addr, - xfer->rxlen /4 ); + rx_data->rxcnt); } }else {clear_bit (seqnum, achan->bitmap_seqnum);
@@ -379,8 +379,8 @@ static void acpm_prepare_xfer(struct acp/* Clear data for upcoming responses */ rx_data = &achan->rx_data[achan->seqnum -1 ];memset (rx_data->cmd,0 ,sizeof (*rx_data->cmd) * rx_data->n_cmd); -if (xfer->rxd) - rx_data->response = true; +/* zero means no response expected */ + rx_data->rxcnt = xfer->rxlen /4 ;/* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ set_bit (achan->seqnum -1 , achan->bitmap_seqnum);
Greg Kroah-Hartman
maintainerPATCH 33/60 sctp: disable BH before calling udp_tunnel_xmit_skb()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xin Long <[email protected]>
commit 2cd7e6971fc2787408ceef17906ea152791448cf upstream.
udp_tunnel_xmit_skb() / udp_tunnel6_xmit_skb() are expected to run with BH disabled. After commit 6f1a9140ecda ("add xmit recursion limit to tunnel xmit functions"), on the path:
udp(6)_tunnel_xmit_skb() -> ip(6)tunnel_xmit()
dev_xmit_recursion_inc()/dec() must stay balanced on the same CPU.
Without local_bh_disable(), the context may move between CPUs, which can break the inc/dec pairing. This may lead to incorrect recursion level detection and cause packets to be dropped in ip(6)_tunnel_xmit() or __dev_queue_xmit().
Fix it by disabling BH around both IPv4 and IPv6 SCTP UDP xmit paths.
In my testing, after enabling the SCTP over UDP:
# ip net exec ha sysctl -w net.sctp.udp_port=9899 # ip net exec ha sysctl -w net.sctp.encap_port=9899 # ip net exec hb sysctl -w net.sctp.udp_port=9899 # ip net exec hb sysctl -w net.sctp.encap_port=9899
# ip net exec ha iperf3 -s
- without this patch:
# ip net exec hb iperf3 -c 192.168.0.1 --sctp [ 5] 0.00-10.00 sec 37.2 MBytes 31.2 Mbits/sec sender [ 5] 0.00-10.00 sec 37.1 MBytes 31.1 Mbits/sec receiver
- with this patch:
# ip net exec hb iperf3 -c 192.168.0.1 --sctp [ 5] 0.00-10.00 sec 3.14 GBytes 2.69 Gbits/sec sender [ 5] 0.00-10.00 sec 3.14 GBytes 2.69 Gbits/sec receiver
--- net/sctp/ipv6.c | 2 ++ net/sctp/protocol.c | 2 ++ 2 files changed, 4 insertions(+)
net/sctp/ipv6.c +2 -0
@@ -261,9 +261,11 @@ static int sctp_v6_xmit(struct sk_buff *skb_set_inner_ipproto (skb,IPPROTO_SCTP ); label =ip6_make_flowlabel (sock_net (sk), skb, fl6->flowlabel, true, fl6); +local_bh_disable ();udp_tunnel6_xmit_skb (dst, sk, skb,NULL , &fl6->saddr, &fl6->daddr, tclass,ip6_dst_hoplimit (dst), label,sctp_sk (sk)->udp_port, t->encap_port, false,0 ); +local_bh_enable ();return 0 ; }
net/sctp/protocol.c +2 -0
@@ -1102,10 +1102,12 @@ static inline int sctp_v4_xmit(struct skskb_reset_inner_mac_header (skb);skb_reset_inner_transport_header (skb);skb_set_inner_ipproto (skb,IPPROTO_SCTP ); +local_bh_disable ();udp_tunnel_xmit_skb (dst_rtable (dst), sk, skb, fl4->saddr, fl4->daddr, dscp,ip4_dst_hoplimit (dst), df,sctp_sk (sk)->udp_port, t->encap_port, false, false,0 ); +local_bh_enable ();return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 34/60 mm: introduce VM_MAYBE_GUARD and make visible in /proc/$pid/smaps
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes <[email protected]>
commit 5dba5cc2e0ffa76f2f6c8922a04469dc9602c396 upstream.
Patch series "introduce VM_MAYBE_GUARD and make it sticky", v4.
Currently, guard regions are not visible to users except through /proc/$pid/pagemap, with no explicit visibility at the VMA level.
This makes the feature less useful, as it isn't entirely apparent which VMAs may have these entries present, especially when performing actions which walk through memory regions such as those performed by CRIU.
This series addresses this issue by introducing the VM_MAYBE_GUARD flag which fulfils this role, updating the smaps logic to display an entry for these.
The semantics of this flag are that a guard region MAY be present if set (we cannot be sure, as we can't efficiently track whether an MADV_GUARD_REMOVE finally removes all the guard regions in a VMA) - but if not set the VMA definitely does NOT have any guard regions present.
It's problematic to establish this flag without further action, because that means that VMAs with guard regions in them become non-mergeable with adjacent VMAs for no especially good reason.
To work around this, this series also introduces the concept of 'sticky' VMA flags - that is flags which:
a. if set in one VMA and not in another still permit those VMAs to be
merged (if otherwise compatible).
b. When they are merged, the resultant VMA must have the flag set.
The VMA logic is updated to propagate these flags correctly.
Additionally, VM_MAYBE_GUARD being an explicit VMA flag allows us to solve an issue with file-backed guard regions - previously these established an anon_vma object for file-backed mappings solely to have vma_needs_copy() correctly propagate guard region mappings to child processes.
We introduce a new flag alias VM_COPY_ON_FORK (which currently only specifies VM_MAYBE_GUARD) and update vma_needs_copy() to check explicitly for this flag and to copy page tables if it is present, which resolves this issue.
Additionally, we add the ability for allow-listed VMA flags to be atomically writable with only mmap/VMA read locks held.
The only flag we allow so far is VM_MAYBE_GUARD, which we carefully ensure does not cause any races by being allowed to do so.
This allows us to maintain guard region installation as a read-locked operation and not endure the overhead of obtaining a write lock here.
Finally we introduce extensive VMA userland tests to assert that the sticky VMA logic behaves correctly as well as guard region self tests to assert that smaps visibility is correctly implemented.
This patch (of 9):
Currently, if a user needs to determine if guard regions are present in a range, they have to scan all VMAs (or have knowledge of which ones might have guard regions).
Since commit 8e2f2aeb8b48 ("fs/proc/task_mmu: add guard region bit to pagemap") and the related commit a516403787e0 ("fs/proc: extend the PAGEMAP_SCAN ioctl to report guard regions"), users can use either /proc/$pid/pagemap or the PAGEMAP_SCAN functionality to perform this operation at a virtual address level.
This is not ideal, and it gives no visibility at a /proc/$pid/smaps level that guard regions exist in ranges.
This patch remedies the situation by establishing a new VMA flag, VM_MAYBE_GUARD, to indicate that a VMA may contain guard regions (it is uncertain because we cannot reasonably determine whether a MADV_GUARD_REMOVE call has removed all of the guard regions in a VMA, and additionally VMAs may change across merge/split).
We utilise 0x800 for this flag which makes it available to 32-bit architectures also, a flag that was previously used by VM_DENYWRITE, which was removed in commit 8d0920bde5eb ("mm: remove VM_DENYWRITE") and hasn't bee reused yet.
We also update the smaps logic and documentation to identify these VMAs.
Another major use of this functionality is that we can use it to identify that we ought to copy page tables on fork.
We do not actually implement usage of this flag in mm/madvise.c yet as we need to allow some VMA flags to be applied atomically under mmap/VMA read lock in order to avoid the need to acquire a write lock for this purpose.
--- Documentation/filesystems/proc.rst | 5 +++-- fs/proc/task_mmu.c | 1 + include/linux/mm.h | 3 +++ include/trace/events/mmflags.h | 1 + mm/memory.c | 4 ++++ tools/testing/vma/vma_internal.h | 1 + 6 files changed, 13 insertions(+), 2 deletions(-)
Documentation/filesystems/proc.rst +3 -2
@@ -553,7 +553,7 @@ otherwise. kernel flags associated with the particular virtual memory area in two letter encoded manner. The codes are the following: - == ======================================= + == ============================================================= rd readable wr writeable ex executable
@@ -591,7 +591,8 @@ encoded manner. The codes are the follow sl sealed lf lock on fault pages dp always lazily freeable mapping - == ======================================= + gu maybe contains guard regions (if not set, definitely doesn't) + == ============================================================= Note that there is no guarantee that every flag and associated mnemonic will be present in all further kernel releases. Things get changed, the flags may
fs/proc/task_mmu.c +1 -0
@@ -1159,6 +1159,7 @@ static void show_smap_vma_flags(struct s [ilog2(VM_MAYSHARE)] = "ms", [ilog2(VM_GROWSDOWN)] = "gd", [ilog2(VM_PFNMAP)] = "pf", + [ilog2(VM_MAYBE_GUARD)] = "gu", [ilog2(VM_LOCKED)] = "lo", [ilog2(VM_IO)] = "io", [ilog2(VM_SEQ_READ)] = "sr",
include/linux/mm.h +3 -0
@@ -269,6 +269,8 @@ extern struct rw_semaphore nommu_region_extern unsigned int kobjsize (const void *objp);#endif +#define VM_MAYBE_GUARD_BIT 11 +/* * vm_flags in vm_area_struct, see mm_types.h. * When changing, update also include/trace/events/mmflags.h
@@ -294,6 +296,7 @@ extern unsigned int kobjsize(const void#define VM_UFFD_MISSING 0#endif /* CONFIG_MMU */ #define VM_PFNMAP 0x00000400/* Page-ranges managed without "struct page", just pure PFN */ +#define VM_MAYBE_GUARD BIT(VM_MAYBE_GUARD_BIT)/* The VMA maybe contains guard regions. */ #define VM_UFFD_WP 0x00001000/* wrprotect pages tracking */ #define VM_LOCKED 0x00002000
include/trace/events/mmflags.h +1 -0
@@ -213,6 +213,7 @@ IF_HAVE_PG_ARCH_3(arch_3) {VM_UFFD_MISSING, "uffd_missing" }, \ IF_HAVE_UFFD_MINOR(VM_UFFD_MINOR, "uffd_minor" ) \ {VM_PFNMAP, "pfnmap" }, \ + {VM_MAYBE_GUARD, "maybe_guard" }, \ {VM_UFFD_WP, "uffd_wp" }, \ {VM_LOCKED, "locked" }, \ {VM_IO, "io" }, \
mm/memory.c +4 -0
@@ -1494,6 +1494,10 @@ vma_needs_copy(struct vm_area_struct *dsif (src_vma->anon_vma)return true; +/* Guard regions have modified page tables that require copying. */ +if (src_vma->vm_flags &VM_MAYBE_GUARD ) +return true; +/* * Don't copy ptes where a page fault will fill them correctly. Fork * becomes much lighter when there are big shared or private readonly
tools/testing/vma/vma_internal.h +1 -0
@@ -56,6 +56,7 @@ extern unsigned long dac_mmap_min_addr;#define VM_MAYEXEC 0x00000040#define VM_GROWSDOWN 0x00000100#define VM_PFNMAP 0x00000400 +#define VM_MAYBE_GUARD 0x00000800#define VM_LOCKED 0x00002000#define VM_IO 0x00004000#define VM_SEQ_READ 0x00008000/* App will access data sequentially */
Greg Kroah-Hartman
maintainerPATCH 35/60 mm: add atomic VMA flags and set VM_MAYBE_GUARD as such
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes <[email protected]>
commit 568822502383acd57d7cc1c72ee43932c45a9524 upstream.
This patch adds the ability to atomically set VMA flags with only the mmap read/VMA read lock held.
As this could be hugely problematic for VMA flags in general given that all other accesses are non-atomic and serialised by the mmap/VMA locks, we implement this with a strict allow-list - that is, only designated flags are allowed to do this.
We make VM_MAYBE_GUARD one of these flags.
--- include/linux/mm.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+)
include/linux/mm.h +44 -0
@@ -501,6 +501,9 @@ extern unsigned int kobjsize(const void/* This mask represents all the VMA flag bits used by mlock */ #define VM_LOCKED_MASK (VM_LOCKED | VM_LOCKONFAULT) +/* These flags can be updated atomically via VMA/mmap read lock. */ +#define VM_ATOMIC_SET_ALLOWED VM_MAYBE_GUARD +/* Arch-specific flags to clear when updating VM flags on protection change */ #ifndef VM_ARCH_CLEAR # define VM_ARCH_CLEAR VM_NONE
@@ -843,6 +846,47 @@ static inline void vm_flags_mod(struct v__vm_flags_mod (vma, set, clear); } +static inline bool __vma_flag_atomic_valid (struct vm_area_struct *vma, +int bit) +{ +const vm_flags_t mask =BIT (bit); + +/* Only specific flags are permitted */ +if (WARN_ON_ONCE (!(mask &VM_ATOMIC_SET_ALLOWED ))) +return false; + +return true; +} + +/* +* Set VMA flag atomically. Requires only VMA/mmap read lock. Only specific +* valid flags are allowed to do this. +*/ +static inline void vma_flag_set_atomic (struct vm_area_struct *vma,int bit) +{ +/* mmap read lock/VMA read lock must be held. */ +if (!rwsem_is_locked (&vma->vm_mm->mmap_lock)) +vma_assert_locked (vma); + +if (__vma_flag_atomic_valid (vma, bit)) +set_bit (bit, &ACCESS_PRIVATE (vma, __vm_flags)); +} + +/* +* Test for VMA flag atomically. Requires no locks. Only specific valid flags +* are allowed to do this. +* +* This is necessarily racey, so callers must ensure that serialisation is +* achieved through some other means, or that races are permissible. +*/ +static inline bool vma_flag_test_atomic (struct vm_area_struct *vma,int bit) +{ +if (__vma_flag_atomic_valid (vma, bit)) +return test_bit (bit, &vma->vm_flags); + +return false; +} +static inline void vma_set_anonymous (struct vm_area_struct *vma) { vma->vm_ops =NULL ;
Greg Kroah-Hartman
maintainerPATCH 36/60 mm: update vma_modify_flags() to handle residual flags, document
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes <[email protected]>
commit 9119d6c2095bb20292cb9812dd70d37f17e3bd37 upstream.
The vma_modify_*() family of functions each either perform splits, a merge or no changes at all in preparation for the requested modification to occur.
When doing so for a VMA flags change, we currently don't account for any flags which may remain (for instance, VM_SOFTDIRTY) despite the requested change in the case that a merge succeeded.
This is made more important by subsequent patches which will introduce the concept of sticky VMA flags which rely on this behaviour.
This patch fixes this by passing the VMA flags parameter as a pointer and updating it accordingly on merge and updating callers to accommodate for this.
Additionally, while we are here, we add kdocs for each of the vma_modify_*() functions, as the fact that the requested modification is not performed is confusing so it is useful to make this abundantly clear.
We also update the VMA userland tests to account for this change.
--- mm/madvise.c | 2 mm/mlock.c | 2 mm/mprotect.c | 2 mm/mseal.c | 7 +- mm/vma.c | 56 ++++++++++--------- mm/vma.h | 138 +++++++++++++++++++++++++++++++++++------------- tools/testing/vma/vma.c | 3 - 7 files changed, 142 insertions(+), 68 deletions(-)
mm/madvise.c +1 -1
@@ -167,7 +167,7 @@ static int madvise_update_vma(vm_flags_t range->start, range->end, anon_name); else vma =vma_modify_flags (&vmi, madv_behavior->prev, vma, - range->start, range->end, new_flags); + range->start, range->end, &new_flags);if (IS_ERR (vma))return PTR_ERR (vma);
mm/mlock.c +1 -1
@@ -480,7 +480,7 @@ static int mlock_fixup(struct vma_iterat*/ goto out; - vma =vma_modify_flags (vmi, *prev, vma, start, end, newflags); + vma =vma_modify_flags (vmi, *prev, vma, start, end, &newflags);if (IS_ERR (vma)) { ret =PTR_ERR (vma); goto out;
mm/mprotect.c +1 -1
@@ -813,7 +813,7 @@ mprotect_fixup(struct vma_iterator *vmi, newflags &= ~VM_ACCOUNT ; } - vma =vma_modify_flags (vmi, *pprev, vma, start, end, newflags); + vma =vma_modify_flags (vmi, *pprev, vma, start, end, &newflags);if (IS_ERR (vma)) { error =PTR_ERR (vma); goto fail;
mm/mseal.c +4 -3
@@ -69,9 +69,10 @@ static int mseal_apply(struct mm_structconst unsigned long curr_end =MIN (vma->vm_end, end);if (!(vma->vm_flags &VM_SEALED )) { - vma =vma_modify_flags (&vmi, prev, vma, - curr_start, curr_end, - vma->vm_flags |VM_SEALED ); +vm_flags_t vm_flags = vma->vm_flags |VM_SEALED ; + + vma =vma_modify_flags (&vmi, prev, vma, curr_start, + curr_end, &vm_flags);if (IS_ERR (vma))return PTR_ERR (vma);vm_flags_set (vma,VM_SEALED );
mm/vma.c +30 -26
81 lines not on this page · raw message
mm/vma.h +103 -35
150 lines not on this page · raw message
tools/testing/vma/vma.c +2 -1
@@ -339,6 +339,7 @@ static bool test_simple_modify(void)struct mm_struct mm = {};struct vm_area_struct *init_vma =alloc_vma (&mm,0 ,0x3000 ,0 , vm_flags);VMA_ITERATOR (vmi, &mm,0x1000 ); +vm_flags_t flags =VM_READ |VM_MAYREAD ;ASSERT_FALSE (attach_vma (&mm, init_vma));
@@ -347,7 +348,7 @@ static bool test_simple_modify(void)* performs the merge/split only. */ vma =vma_modify_flags (&vmi, init_vma, init_vma, -0x1000 ,0x2000 ,VM_READ |VM_MAYREAD ); +0x1000 ,0x2000 , &flags);ASSERT_NE (vma,NULL );/* We modify the provided VMA, and on split allocate new VMAs. */ ASSERT_EQ (vma, init_vma);
Greg Kroah-Hartman
maintainerPATCH 37/60 mm: implement sticky VMA flags
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes <[email protected]>
commit 64212ba02e66e705cabce188453ba4e61e9d7325 upstream.
It is useful to be able to designate that certain flags are 'sticky', that is, if two VMAs are merged one with a flag of this nature and one without, the merged VMA sets this flag.
As a result we ignore these flags for the purposes of determining VMA flag differences between VMAs being considered for merge.
This patch therefore updates the VMA merge logic to perform this action, with flags possessing this property being described in the VM_STICKY bitmap.
Those flags which ought to be ignored for the purposes of VMA merge are described in the VM_IGNORE_MERGE bitmap, which the VMA merge logic is also updated to use.
As part of this change we place VM_SOFTDIRTY in VM_IGNORE_MERGE as it already had this behaviour, alongside VM_STICKY as sticky flags by implication must not disallow merge.
Ultimately it seems that we should make VM_SOFTDIRTY a sticky flag in its own right, but this change is out of scope for this series.
The only sticky flag designated as such is VM_MAYBE_GUARD, so as a result of this change, once the VMA flag is set upon guard region installation, VMAs with guard ranges will now not have their merge behaviour impacted as a result and can be freely merged with other VMAs without VM_MAYBE_GUARD set.
Also update the comments for vma_modify_flags() to directly reference sticky flags now we have established the concept.
We also update the VMA userland tests to account for the changes.
--- include/linux/mm.h | 28 ++++++++++++++++++++++++++++ mm/vma.c | 31 +++++++++++++++++-------------- mm/vma.h | 10 ++++------ tools/testing/vma/vma_internal.h | 28 ++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 20 deletions(-)
include/linux/mm.h +28 -0
@@ -511,6 +511,34 @@ extern unsigned int kobjsize(const void#define VM_FLAGS_CLEAR (ARCH_VM_PKEY_FLAGS | VM_ARCH_CLEAR)/* +* Flags which should be 'sticky' on merge - that is, flags which, when one VMA +* possesses it but the other does not, the merged VMA should nonetheless have +* applied to it: +* +* VM_MAYBE_GUARD - If a VMA may have guard regions in place it implies that +* mapped page tables may contain metadata not described by the +* VMA and thus any merged VMA may also contain this metadata, +* and thus we must make this flag sticky. +*/ +#define VM_STICKY VM_MAYBE_GUARD + +/* +* VMA flags we ignore for the purposes of merge, i.e. one VMA possessing one +* of these flags and the other not does not preclude a merge. +* +* VM_SOFTDIRTY - Should not prevent from VMA merging, if we match the flags but +* dirty bit -- the caller should mark merged VMA as dirty. If +* dirty bit won't be excluded from comparison, we increase +* pressure on the memory system forcing the kernel to generate +* new VMAs when old one could be extended instead. +* +* VM_STICKY - When merging VMAs, VMA flags must match, unless they are +* 'sticky'. If any sticky flags exist in either VMA, we simply +* set all of them on the merged VMA. +*/ +#define VM_IGNORE_MERGE (VM_SOFTDIRTY | VM_STICKY) + +/* * mapping from the currently active vm_flags protection bits (the * low four bits) to a page protection mask.. */
mm/vma.c +17 -14
@@ -82,15 +82,7 @@ static inline bool is_mergeable_vma(struif (!mpol_equal (vmg->policy,vma_policy (vma)))return false; -/* -* VM_SOFTDIRTY should not prevent from VMA merging, if we -* match the flags but dirty bit -- the caller should mark -* merged VMA as dirty. If dirty bit won't be excluded from -* comparison, we increase pressure on the memory system forcing -* the kernel to generate new VMAs when old one could be -* extended instead. -*/ -if ((vma->vm_flags ^ vmg->vm_flags) & ~VM_SOFTDIRTY ) +if ((vma->vm_flags ^ vmg->vm_flags) & ~VM_IGNORE_MERGE )return false;if (vma->vm_file != vmg->file)return false;
@@ -810,6 +802,7 @@ static bool can_merge_remove_vma(structstatic __must_check struct vm_area_struct *vma_merge_existing_range (struct vma_merge_struct *vmg) { +vm_flags_t sticky_flags = vmg->vm_flags &VM_STICKY ;struct vm_area_struct *middle = vmg->middle;struct vm_area_struct *prev = vmg->prev;struct vm_area_struct *next;
@@ -904,11 +897,13 @@ static __must_check struct vm_area_strucif (merge_right) {vma_start_write (next); vmg->target = next; + sticky_flags |= (next->vm_flags &VM_STICKY ); }if (merge_left) {vma_start_write (prev); vmg->target = prev; + sticky_flags |= (prev->vm_flags &VM_STICKY ); }if (merge_both) {
@@ -978,6 +973,7 @@ static __must_check struct vm_area_strucif (err ||commit_merge (vmg)) goto abort; +vm_flags_set (vmg->target, sticky_flags);khugepaged_enter_vma (vmg->target, vmg->vm_flags); vmg->state =VMA_MERGE_SUCCESS ;return vmg->target;
@@ -1156,14 +1152,20 @@ int vma_expand(struct vma_merge_struct *struct vm_area_struct *target = vmg->target;struct vm_area_struct *next = vmg->next;int ret =0 ; +vm_flags_t sticky_flags; + + sticky_flags = vmg->vm_flags &VM_STICKY ; + sticky_flags |= target->vm_flags &VM_STICKY ;VM_WARN_ON_VMG (!target, vmg);mmap_assert_write_locked (vmg->mm);vma_start_write (target); -if (next && target != next && vmg->end == next->vm_end) +if (next && target != next && vmg->end == next->vm_end) { + sticky_flags |= next->vm_flags &VM_STICKY ; remove_next = true; + }/* We must have a target. */ VM_WARN_ON_VMG (!target, vmg);
@@ -1197,6 +1199,7 @@ int vma_expand(struct vma_merge_struct *if (commit_merge (vmg)) goto nomem; +vm_flags_set (target, sticky_flags);return 0 ; nomem:
@@ -1692,9 +1695,9 @@ struct vm_area_struct *vma_modify_flags(return ret;/* -* For a merge to succeed, the flags must match those requested. For -* flags which do not obey typical merge rules (i.e. do not need to -* match), we must let the caller know about them. +* For a merge to succeed, the flags must match those +* requested. However, sticky flags may have been retained, so propagate +* them to the caller. */ if (vmg.state ==VMA_MERGE_SUCCESS ) *vm_flags_ptr = ret->vm_flags;
@@ -1959,7 +1962,7 @@ static int anon_vma_compatible(struct vmreturn a->vm_end == b->vm_start &&mpol_equal (vma_policy (a),vma_policy (b)) && a->vm_file == b->vm_file && - !((a->vm_flags ^ b->vm_flags) & ~(VM_ACCESS_FLAGS |VM_SOFTDIRTY )) && + !((a->vm_flags ^ b->vm_flags) & ~(VM_ACCESS_FLAGS |VM_IGNORE_MERGE )) && b->vm_pgoff == a->vm_pgoff + ((b->vm_start - a->vm_start) >>PAGE_SHIFT ); }
mm/vma.h +4 -6
@@ -276,17 +276,15 @@ void unmap_region(struct ma_state *mas,* @start: The start of the range to update. May be offset within @vma. * @end: The exclusive end of the range to update, may be offset within @vma. * @vm_flags_ptr: A pointer to the VMA flags that the @start to @end range is -* about to be set to. On merge, this will be updated to include any additional -* flags which remain in place. +* about to be set to. On merge, this will be updated to include sticky flags. * * IMPORTANT: The actual modification being requested here is NOT applied, * rather the VMA is perhaps split, perhaps merged to accommodate the change, * and the caller is expected to perform the actual modification. * -* In order to account for VMA flags which may persist (e.g. soft-dirty), the -* @vm_flags_ptr parameter points to the requested flags which are then updated -* so the caller, should they overwrite any existing flags, correctly retains -* these. +* In order to account for sticky VMA flags, the @vm_flags_ptr parameter points +* to the requested flags which are then updated so the caller, should they +* overwrite any existing flags, correctly retains these. * * Returns: A VMA which contains the range @start to @end ready to have its * flags altered to *@vm_flags.
tools/testing/vma/vma_internal.h +28 -0
@@ -117,6 +117,34 @@ extern unsigned long dac_mmap_min_addr;#define VM_SEALED VM_NONE#endif +/* +* Flags which should be 'sticky' on merge - that is, flags which, when one VMA +* possesses it but the other does not, the merged VMA should nonetheless have +* applied to it: +* +* VM_MAYBE_GUARD - If a VMA may have guard regions in place it implies that +* mapped page tables may contain metadata not described by the +* VMA and thus any merged VMA may also contain this metadata, +* and thus we must make this flag sticky. +*/ +#define VM_STICKY VM_MAYBE_GUARD + +/* +* VMA flags we ignore for the purposes of merge, i.e. one VMA possessing one +* of these flags and the other not does not preclude a merge. +* +* VM_SOFTDIRTY - Should not prevent from VMA merging, if we match the flags but +* dirty bit -- the caller should mark merged VMA as dirty. If +* dirty bit won't be excluded from comparison, we increase +* pressure on the memory system forcing the kernel to generate +* new VMAs when old one could be extended instead. +* +* VM_STICKY - When merging VMAs, VMA flags must match, unless they are +* 'sticky'. If any sticky flags exist in either VMA, we simply +* set all of them on the merged VMA. +*/ +#define VM_IGNORE_MERGE (VM_SOFTDIRTY | VM_STICKY) +#define FIRST_USER_ADDRESS 0UL#define USER_PGTABLES_CEILING 0UL
Greg Kroah-Hartman
maintainerPATCH 38/60 mm: introduce copy-on-fork VMAs and make VM_MAYBE_GUARD one
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes <[email protected]>
commit ab04b530e7e8bd5cf9fb0c1ad20e0deee8f569ec upstream.
Gather all the VMA flags whose presence implies that page tables must be copied on fork into a single bitmap - VM_COPY_ON_FORK - and use this rather than specifying individual flags in vma_needs_copy().
We also add VM_MAYBE_GUARD to this list, as it being set on a VMA implies that there may be metadata contained in the page tables (that is - guard markers) which would will not and cannot be propagated upon fork.
This was already being done manually previously in vma_needs_copy(), but this makes it very explicit, alongside VM_PFNMAP, VM_MIXEDMAP and VM_UFFD_WP all of which imply the same.
Note that VM_STICKY flags ought generally to be marked VM_COPY_ON_FORK too
- because equally a flag being VM_STICKY indicates that the VMA contains metadat that is not propagated by being faulted in - i.e. that the VMA metadata does not fully describe the VMA alone, and thus we must propagate whatever metadata there is on a fork.
However, for maximum flexibility, we do not make this necessarily the case here.
--- include/linux/mm.h | 26 ++++++++++++++++++++++++++ mm/memory.c | 18 ++++-------------- tools/testing/vma/vma_internal.h | 26 ++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 14 deletions(-)
include/linux/mm.h +26 -0
@@ -539,6 +539,32 @@ extern unsigned int kobjsize(const void#define VM_IGNORE_MERGE (VM_SOFTDIRTY | VM_STICKY)/* +* Flags which should result in page tables being copied on fork. These are +* flags which indicate that the VMA maps page tables which cannot be +* reconsistuted upon page fault, so necessitate page table copying upon +* +* VM_PFNMAP / VM_MIXEDMAP - These contain kernel-mapped data which cannot be +* reasonably reconstructed on page fault. +* +* VM_UFFD_WP - Encodes metadata about an installed uffd +* write protect handler, which cannot be +* reconstructed on page fault. +* +* We always copy pgtables when dst_vma has uffd-wp +* enabled even if it's file-backed +* (e.g. shmem). Because when uffd-wp is enabled, +* pgtable contains uffd-wp protection information, +* that's something we can't retrieve from page cache, +* and skip copying will lose those info. +* +* VM_MAYBE_GUARD - Could contain page guard region markers which +* by design are a property of the page tables +* only and thus cannot be reconstructed on page +* fault. +*/ +#define VM_COPY_ON_FORK (VM_PFNMAP | VM_MIXEDMAP | VM_UFFD_WP | VM_MAYBE_GUARD) + +/* * mapping from the currently active vm_flags protection bits (the * low four bits) to a page protection mask.. */
mm/memory.c +4 -14
@@ -1479,25 +1479,15 @@ copy_p4d_range(struct vm_area_struct *dsstatic bool vma_needs_copy (struct vm_area_struct *dst_vma,struct vm_area_struct *src_vma) { +if (src_vma->vm_flags &VM_COPY_ON_FORK ) +return true;/* -* Always copy pgtables when dst_vma has uffd-wp enabled even if it's -* file-backed (e.g. shmem). Because when uffd-wp is enabled, pgtable -* contains uffd-wp protection information, that's something we can't -* retrieve from page cache, and skip copying will lose those info. +* The presence of an anon_vma indicates an anonymous VMA has page +* tables which naturally cannot be reconstituted on page fault. */ -if (userfaultfd_wp (dst_vma)) -return true; - -if (src_vma->vm_flags & (VM_PFNMAP |VM_MIXEDMAP )) -return true; -if (src_vma->anon_vma)return true; -/* Guard regions have modified page tables that require copying. */ -if (src_vma->vm_flags &VM_MAYBE_GUARD ) -return true; -/* * Don't copy ptes where a page fault will fill them correctly. Fork * becomes much lighter when there are big shared or private readonly
tools/testing/vma/vma_internal.h +26 -0
@@ -145,6 +145,32 @@ extern unsigned long dac_mmap_min_addr;*/ #define VM_IGNORE_MERGE (VM_SOFTDIRTY | VM_STICKY) +/* +* Flags which should result in page tables being copied on fork. These are +* flags which indicate that the VMA maps page tables which cannot be +* reconsistuted upon page fault, so necessitate page table copying upon +* +* VM_PFNMAP / VM_MIXEDMAP - These contain kernel-mapped data which cannot be +* reasonably reconstructed on page fault. +* +* VM_UFFD_WP - Encodes metadata about an installed uffd +* write protect handler, which cannot be +* reconstructed on page fault. +* +* We always copy pgtables when dst_vma has uffd-wp +* enabled even if it's file-backed +* (e.g. shmem). Because when uffd-wp is enabled, +* pgtable contains uffd-wp protection information, +* that's something we can't retrieve from page cache, +* and skip copying will lose those info. +* +* VM_MAYBE_GUARD - Could contain page guard region markers which +* by design are a property of the page tables +* only and thus cannot be reconstructed on page +* fault. +*/ +#define VM_COPY_ON_FORK (VM_PFNMAP | VM_MIXEDMAP | VM_UFFD_WP | VM_MAYBE_GUARD) +#define FIRST_USER_ADDRESS 0UL#define USER_PGTABLES_CEILING 0UL
Greg Kroah-Hartman
maintainerPATCH 39/60 mm: set the VM_MAYBE_GUARD flag on guard region install
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes <[email protected]>
commit 49e14dabed7a294427588d4b315f57fbfcab9990 upstream.
Now we have established the VM_MAYBE_GUARD flag and added the capacity to set it atomically, do so upon MADV_GUARD_INSTALL.
The places where this flag is used currently and matter are:
* VMA merge - performed under mmap/VMA write lock, therefore excluding racing writes.
* /proc/$pid/smaps - can race the write, however this isn't meaningful as the flag write is performed at the point of the guard region being established, and thus an smaps reader can't reasonably expect to avoid races. Due to atomicity, a reader will observe either the flag being set or not. Therefore consistency will be maintained.
In all other cases the flag being set is irrelevant and atomicity guarantees other flags will be read correctly.
Note that non-atomic updates of unrelated flags do not cause an issue with this flag being set atomically, as writes of other flags are performed under mmap/VMA write lock, and these atomic writes are performed under mmap/VMA read lock, which excludes the write, avoiding RMW races.
Note that we do not encounter issues with KCSAN by adjusting this flag atomically, as we are only updating a single bit in the flag bitmap and therefore we do not need to annotate these changes.
We intentionally set this flag in advance of actually updating the page tables, to ensure that any racing atomic read of this flag will only return false prior to page tables being updated, to allow for serialisation via page table locks.
Note that we set vma->anon_vma for anonymous mappings. This is because the expectation for anonymous mappings is that an anon_vma is established should they possess any page table mappings. This is also consistent with what we were doing prior to this patch (unconditionally setting anon_vma on guard region installation).
We also need to update retract_page_tables() to ensure that madvise(..., MADV_COLLAPSE) doesn't incorrectly collapse file-backed ranges contain guard regions.
This was previously guarded by anon_vma being set to catch MAP_PRIVATE cases, but the introduction of VM_MAYBE_GUARD necessitates that we check this flag instead.
We utilise vma_flag_test_atomic() to do so - we first perform an optimistic check, then after the PTE page table lock is held, we can check again safely, as upon guard marker install the flag is set atomically prior to the page table lock being taken to actually apply it.
So if the initial check fails either:
* Page table retraction acquires page table lock prior to VM_MAYBE_GUARD being set - guard marker installation will be blocked until page table retraction is complete.
OR:
* Guard marker installation acquires page table lock after setting VM_MAYBE_GUARD, which raced and didn't pick this up in the initial optimistic check, blocking page table retraction until the guard regions are installed - the second VM_MAYBE_GUARD check will prevent page table retraction.
Either way we're safe.
We refactor the retraction checks into a single file_backed_vma_is_retractable(), there doesn't seem to be any reason that the checks were separated as before.
Note that VM_MAYBE_GUARD being set atomically remains correct as vma_needs_copy() is invoked with the mmap and VMA write locks held, excluding any race with madvise_guard_install().
--- mm/khugepaged.c | 71 +++++++++++++++++++++++++++++++++++++------------------- mm/madvise.c | 22 +++++++++++------ 2 files changed, 61 insertions(+), 32 deletions(-)
mm/khugepaged.c +47 -24
@@ -1715,6 +1715,43 @@ drop_folio:return result; } +/* Can we retract page tables for this file-backed VMA? */ +static bool file_backed_vma_is_retractable (struct vm_area_struct *vma) +{ +/* +* Check vma->anon_vma to exclude MAP_PRIVATE mappings that +* got written to. These VMAs are likely not worth removing +* page tables from, as PMD-mapping is likely to be split later. +*/ +if (READ_ONCE (vma->anon_vma)) +return false; + +/* +* When a vma is registered with uffd-wp, we cannot recycle +* the page table because there may be pte markers installed. +* Other vmas can still have the same file mapped hugely, but +* skip this one: it will always be mapped in small page size +* for uffd-wp registered ranges. +*/ +if (userfaultfd_wp (vma)) +return false; + +/* +* If the VMA contains guard regions then we can't collapse it. +* +* This is set atomically on guard marker installation under mmap/VMA +* read lock, and here we may not hold any VMA or mmap lock at all. +* +* This is therefore serialised on the PTE page table lock, which is +* obtained on guard region installation after the flag is set, so this +* check being performed under this lock excludes races. +*/ +if (vma_flag_test_atomic (vma,VM_MAYBE_GUARD_BIT )) +return false; + +return true; +} +static void retract_page_tables (struct address_space *mapping,pgoff_t pgoff) {struct vm_area_struct *vma;
@@ -1729,14 +1766,6 @@ static void retract_page_tables(struct aspinlock_t *ptl;bool success = false; -/* -* Check vma->anon_vma to exclude MAP_PRIVATE mappings that -* got written to. These VMAs are likely not worth removing -* page tables from, as PMD-mapping is likely to be split later. -*/ -if (READ_ONCE (vma->anon_vma)) -continue ; - addr = vma->vm_start + ((pgoff - vma->vm_pgoff) <<PAGE_SHIFT );if (addr & ~HPAGE_PMD_MASK || vma->vm_end < addr +HPAGE_PMD_SIZE )
@@ -1748,14 +1777,8 @@ static void retract_page_tables(struct aif (hpage_collapse_test_exit (mm))continue ; -/* -* When a vma is registered with uffd-wp, we cannot recycle -* the page table because there may be pte markers installed. -* Other vmas can still have the same file mapped hugely, but -* skip this one: it will always be mapped in small page size -* for uffd-wp registered ranges. -*/ -if (userfaultfd_wp (vma)) + +if (!file_backed_vma_is_retractable (vma))continue ;/* PTEs were notified when unmapped; but now for the PMD? */
@@ -1782,15 +1805,15 @@ static void retract_page_tables(struct aspin_lock_nested (ptl,SINGLE_DEPTH_NESTING );/* -* Huge page lock is still held, so normally the page table -* must remain empty; and we have already skipped anon_vma -* and userfaultfd_wp() vmas. But since the mmap_lock is not -* held, it is still possible for a racing userfaultfd_ioctl() -* to have inserted ptes or markers. Now that we hold ptlock, -* repeating the anon_vma check protects from one category, -* and repeating the userfaultfd_wp() check from another. +* Huge page lock is still held, so normally the page table must +* remain empty; and we have already skipped anon_vma and +* userfaultfd_wp() vmas. But since the mmap_lock is not held, +* it is still possible for a racing userfaultfd_ioctl() or +* madvise() to have inserted ptes or markers. Now that we hold +* ptlock, repeating the retractable checks protects us from +* races against the prior checks. */ -if (likely (!vma->anon_vma && !userfaultfd_wp (vma))) { +if (likely (file_backed_vma_is_retractable (vma))) { pgt_pmd =pmdp_collapse_flush (vma, addr, pmd);pmdp_get_lockless_sync (); success = true;
mm/madvise.c +14 -8
@@ -1141,15 +1141,21 @@ static long madvise_guard_install(structreturn -EINVAL ;/* -* If we install guard markers, then the range is no longer -* empty from a page table perspective and therefore it's -* appropriate to have an anon_vma. -* -* This ensures that on fork, we copy page tables correctly. +* Set atomically under read lock. All pertinent readers will need to +* acquire an mmap/VMA write lock to read it. All remaining readers may +* or may not see the flag set, but we don't care. */ - err =anon_vma_prepare (vma); -if (err) -return err; +vma_flag_set_atomic (vma,VM_MAYBE_GUARD_BIT ); + +/* +* If anonymous and we are establishing page tables the VMA ought to +* have an anon_vma associated with it. +*/ +if (vma_is_anonymous (vma)) { + err =anon_vma_prepare (vma); +if (err) +return err; + }/* * Optimistically try to install the guard marker pages first. If any
Greg Kroah-Hartman
maintainerPATCH 40/60 mm: propagate VM_SOFTDIRTY on merge
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes <[email protected]>
commit 6707915e030a3258868355f989b80140c1a45bbe upstream.
Patch series "make VM_SOFTDIRTY a sticky VMA flag", v2.
Currently we set VM_SOFTDIRTY when a new mapping is set up (whether by establishing a new VMA, or via merge) as implemented in __mmap_complete() and do_brk_flags().
However, when performing a merge of existing mappings such as when performing mprotect(), we may lose the VM_SOFTDIRTY flag.
Now we have the concept of making VMA flags 'sticky', that is that they both don't prevent merge and, importantly, are propagated to merged VMAs, this seems a sensible alternative to the existing special-casing of VM_SOFTDIRTY.
We additionally add a self-test that demonstrates that this logic behaves as expected.
This patch (of 2):
Currently we set VM_SOFTDIRTY when a new mapping is set up (whether by establishing a new VMA, or via merge) as implemented in __mmap_complete() and do_brk_flags().
However, when performing a merge of existing mappings such as when performing mprotect(), we may lose the VM_SOFTDIRTY flag.
This is because currently we simply ignore VM_SOFTDIRTY for the purposes of merge, so one VMA may possess the flag and another not, and whichever happens to be the target VMA will be the one upon which the merge is performed which may or may not have VM_SOFTDIRTY set.
Now we have the concept of 'sticky' VMA flags, let's make VM_SOFTDIRTY one which solves this issue.
Additionally update VMA userland tests to propagate changes.
--- include/linux/mm.h | 15 +++++++-------- tools/testing/vma/vma_internal.h | 18 ++++++------------ 2 files changed, 13 insertions(+), 20 deletions(-)
include/linux/mm.h +7 -8
@@ -515,28 +515,27 @@ extern unsigned int kobjsize(const void* possesses it but the other does not, the merged VMA should nonetheless have * applied to it: * +* VM_SOFTDIRTY - if a VMA is marked soft-dirty, that is has not had its +* references cleared via /proc/$pid/clear_refs, any merged VMA +* should be considered soft-dirty also as it operates at a VMA +* granularity. +* * VM_MAYBE_GUARD - If a VMA may have guard regions in place it implies that * mapped page tables may contain metadata not described by the * VMA and thus any merged VMA may also contain this metadata, * and thus we must make this flag sticky. */ -#define VM_STICKY VM_MAYBE_GUARD +#define VM_STICKY (VM_SOFTDIRTY | VM_MAYBE_GUARD)/* * VMA flags we ignore for the purposes of merge, i.e. one VMA possessing one * of these flags and the other not does not preclude a merge. * -* VM_SOFTDIRTY - Should not prevent from VMA merging, if we match the flags but -* dirty bit -- the caller should mark merged VMA as dirty. If -* dirty bit won't be excluded from comparison, we increase -* pressure on the memory system forcing the kernel to generate -* new VMAs when old one could be extended instead. -* * VM_STICKY - When merging VMAs, VMA flags must match, unless they are * 'sticky'. If any sticky flags exist in either VMA, we simply * set all of them on the merged VMA. */ -#define VM_IGNORE_MERGE (VM_SOFTDIRTY | VM_STICKY) +#define VM_IGNORE_MERGE VM_STICKY/* * Flags which should result in page tables being copied on fork. These are
tools/testing/vma/vma_internal.h +6 -12
@@ -122,28 +122,22 @@ extern unsigned long dac_mmap_min_addr;* possesses it but the other does not, the merged VMA should nonetheless have * applied to it: * -* VM_MAYBE_GUARD - If a VMA may have guard regions in place it implies that -* mapped page tables may contain metadata not described by the -* VMA and thus any merged VMA may also contain this metadata, -* and thus we must make this flag sticky. +* VM_SOFTDIRTY - if a VMA is marked soft-dirty, that is has not had its +* references cleared via /proc/$pid/clear_refs, any merged VMA +* should be considered soft-dirty also as it operates at a VMA +* granularity. */ -#define VM_STICKY VM_MAYBE_GUARD +#define VM_STICKY (VM_SOFTDIRTY | VM_MAYBE_GUARD)/* * VMA flags we ignore for the purposes of merge, i.e. one VMA possessing one * of these flags and the other not does not preclude a merge. * -* VM_SOFTDIRTY - Should not prevent from VMA merging, if we match the flags but -* dirty bit -- the caller should mark merged VMA as dirty. If -* dirty bit won't be excluded from comparison, we increase -* pressure on the memory system forcing the kernel to generate -* new VMAs when old one could be extended instead. -* * VM_STICKY - When merging VMAs, VMA flags must match, unless they are * 'sticky'. If any sticky flags exist in either VMA, we simply * set all of them on the merged VMA. */ -#define VM_IGNORE_MERGE (VM_SOFTDIRTY | VM_STICKY) +#define VM_IGNORE_MERGE VM_STICKY/* * Flags which should result in page tables being copied on fork. These are
Greg Kroah-Hartman
maintainerPATCH 41/60 testing/selftests/mm: add soft-dirty merge self-test
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes <[email protected]>
commit c7ba92bcfea34f6b4afc744c3b65c8f7420fefe0 upstream.
Assert that we correctly merge VMAs containing VM_SOFTDIRTY flags now that we correctly handle these as sticky.
In order to do so, we have to account for the fact the pagemap interface checks soft dirty PTEs and additionally that newly merged VMAs are marked VM_SOFTDIRTY.
We do this by using use unfaulted anon VMAs, establishing one and clearing references on that one, before establishing another and merging the two before checking that soft-dirty is propagated as expected.
We check that this functions correctly with mremap() and mprotect() as sample cases, because VMA merge of adjacent newly mapped VMAs will automatically be made soft-dirty due to existing logic which does so.
We are therefore exercising other means of merging VMAs.
--- tools/testing/selftests/mm/soft-dirty.c | 127 +++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 1 deletion(-)
tools/testing/selftests/mm/soft-dirty.c +126 -1
@@ -184,6 +184,130 @@ static void test_mprotect(int pagemap_fdclose (test_fd); } +static void test_merge (int pagemap_fd,int pagesize) +{ +char *reserved, *map, *map2; + +/* +* Reserve space for tests: +* +* ---padding to --- +* | avoid adj. | +* v merge v +* |---|---|---|---|---| +* | | 1 | 2 | 3 | | +* |---|---|---|---|---| +*/ + reserved =mmap (NULL ,5 * pagesize,PROT_NONE , +MAP_ANON |MAP_PRIVATE ,-1 ,0 ); +if (reserved ==MAP_FAILED ) +ksft_exit_fail_msg ("mmap failed\n" ); +munmap (reserved,4 * pagesize); + +/* +* Establish initial VMA: +* +* S/D +* |---|---|---|---|---| +* | | 1 | | | | +* |---|---|---|---|---| +*/ + map =mmap (&reserved[pagesize], pagesize,PROT_READ |PROT_WRITE , +MAP_ANON |MAP_PRIVATE |MAP_FIXED ,-1 ,0 ); +if (map ==MAP_FAILED ) +ksft_exit_fail_msg ("mmap failed\n" ); + +/* This will clear VM_SOFTDIRTY too. */ +clear_softdirty (); + +/* +* Now place a new mapping which will be marked VM_SOFTDIRTY. Away from +* map: +* +* - S/D +* |---|---|---|---|---| +* | | 1 | | 2 | | +* |---|---|---|---|---| +*/ + map2 =mmap (&reserved[3 * pagesize], pagesize,PROT_READ |PROT_WRITE , +MAP_ANON |MAP_PRIVATE |MAP_FIXED ,-1 ,0 ); +if (map2 ==MAP_FAILED ) +ksft_exit_fail_msg ("mmap failed\n" ); + +/* +* Now remap it immediately adjacent to map, if the merge correctly +* propagates VM_SOFTDIRTY, we should then observe the VMA as a whole +* being marked soft-dirty: +* +* merge +* S/D +* |---|-------|---|---| +* | | 1 | | | +* |---|-------|---|---| +*/ + map2 =mremap (map2, pagesize, pagesize,MREMAP_FIXED |MREMAP_MAYMOVE , + &reserved[2 * pagesize]); +if (map2 ==MAP_FAILED ) +ksft_exit_fail_msg ("mremap failed\n" ); +ksft_test_result (pagemap_is_softdirty (pagemap_fd, map) ==1 , +"Test %s-anon soft-dirty after remap merge 1st pg\n" , + __func__); +ksft_test_result (pagemap_is_softdirty (pagemap_fd, map2) ==1 , +"Test %s-anon soft-dirty after remap merge 2nd pg\n" , + __func__); + +munmap (map,2 * pagesize); + +/* +* Now establish another VMA: +* +* S/D +* |---|---|---|---|---| +* | | 1 | | | | +* |---|---|---|---|---| +*/ + map =mmap (&reserved[pagesize], pagesize,PROT_READ |PROT_WRITE , +MAP_ANON |MAP_PRIVATE |MAP_FIXED ,-1 ,0 ); +if (map ==MAP_FAILED ) +ksft_exit_fail_msg ("mmap failed\n" ); + +/* Clear VM_SOFTDIRTY... */ +clear_softdirty (); +/* ...and establish incompatible adjacent VMA: +* +* - S/D +* |---|---|---|---|---| +* | | 1 | 2 | | | +* |---|---|---|---|---| +*/ + map2 =mmap (&reserved[2 * pagesize], pagesize, +PROT_READ |PROT_WRITE |PROT_EXEC , +MAP_ANON |MAP_PRIVATE |MAP_FIXED ,-1 ,0 ); +if (map2 ==MAP_FAILED ) +ksft_exit_fail_msg ("mmap failed\n" ); + +/* +* Now mprotect() VMA 1 so it's compatible with 2 and therefore merges: +* +* merge +* S/D +* |---|-------|---|---| +* | | 1 | | | +* |---|-------|---|---| +*/ +if (mprotect (map, pagesize,PROT_READ |PROT_WRITE |PROT_EXEC )) +ksft_exit_fail_msg ("mprotect failed\n" ); + +ksft_test_result (pagemap_is_softdirty (pagemap_fd, map) ==1 , +"Test %s-anon soft-dirty after mprotect merge 1st pg\n" , + __func__); +ksft_test_result (pagemap_is_softdirty (pagemap_fd, map2) ==1 , +"Test %s-anon soft-dirty after mprotect merge 2nd pg\n" , + __func__); + +munmap (map,2 * pagesize); +} +static void test_mprotect_anon (int pagemap_fd,int pagesize) {test_mprotect (pagemap_fd, pagesize, true);
@@ -204,7 +328,7 @@ int main(int argc, char **argv)if (!softdirty_supported ())ksft_exit_skip ("soft-dirty is not support\n" ); -ksft_set_plan (15 ); +ksft_set_plan (19 ); pagemap_fd =open (PAGEMAP_FILE_PATH ,O_RDONLY );if (pagemap_fd <0 )ksft_exit_fail_msg ("Failed to open %s\n" ,PAGEMAP_FILE_PATH );
@@ -216,6 +340,7 @@ int main(int argc, char **argv)test_hugepage (pagemap_fd, pagesize);test_mprotect_anon (pagemap_fd, pagesize);test_mprotect_file (pagemap_fd, pagesize); +test_merge (pagemap_fd, pagesize);close (pagemap_fd);
Greg Kroah-Hartman
maintainerPATCH 42/60 net: export netif_open for self_test usage
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mike Marciniszyn (Meta) <[email protected]>
commit 3fdd33697c2be9184668c89ba4f24a5ecbc8ec51 upstream.
dev_open() already is exported, but drivers which use the netdev instance lock need to use netif_open() instead. netif_close() is also already exported [1] so this completes the pairing.
This export is required for the following fbnic self tests to avoid calling ndo_stop() and ndo_open() in favor of the more appropriate netif_open() and netif_close() that notifies any listeners that the interface went down to test and is now coming back up.
--- net/core/dev.c | 1 + 1 file changed, 1 insertion(+)
net/core/dev.c +1 -0
@@ -1724,6 +1724,7 @@ int netif_open(struct net_device *dev, sreturn ret; } +EXPORT_SYMBOL (netif_open);static void __dev_close_many (struct list_head *head) {
Greg Kroah-Hartman
maintainerPATCH 43/60 net: net_failover: Fix the deadlock in slave register
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Faicker Mo <[email protected]>
commit b84c5632c7b31f8910167075a8128cfb9e50fcfe upstream.
There is netdev_lock_ops() before the NETDEV_REGISTER notifier in register_netdevice(), so use the non-locking functions
in net_failover_slave_register().failover_slave_register() in failover_existing_slave_register() adds lock and unlock ops too.
Call Trace: <TASK> __schedule+0x30d/0x7a0 schedule+0x27/0x90 schedule_preempt_disabled+0x15/0x30 __mutex_lock.constprop.0+0x538/0x9e0 __mutex_lock_slowpath+0x13/0x20 mutex_lock+0x3b/0x50 dev_set_mtu+0x40/0xe0 net_failover_slave_register+0x24/0x280 failover_slave_register+0x103/0x1b0 failover_event+0x15e/0x210 ? dropmon_net_event+0xac/0xe0 notifier_call_chain+0x5e/0xe0 raw_notifier_call_chain+0x16/0x30 call_netdevice_notifiers_info+0x52/0xa0 register_netdevice+0x5f4/0x7c0 register_netdev+0x1e/0x40 _mlx5e_probe+0xe2/0x370 [mlx5_core] mlx5e_probe+0x59/0x70 [mlx5_core] ? __pfx_mlx5e_probe+0x10/0x10 [mlx5_core]
--- drivers/net/net_failover.c | 12 ++++++------ net/core/failover.c | 6 +++++- 2 files changed, 11 insertions(+), 7 deletions(-)
drivers/net/net_failover.c +6 -6
@@ -502,7 +502,7 @@ static int net_failover_slave_register(s/* Align MTU of slave with failover dev */ orig_mtu = slave_dev->mtu; - err =dev_set_mtu (slave_dev, failover_dev->mtu); + err =netif_set_mtu (slave_dev, failover_dev->mtu);if (err) {netdev_err (failover_dev,"unable to change mtu of %s to %u register failed\n" , slave_dev->name, failover_dev->mtu);
@@ -512,11 +512,11 @@ static int net_failover_slave_register(sdev_hold (slave_dev);if (netif_running (failover_dev)) { - err =dev_open (slave_dev,NULL ); + err =netif_open (slave_dev,NULL );if (err && (err != -EBUSY )) {netdev_err (failover_dev,"Opening slave %s failed err:%d\n" , slave_dev->name, err); - goto err_dev_open; + goto err_netif_open; } }
@@ -562,10 +562,10 @@ static int net_failover_slave_register(s err_vlan_add:dev_uc_unsync (slave_dev, failover_dev);dev_mc_unsync (slave_dev, failover_dev); -dev_close (slave_dev); -err_dev_open: +netif_close (slave_dev); +err_netif_open:dev_put (slave_dev); -dev_set_mtu (slave_dev, orig_mtu); +netif_set_mtu (slave_dev, orig_mtu); done:return err; }
net/core/failover.c +5 -1
@@ -12,6 +12,7 @@#include <uapi/linux/if_arp.h> #include <linux/rtnetlink.h> #include <linux/if_vlan.h> +#include <net/netdev_lock.h> #include <net/failover.h> static LIST_HEAD (failover_list);
@@ -221,8 +222,11 @@ failover_existing_slave_register(structfor_each_netdev (net, dev) {if (netif_is_failover (dev))continue ; -if (ether_addr_equal (failover_dev->perm_addr, dev->perm_addr)) +if (ether_addr_equal (failover_dev->perm_addr, dev->perm_addr)) { +netdev_lock_ops (dev);failover_slave_register (dev); +netdev_unlock_ops (dev); + } }rtnl_unlock (); }
Greg Kroah-Hartman
maintainerPATCH 44/60 iio: light: veml6075: add bounds check to veml6075_it_ms index
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sam Daly <[email protected]>
commit 307dc4240bd41852d9e0912921e298160db1c109 upstream.
veml6075_it_ms has 5 elements but VEML6075_CONF_IT can yield values 0-7. If it returns a value >= 5, this causes an out-of-bounds array access. Add a bounds check and return -EINVAL if the index is out of range.
The problem values are reserved so should never be read from the register. Hence this is hardening against fault device, missprogramming or bus corruption.
--- drivers/iio/light/veml6075.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/iio/light/veml6075.c +6 -2
@@ -100,7 +100,7 @@ static const struct iio_chan_spec veml60static int veml6075_request_measurement (struct veml6075_data *data) { -int ret, conf, int_time; +int ret, conf, int_time, int_index; ret =regmap_read (data->regmap,VEML6075_CMD_CONF , &conf);if (ret <0 )
@@ -117,7 +117,11 @@ static int veml6075_request_measurement(* time for all possible configurations. Using a 1.50 factor simplifies * operations and ensures reliability under all circumstances. */ - int_time = veml6075_it_ms[FIELD_GET (VEML6075_CONF_IT , conf)]; + int_index =FIELD_GET (VEML6075_CONF_IT , conf); +if (int_index >=ARRAY_SIZE (veml6075_it_ms)) +return -EINVAL ; + + int_time = veml6075_it_ms[int_index];msleep (int_time + (int_time /2 ));/* shutdown again, data registers are still accessible */
Greg Kroah-Hartman
maintainerPATCH 45/60 iio: adc: ti-ads1298: add bounds check to pga_settings index
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sam Daly <[email protected]>
commit 95e8a48d7a85d4226934020e57815a3316d3a14b upstream.
ads1298_pga_settings has 7 elements but ADS1298_MASK_CH_PGA can yield values 0-7. If it yields a value >= 7, this causes an out-of-bounds array access. Add a bounds check and return -EINVAL if the index is out of range.
Note that the remaining value b111 is reserved so should not be seen in a correctly functioning system.
--- drivers/iio/adc/ti-ads1298.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/iio/adc/ti-ads1298.c +6 -1
@@ -279,6 +279,7 @@ static const u8 ads1298_pga_settings[] =static int ads1298_get_scale (struct ads1298_private *priv,int channel,int *val,int *val2) { +unsigned int pga_idx;int ret;unsigned int regval;u8 gain;
@@ -302,7 +303,11 @@ static int ads1298_get_scale(struct ads1if (ret)return ret; - gain = ads1298_pga_settings[FIELD_GET (ADS1298_MASK_CH_PGA , regval)]; + pga_idx =FIELD_GET (ADS1298_MASK_CH_PGA , regval); +if (pga_idx >=ARRAY_SIZE (ads1298_pga_settings)) +return -EINVAL ; + + gain = ads1298_pga_settings[pga_idx]; *val /= gain;/* Full scale is VREF / gain */ *val2 =ADS1298_BITS_PER_SAMPLE -1 ;/* Signed, hence the -1 */
Greg Kroah-Hartman
maintainerPATCH 46/60 Input: rmi4 - fix register descriptor address calculation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit a98518e72439fd42cbfe641c2896543cb088e3d1 upstream.
When reading the register descriptor, the base address is incremented by 1 to read the presence register block. However, after reading the presence register block, the address is incorrectly incremented by only 1 byte (++addr) instead of the actual size of the presence block (size_presence_reg). This causes the subsequent structure block read to read from the wrong memory location if the presence block is larger than 1 byte.
Fix this by advancing the address by size_presence_reg.
--- drivers/input/rmi4/rmi_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/input/rmi4/rmi_driver.c +1 -1
@@ -594,7 +594,7 @@ int rmi_read_register_desc(struct rmi_de ret =rmi_read_block (d, addr, buf, size_presence_reg);if (ret)return ret; - ++addr; + addr += size_presence_reg;if (buf[0 ] ==0 ) { presense_offset =3 ;
Greg Kroah-Hartman
maintainerPATCH 47/60 Input: rmi4 - refactor register descriptor parsing
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit 0adb483fbf2dc43c875cd7550a58b41e92efc52d upstream.
Factor out parsing a register descriptor item from rmi_read_register_desc() and ensure there are no out-of-bounds accesses.
Use get_unaligned_le16() and get_unaligned_le32() for reading multi-byte values.
--- drivers/input/rmi4/rmi_driver.c | 124 ++++++++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 48 deletions(-)
drivers/input/rmi4/rmi_driver.c +76 -48
@@ -22,6 +22,7 @@#include <uapi/linux/input.h> #include <linux/rmi.h> #include <linux/export.h> +#include <linux/unaligned.h> #include "rmi_bus.h" #include "rmi_driver.h"
@@ -558,30 +559,74 @@ int rmi_scan_pdt(struct rmi_device *rmi_return retval <0 ? retval :0 ; } +static int rmi_parse_register_desc_item (struct rmi_register_desc_item *item, +const u8 *buf,size_t size) +{ +unsigned int offset =0 ; +unsigned int map_offset =0 ; +int b; + +if (offset >= size) +return -EIO ; + + item->reg_size = buf[offset++]; +if (item->reg_size ==0 ) { +if (size - offset <2 ) +return -EIO ; + item->reg_size =get_unaligned_le16 (&buf[offset]); + offset +=2 ; + } + +if (item->reg_size ==0 ) { +if (size - offset <4 ) +return -EIO ; + item->reg_size =get_unaligned_le32 (&buf[offset]); + offset +=4 ; + } + +do { +if (offset >= size) +return -EIO ; + +for (b =0 ; b <7 ; b++) { +if (buf[offset] &BIT (b)) { +if (map_offset >=RMI_REG_DESC_SUBPACKET_BITS ) +return -EIO ; +__set_bit (map_offset, item->subpacket_map); + } + ++map_offset; + } + }while (buf[offset++] &BIT (7 )); + + item->num_subpackets =bitmap_weight (item->subpacket_map, +RMI_REG_DESC_SUBPACKET_BITS ); + +return offset; +} +int rmi_read_register_desc (struct rmi_device *d,u16 addr, -struct rmi_register_descriptor *rdesc) +struct rmi_register_descriptor *rdesc) {int ret;u8 size_presence_reg;u8 buf[35 ]; -int presense_offset =1 ; -u8 *struct_buf; -int reg; -int offset =0 ; -int map_offset =0 ; +unsigned int presence_offset; +unsigned int map_offset; +unsigned int offset; +unsigned int reg;int i;int b;/* * The first register of the register descriptor is the size of -* the register descriptor's presense register. +* the register descriptor's presence register. */ ret =rmi_read (d, addr, &size_presence_reg);if (ret)return ret; ++addr; -if (size_presence_reg <0 || size_presence_reg >35 ) +if (size_presence_reg <1 || size_presence_reg >35 )return -EIO ;memset (buf,0 ,sizeof (buf));
@@ -597,16 +642,23 @@ int rmi_read_register_desc(struct rmi_de addr += size_presence_reg;if (buf[0 ] ==0 ) { - presense_offset =3 ; - rdesc->struct_size = buf[1 ] | (buf[2 ] <<8 ); +if (size_presence_reg <3 ) +return -EIO ; + presence_offset =3 ; + rdesc->struct_size =get_unaligned_le16 (&buf[1 ]); }else { + presence_offset =1 ; rdesc->struct_size = buf[0 ]; } -for (i = presense_offset; i < size_presence_reg; i++) { + map_offset =0 ; +for (i = presence_offset; i < size_presence_reg; i++) {for (b =0 ; b <8 ; b++) { -if (buf[i] & (0x1 << b)) +if (buf[i] &BIT (b)) { +if (map_offset >=RMI_REG_DESC_PRESENSE_BITS ) +return -EIO ;bitmap_set (rdesc->presense_map, map_offset,1 ); + } ++map_offset; } }
@@ -626,7 +678,7 @@ int rmi_read_register_desc(struct rmi_de* I'm not using devm_kzalloc here since it will not be retained * after exiting this function */ - struct_buf =kzalloc (rdesc->struct_size,GFP_KERNEL ); + u8 *struct_buf __free(kfree) = kzalloc(rdesc->struct_size, GFP_KERNEL);if (!struct_buf)return -ENOMEM ;
@@ -638,56 +690,32 @@ int rmi_read_register_desc(struct rmi_de*/ ret =rmi_read_block (d, addr, struct_buf, rdesc->struct_size);if (ret) - goto free_struct_buff; +return ret; reg =find_first_bit (rdesc->presense_map,RMI_REG_DESC_PRESENSE_BITS ); + offset =0 ;for (i =0 ; i < rdesc->num_registers; i++) {struct rmi_register_desc_item *item = &rdesc->registers[i]; -int reg_size = struct_buf[offset]; +int item_size; - ++offset; -if (reg_size ==0 ) { - reg_size = struct_buf[offset] | - (struct_buf[offset +1 ] <<8 ); - offset +=2 ; - } - -if (reg_size ==0 ) { - reg_size = struct_buf[offset] | - (struct_buf[offset +1 ] <<8 ) | - (struct_buf[offset +2 ] <<16 ) | - (struct_buf[offset +3 ] <<24 ); - offset +=4 ; - } + item_size =rmi_parse_register_desc_item (item, + &struct_buf[offset], + rdesc->struct_size - offset); +if (item_size <0 ) +return item_size; item->reg = reg; - item->reg_size = reg_size; - - map_offset =0 ; - -do { -for (b =0 ; b <7 ; b++) { -if (struct_buf[offset] & (0x1 << b)) -bitmap_set (item->subpacket_map, - map_offset,1 ); - ++map_offset; - } - }while (struct_buf[offset++] &0x80 ); - - item->num_subpackets =bitmap_weight (item->subpacket_map, -RMI_REG_DESC_SUBPACKET_BITS ); + offset += item_size;rmi_dbg (RMI_DEBUG_CORE , &d->dev,"%s: reg: %d reg size: %ld subpackets: %d\n" , __func__, item->reg, item->reg_size, item->num_subpackets); reg =find_next_bit (rdesc->presense_map, -RMI_REG_DESC_PRESENSE_BITS , reg +1 ); +RMI_REG_DESC_PRESENSE_BITS , reg +1 ); } -free_struct_buff: -kfree (struct_buf); -return ret; +return 0 ; } const struct rmi_register_desc_item *rmi_get_register_desc_item(
Greg Kroah-Hartman
maintainerPATCH 48/60 Input: rmi4 - fix type overflow in register counts
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit a0a87e441238e07c5f7e3de133ef77a9d4229f01 upstream.
The number of registers in the RMI4 register descriptor is populated by counting the bits in the presence map using bitmap_weight(). Since the presence map can contain up to 256 bits (RMI_REG_DESC_PRESENSE_BITS), storing this count in a u8 can overflow to 0 if all 256 bits are set.
Change the num_registers field in struct rmi_register_descriptor from u8 to u16 to prevent potential integer overflow and ensure safe processing of devices reporting large descriptors.
--- drivers/input/rmi4/rmi_driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/input/rmi4/rmi_driver.h +1 -1
@@ -65,7 +65,7 @@ struct rmi_register_desc_item {struct rmi_register_descriptor {unsigned long struct_size;unsigned long presense_map[BITS_TO_LONGS (RMI_REG_DESC_PRESENSE_BITS )]; -u8 num_registers; +u16 num_registers;struct rmi_register_desc_item *registers; };
Greg Kroah-Hartman
maintainerPATCH 49/60 Input: rmi4 - fix num_subpackets overflow in register descriptor
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit 2b4b482d5c4c23c668b998a7da985aea0fa4a978 upstream.
RMI_REG_DESC_SUBPACKET_BITS is defined as 296 (37 * BITS_PER_BYTE). This may overflow num_subpackets in struct rmi_register_desc_item which is defined as a u8.
Fix this by changing the type of num_subpackets to u16.
--- drivers/input/rmi4/rmi_driver.h | 2 +- drivers/input/rmi4/rmi_f12.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-)
drivers/input/rmi4/rmi_driver.h +1 -1
@@ -53,7 +53,7 @@ struct pdt_entry {struct rmi_register_desc_item {u16 reg;unsigned long reg_size; -u8 num_subpackets; +u16 num_subpackets;unsigned long subpacket_map[BITS_TO_LONGS (RMI_REG_DESC_SUBPACKET_BITS )]; };
drivers/input/rmi4/rmi_f12.c +7 -0
@@ -467,6 +467,13 @@ static int rmi_f12_probe(struct rmi_func f12->data1 = item; f12->data1_offset = data_offset; data_offset += item->reg_size; + +if (item->num_subpackets >255 ) { +dev_err (&fn->dev,"Too many fingers declared: %d\n" , + item->num_subpackets); +return -EINVAL ; + } + sensor->nbr_fingers = item->num_subpackets; sensor->report_abs =1 ; sensor->attn_size += item->reg_size;
Greg Kroah-Hartman
maintainerPATCH 50/60 Input: rmi4 - fix memory leak in rmi_set_attn_data()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit a55a683a8e2bddb5467baab3e597a93022d4ee05 upstream.
kfifo_put() returns 0 if the FIFO is full. In this case, we must free the memory allocated for the attention data to avoid a leak.
--- drivers/input/rmi4/rmi_driver.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/input/rmi4/rmi_driver.c +5 -1
@@ -183,7 +183,11 @@ void rmi_set_attn_data(struct rmi_device attn_data.size = size; attn_data.data = fifo_data; -kfifo_put (&drvdata->attn_fifo, attn_data); +if (!kfifo_put (&drvdata->attn_fifo, attn_data)) { +dev_warn_ratelimited (&rmi_dev->dev, +"Failed to enqueue attention data, FIFO full\n" ); +kfree (fifo_data); + } }EXPORT_SYMBOL_GPL (rmi_set_attn_data);
Greg Kroah-Hartman
maintainerPATCH 51/60 Input: rmi4 - iterative IRQ handler
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit b6ca982afd0e8fbcbb340092d3c6d3b4a217686c upstream.
The current IRQ handler uses recursion to drain the attention FIFO, which can lead to stack overflow on deep queues. Convert it to a loop.
--- drivers/input/rmi4/rmi_driver.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-)
drivers/input/rmi4/rmi_driver.c +16 -16
@@ -198,24 +198,24 @@ static irqreturn_t rmi_irq_fn(int irq, vstruct rmi4_attn_data attn_data = {0 };int ret, count; - count =kfifo_get (&drvdata->attn_fifo, &attn_data); -if (count) { - *(drvdata->irq_status) = attn_data.irq_status; - drvdata->attn_data = attn_data; - } +do { + count =kfifo_get (&drvdata->attn_fifo, &attn_data); +if (count) { + *drvdata->irq_status = attn_data.irq_status; + drvdata->attn_data = attn_data; + } - ret =rmi_process_interrupt_requests (rmi_dev); -if (ret) -rmi_dbg (RMI_DEBUG_CORE , &rmi_dev->dev, -"Failed to process interrupt request: %d\n" , ret); + ret =rmi_process_interrupt_requests (rmi_dev); +if (ret) +rmi_dbg (RMI_DEBUG_CORE , &rmi_dev->dev, +"Failed to process interrupt request: %d\n" , + ret); -if (count) { -kfree (attn_data.data); - drvdata->attn_data.data =NULL ; - } - -if (!kfifo_is_empty (&drvdata->attn_fifo)) -return rmi_irq_fn (irq, dev_id); +if (count) { +kfree (attn_data.data); + drvdata->attn_data.data =NULL ; + } + }while (!kfifo_is_empty (&drvdata->attn_fifo));return IRQ_HANDLED ; }
Greg Kroah-Hartman
maintainerPATCH 52/60 Input: rmi4 - fix bit count in bitmap_copy()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit f22dbbcbd1f70ed004a7bf8837e0f0c3cc230b78 upstream.
bitmap_copy() takes number of bits, not bytes (or longs). Correct the bit count in rmi_driver_set_irq_bits() and rmi_driver_clear_irq_bits().
--- drivers/input/rmi4/rmi_driver.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
drivers/input/rmi4/rmi_driver.c +3 -4
@@ -388,9 +388,8 @@ static int rmi_driver_set_irq_bits(struc __func__); goto error_unlock; } -bitmap_copy (data->current_irq_mask, data->new_irq_mask, - data->num_of_irq_regs); +bitmap_copy (data->current_irq_mask, data->new_irq_mask, data->irq_count);bitmap_or (data->fn_irq_bits, data->fn_irq_bits, mask, data->irq_count); error_unlock:
@@ -419,8 +418,8 @@ static int rmi_driver_clear_irq_bits(str __func__); goto error_unlock; } -bitmap_copy (data->current_irq_mask, data->new_irq_mask, - data->num_of_irq_regs); + +bitmap_copy (data->current_irq_mask, data->new_irq_mask, data->irq_count); error_unlock:mutex_unlock (&data->irq_mutex);
Greg Kroah-Hartman
maintainerPATCH 53/60 crypto: qat - remove unused character device and IOCTLs
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Giovanni Cabiddu <[email protected]>
commit d237230728c567297f2f98b425d63156ab2ed17f upstream.
The QAT driver exposes a character device (qat_adf_ctl) with IOCTLs for device configuration, start, stop, status query and enumeration. These IOCTLs are not part of any public uAPI header and have no known in-tree or out-of-tree users. Device lifecycle is already managed via sysfs.
The ioctl interface also increases the attack surface and is the subject of a number of bug reports.
Remove the character device, the IOCTL definitions, and the related data structures (adf_dev_status_info, adf_user_cfg_key_val, adf_user_cfg_section, adf_user_cfg_ctl_data). Drop the now-unused adf_cfg_user.h header and strip adf_ctl_drv.c down to the minimal module_init/module_exit hooks for workqueue, AER, and crypto/compression algorithm registration.
Clean up leftover dead code that was only reachable from the removed IOCTL paths: adf_cfg_del_all(), adf_devmgr_verify_id(), adf_devmgr_get_num_dev(), adf_devmgr_get_dev_by_id(), adf_get_vf_real_id() and the unused ADF_CFG macros.
Additionally, drop the entry associated to QAT IOCTLs in ioctl-number.rst.
--- Documentation/userspace-api/ioctl/ioctl-number.rst | 1 drivers/crypto/intel/qat/qat_common/adf_cfg.c | 10 drivers/crypto/intel/qat/qat_common/adf_cfg.h | 1 drivers/crypto/intel/qat/qat_common/adf_cfg_common.h | 32 - drivers/crypto/intel/qat/qat_common/adf_cfg_user.h | 38 - drivers/crypto/intel/qat/qat_common/adf_common_drv.h | 3 drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c | 404 ------------------- drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c | 70 --- 8 files changed, 1 insertion(+), 558 deletions(-) delete mode 100644 drivers/crypto/intel/qat/qat_common/adf_cfg_user.h
Documentation/userspace-api/ioctl/ioctl-number.rst +0 -1
@@ -229,7 +229,6 @@ Code Seq# Include File <mailto:[email protected]> 'a' all linux/atm*.h, linux/sonet.h ATM on linux <http://lrcwww.epfl.ch/> -'a' 00-0F drivers/crypto/qat/qat_common/adf_cfg_common.h conflict! qat driver 'b' 00-FF conflict! bit3 vme host bridge <mailto:[email protected]> 'b' 00-0F linux/dma-buf.h conflict!
drivers/crypto/intel/qat/qat_common/adf_cfg.c +0 -10
@@ -103,16 +103,6 @@ static void adf_cfg_section_del_all(strustatic void adf_cfg_section_del_all_except (struct list_head *head,const char *section_name); -void adf_cfg_del_all (struct adf_accel_dev *accel_dev) -{ -struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg; - -down_write (&dev_cfg_data->lock); -adf_cfg_section_del_all (&dev_cfg_data->sec_list); -up_write (&dev_cfg_data->lock); -clear_bit (ADF_STATUS_CONFIGURED , &accel_dev->status); -} -void adf_cfg_del_all_except (struct adf_accel_dev *accel_dev,const char *section_name) {
drivers/crypto/intel/qat/qat_common/adf_cfg.h +0 -1
@@ -34,7 +34,6 @@ void adf_cfg_dev_remove(struct adf_accelvoid adf_cfg_dev_dbgfs_add (struct adf_accel_dev *accel_dev);void adf_cfg_dev_dbgfs_rm (struct adf_accel_dev *accel_dev);int adf_cfg_section_add (struct adf_accel_dev *accel_dev,const char *name); -void adf_cfg_del_all (struct adf_accel_dev *accel_dev);void adf_cfg_del_all_except (struct adf_accel_dev *accel_dev,const char *section_name); int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev,
drivers/crypto/intel/qat/qat_common/adf_cfg_common.h +0 -32
@@ -4,18 +4,11 @@#define ADF_CFG_COMMON_H_ #include <linux/types.h> -#include <linux/ioctl.h> #define ADF_CFG_MAX_STR_LEN 64#define ADF_CFG_MAX_KEY_LEN_IN_BYTES ADF_CFG_MAX_STR_LEN#define ADF_CFG_MAX_VAL_LEN_IN_BYTES ADF_CFG_MAX_STR_LEN#define ADF_CFG_MAX_SECTION_LEN_IN_BYTES ADF_CFG_MAX_STR_LEN -#define ADF_CFG_BASE_DEC 10 -#define ADF_CFG_BASE_HEX 16 -#define ADF_CFG_ALL_DEVICES 0xFE -#define ADF_CFG_NO_DEVICE 0xFF -#define ADF_CFG_AFFINITY_WHATEVER 0xFF -#define MAX_DEVICE_NAME_SIZE 32#define ADF_MAX_DEVICES (32 * 32)#define ADF_DEVS_ARRAY_SIZE BITS_TO_LONGS(ADF_MAX_DEVICES)
@@ -51,29 +44,4 @@ enum adf_device_type {DEV_420XX , DEV_6XXX, }; - -struct adf_dev_status_info { -enum adf_device_type type; -__u32 accel_id; -__u32 instance_id; -__u8 num_ae; -__u8 num_accel; -__u8 num_logical_accel; -__u8 banks_per_accel; -__u8 state; -__u8 bus; -__u8 dev; -__u8 fun; -char name[MAX_DEVICE_NAME_SIZE ]; -}; - -#define ADF_CTL_IOC_MAGIC 'a' -#define IOCTL_CONFIG_SYS_RESOURCE_PARAMETERS _IOW(ADF_CTL_IOC_MAGIC, 0, \ - struct adf_user_cfg_ctl_data) -#define IOCTL_STOP_ACCEL_DEV _IOW(ADF_CTL_IOC_MAGIC, 1, \ - struct adf_user_cfg_ctl_data) -#define IOCTL_START_ACCEL_DEV _IOW(ADF_CTL_IOC_MAGIC, 2, \ - struct adf_user_cfg_ctl_data) -#define IOCTL_STATUS_ACCEL_DEV _IOW(ADF_CTL_IOC_MAGIC, 3, __u32) -#define IOCTL_GET_NUM_DEVICES _IOW(ADF_CTL_IOC_MAGIC, 4, __s32)#endif
drivers/crypto/intel/qat/qat_common/adf_cfg_user.h deleted +0 -38
@@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */ -/* Copyright(c) 2014 - 2020 Intel Corporation */ -#ifndef ADF_CFG_USER_H_ -#define ADF_CFG_USER_H_ - -#include "adf_cfg_common.h" -#include "adf_cfg_strings.h" - -struct adf_user_cfg_key_val { -char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES ]; -char val[ADF_CFG_MAX_VAL_LEN_IN_BYTES ]; -union { -struct adf_user_cfg_key_val *next; -__u64 padding3; - }; -enum adf_cfg_val_type type; -} __packed; - -struct adf_user_cfg_section { -char name[ADF_CFG_MAX_SECTION_LEN_IN_BYTES ]; -union { -struct adf_user_cfg_key_val *params; -__u64 padding1; - }; -union { -struct adf_user_cfg_section *next; -__u64 padding3; - }; -} __packed; - -struct adf_user_cfg_ctl_data { -union { -struct adf_user_cfg_section *config_section; -__u64 padding; - }; -__u8 device_id; -} __packed; -#endif
drivers/crypto/intel/qat/qat_common/adf_common_drv.h +0 -3
@@ -68,10 +68,7 @@ int adf_devmgr_add_dev(struct adf_accel_void adf_devmgr_rm_dev (struct adf_accel_dev *accel_dev,struct adf_accel_dev *pf);struct list_head *adf_devmgr_get_head (void ); -struct adf_accel_dev *adf_devmgr_get_dev_by_id (u32 id);struct adf_accel_dev *adf_devmgr_pci_to_accel_dev (struct pci_dev *pci_dev); -int adf_devmgr_verify_id (u32 id); -void adf_devmgr_get_num_dev (u32 *num);int adf_devmgr_in_reset (struct adf_accel_dev *accel_dev);int adf_dev_started (struct adf_accel_dev *accel_dev);int adf_dev_restarting_notify (struct adf_accel_dev *accel_dev);
drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c +1 -403
433 lines not on this page · raw message
drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c +0 -70
82 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 54/60 vc_screen: fix null-ptr-deref in vcs_notifier() during concurrent vcs_write
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yi Yang <[email protected]>
commit a287620312dc6dcb9a093417a0e589bf30fcf38a upstream.
A KASAN null-ptr-deref was observed in vcs_notifier():
BUG: KASAN: null-ptr-deref in vcs_notifier+0x98/0x130
Read of size 2 at addr qmp_cmd_name: qmp_capabilities, arguments: {}The issue is a race condition in vcs_write(). When the console_lock is temporarily dropped (to copy data from userspace), the vc_data pointer obtained from vcs_vc() may become stale. After re-acquiring the lock, vcs_vc() is called again to re-validate the pointer. If the vc has been deallocated in the meantime, vcs_vc() returns NULL, and the while loop breaks (with written > 0). However, after the loop, vcs_scr_updated(vc) is still called with the now-NULL vc pointer, leading to a null pointer dereference in the notifier chain (vcs_notifier dereferences param->vc).
Fix this by adding a NULL check for vc before calling vcs_scr_updated().
--- drivers/tty/vt/vc_screen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/tty/vt/vc_screen.c +1 -1
@@ -686,7 +686,7 @@ vcs_write(struct file *file, const char } *ppos += written; ret = written; -if (written) +if (written && vc)vcs_scr_updated (vc);return ret;
Greg Kroah-Hartman
maintainerPATCH 55/60 serial: qcom_geni: Fix RX DMA stall when SE_DMA_RX_LEN_IN is zero
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Viken Dadhaniya <[email protected]>
commit b93062b6d8a1b2d9bad235cac25558a909819026 upstream.
In qcom_geni_serial_handle_rx_dma(), geni_se_rx_dma_unprep() clears port->rx_dma_addr before SE_DMA_RX_LEN_IN is read. If the register is zero, for example when the RX stale counter fires on an idle line, the handler returns without calling geni_se_rx_dma_prep().
The next RX DMA interrupt then hits the !port->rx_dma_addr guard and returns immediately, so the RX DMA buffer is never rearmed and later input is lost.
Keep the handler on the rearm path when rx_in is zero. Warn about the unexpected zero-length DMA completion, skip received-data handling, and always call geni_se_rx_dma_prep().
--- drivers/tty/serial/qcom_geni_serial.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)
drivers/tty/serial/qcom_geni_serial.c +3 -6
@@ -896,12 +896,9 @@ static void qcom_geni_serial_handle_rx_d port->rx_dma_addr =0 ; rx_in =readl (uport->membase +SE_DMA_RX_LEN_IN ); -if (!rx_in) { -dev_warn (uport->dev,"serial engine reports 0 RX bytes in!\n" ); -return ; - } - -if (!drop) +if (!rx_in) +dev_warn_ratelimited (uport->dev,"serial engine reports 0 RX bytes in!\n" ); +else if (!drop)handle_rx_uart (uport, rx_in); ret = geni_se_rx_dma_prep(&port->se, port->rx_buf,
Greg Kroah-Hartman
maintainerPATCH 56/60 drivers/base/memory: set mem->altmap after successful device registration
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Georgi Djakov <[email protected]>
commit a2b8d7827f48ee54a686cb80e4a1d0ff954ec42a upstream.
If __add_memory_block() fails at xa_store() (under memory pressure for example), device_unregister() is called, which eventually triggers memory_block_release() with mem->altmap still set, causing a WARN_ON(mem->altmap). This was triggered by modifying virtio-mem driver.
Fix this by delaying the assignment of mem->altmap until after __add_memory_block() has succeeded.
--- drivers/base/memory.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/base/memory.c +2 -1
@@ -828,7 +828,6 @@ static int add_memory_block(unsigned lon mem->start_section_nr = block_id * sections_per_block; mem->state = state; mem->nid = nid; - mem->altmap = altmap;INIT_LIST_HEAD (&mem->group_next);#ifndef CONFIG_NUMA
@@ -846,6 +845,8 @@ static int add_memory_block(unsigned lonif (ret)return ret; + mem->altmap = altmap; +if (group) { mem->group = group;list_add (&mem->group_next, &group->memory_blocks);
Greg Kroah-Hartman
maintainerPATCH 57/60 ksmbd: reject non-VALID session in compound request branch
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gil Portnoy <[email protected]>
commit 609ca17d869d04ba249e32cdcbf13c0b1c66f43c upstream.
smb2_check_user_session() takes a shortcut for any operation that is not the first in a COMPOUND request: it reuses work->sess (the session bound by the first operation) and validates only the SessionId, then returns "valid". It never re-checks work->sess->state == SMB2_SESSION_VALID, and a SessionId of 0xFFFFFFFFFFFFFFFF (ULLONG_MAX, the MS-SMB2 related-operation value) skips even the id comparison. The standalone path (ksmbd_session_lookup_all() plus the SESSION_SETUP state machine) does enforce the VALID state; the compound branch bypasses all of it.
A SESSION_SETUP carrying only an NTLM Type-1 (NtLmNegotiate) blob publishes a fresh SMB2_SESSION_IN_PROGRESS session whose sess->user is still NULL (->user is assigned later, by ntlm_authenticate()). Used as operation 1 of a COMPOUND with operation 2 = TREE_CONNECT (related, SessionId=ULLONG_MAX, \\host\IPC$), the tree-connect then runs on that IN_PROGRESS session and reaches ksmbd_ipc_tree_connect_request(), which dereferences user_name(sess->user) with sess->user == NULL (transport_ipc.c:687/701/704) -> remote NULL-pointer dereference and a kernel Oops that wedges the ksmbd worker for all clients.
Reject any non-first compound operation that lands on a session which is not SMB2_SESSION_VALID, mirroring the validity the standalone lookup path enforces. SESSION_SETUP itself legitimately runs on an IN_PROGRESS session, but it is never carried as a non-first compound operation, so multi-leg authentication is unaffected by this check.
--- fs/smb/server/smb2pdu.c | 5 +++++ 1 file changed, 5 insertions(+)
fs/smb/server/smb2pdu.c +5 -0
@@ -613,6 +613,11 @@ int smb2_check_user_session(struct ksmbd sess_id, work->sess->id);return -EINVAL ; } +if (work->sess->state !=SMB2_SESSION_VALID ) { +pr_err ("compound request on a non-valid session (state %d)\n" , + work->sess->state); +return -EINVAL ; + }return 1 ; }
Greg Kroah-Hartman
maintainerPATCH 58/60 media: vidtv: fix NULL pointer dereference in vidtv_mux_push_si
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ruslan Valiyev <[email protected]>
commit 7d8bf3d8f91073f4db347ed3aa6302b56107499c upstream.
syzbot reported a general protection fault invidtv_psi_ts_psi_write_into [1].
vidtv_mux_get_pid_ctx() can return NULL, but vidtv_mux_push_si() does not check for this before dereferencing the returned pointer to access the continuity counter. This leads to a general protection fault when accessing a near-NULL address.
The root cause is that vidtv_mux_pid_ctx_init() does not check the return value of vidtv_mux_create_pid_ctx_once() for PMT section PIDs. If the allocation fails, the PID context is never created, but init returns success. The subsequent vidtv_mux_push_si() call then gets NULL from vidtv_mux_get_pid_ctx() and crashes.
Fix both the root cause (add error check in vidtv_mux_pid_ctx_init for PMT PIDs) and add defensive NULL checks in vidtv_mux_push_si for all vidtv_mux_get_pid_ctx() calls.
[1]
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN PTI KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] Workqueue: events vidtv_mux_tick RIP: 0010:vidtv_psi_ts_psi_write_into+0x54a/0xbc0 drivers/media/test-drivers/vidtv/vidtv_psi.c:197 Call Trace: <TASK> vidtv_psi_table_header_write_into drivers/media/test-drivers/vidtv/vidtv_psi.c:799 [inline] vidtv_psi_pmt_write_into+0x3b2/0xa70 drivers/media/test-drivers/vidtv/vidtv_psi.c:1231 vidtv_mux_push_si+0x932/0xe80 drivers/media/test-drivers/vidtv/vidtv_mux.c:196 vidtv_mux_tick+0xe9b/0x1480 drivers/media/test-drivers/vidtv/vidtv_mux.c:408
--- drivers/media/test-drivers/vidtv/vidtv_mux.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/media/test-drivers/vidtv/vidtv_mux.c +7 -1
@@ -101,7 +101,8 @@ static int vidtv_mux_pid_ctx_init(struct/* add a ctx for all PMT sections */ while (p) { pid =vidtv_psi_get_pat_program_pid (p); -vidtv_mux_create_pid_ctx_once (m, pid); +if (!vidtv_mux_create_pid_ctx_once (m, pid)) + goto free; p = p->next; }
@@ -170,6 +171,9 @@ static u32 vidtv_mux_push_si(struct vidt nit_ctx =vidtv_mux_get_pid_ctx (m,VIDTV_NIT_PID ); eit_ctx =vidtv_mux_get_pid_ctx (m,VIDTV_EIT_PID ); +if (!pat_ctx || !sdt_ctx || !nit_ctx || !eit_ctx) +return 0 ; + pat_args.offset = m->mux_buf_offset; pat_args.continuity_counter = &pat_ctx->cc;
@@ -186,6 +190,8 @@ static u32 vidtv_mux_push_si(struct vidt } pmt_ctx =vidtv_mux_get_pid_ctx (m, pmt_pid); +if (!pmt_ctx) +continue ; pmt_args.offset = m->mux_buf_offset; pmt_args.pmt = m->si.pmt_secs[i];
Greg Kroah-Hartman
maintainerPATCH 59/60 virtiofs: fix UAF on submount umount
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Miklos Szeredi <[email protected]>
commit 06b41351779e9289e8785694ade9042ae85e41ea upstream.
iput() called from fuse_release_end() can Oops if the super block has already been destroyed. Normally this is prevented by waiting for num_waiting to go down to zero before commencing with super block shutdown.
This only works, however, for the last submount instance, as the wait counter is per connection, not per superblock.
Revert to using synchronous release requests for the auto_submounts case, which is virtiofs only at this time.
--- fs/fuse/file.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
fs/fuse/file.c +7 -1
@@ -374,8 +374,14 @@ void fuse_file_release(struct inode *ino* aio and closes the fd before the aio completes. Since aio takes its * own ref to the file, the IO completion has to drop the ref, which is * how the fuse server can end up closing its clients' files. +* +* Exception is virtio-fs, which is not affected by the above (server is +* on host, cannot close open files in guest). Virtio-fs needs sync +* release, because the num_waiting mechanism to wait for all requests +* before commencing with fs shutdown doesn't work if submounts are +* used. */ -fuse_file_put (ff, false); +fuse_file_put (ff, ff->fm->fc->auto_submounts); }void fuse_release_common (struct file *file,bool isdir)
Greg Kroah-Hartman
maintainerPATCH 60/60 mm: do not copy page tables unnecessarily for VM_UFFD_WP
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes <[email protected]>
commit 35e247032606f06c2f19d90a6562bc315206b7a7 upstream.
Commit ab04b530e7e8 ("mm: introduce copy-on-fork VMAs and make VM_MAYBE_GUARD one") aggregates flags checks in vma_needs_copy(), including VM_UFFD_WP.
However in doing so, it incorrectly performed this check against src_vma. This check was done on the assumption that all relevant flags are copied upon fork.
However the userfaultfd logic is very innovative in that it implements custom logic on fork in dup_userfaultfd(), including a rather well hidden case where lacking UFFD_FEATURE_EVENT_FORK causes VM_UFFD_WP to not be propagated to the destination VMA.
And indeed, vma_needs_copy(), prior to this patch, did check this property on dst_vma, not src_vma.
Since all the other relevant flags are copied on fork, we can simply fix this by checking against dst_vma.
While we're here, we fix a comment against VM_COPY_ON_FORK (noting that it did indeed already reference dst_vma) to make it abundantly clear that we must check against the destination VMA.
--- include/linux/mm.h | 6 +++++- mm/memory.c | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-)
include/linux/mm.h +5 -1
@@ -540,7 +540,11 @@ extern unsigned int kobjsize(const void/* * Flags which should result in page tables being copied on fork. These are * flags which indicate that the VMA maps page tables which cannot be -* reconsistuted upon page fault, so necessitate page table copying upon +* reconsistuted upon page fault, so necessitate page table copying upon fork. +* +* Note that these flags should be compared with the DESTINATION VMA not the +* source, as VM_UFFD_WP may not be propagated to destination, while all other +* flags will be. * * VM_PFNMAP / VM_MIXEDMAP - These contain kernel-mapped data which cannot be * reasonably reconstructed on page fault.
mm/memory.c +5 -1
@@ -1479,7 +1479,11 @@ copy_p4d_range(struct vm_area_struct *dsstatic bool vma_needs_copy (struct vm_area_struct *dst_vma,struct vm_area_struct *src_vma) { -if (src_vma->vm_flags &VM_COPY_ON_FORK ) +/* +* We check against dst_vma as while sane VMA flags will have been +* copied, VM_UFFD_WP may be set only on dst_vma. +*/ +if (dst_vma->vm_flags &VM_COPY_ON_FORK )return true;/* * The presence of an anon_vma indicates an anonymous VMA has page
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.37 release. There are 60 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 Sat, 27 Jun 2026 12:54:50 +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.18.37-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.18.y
and the diffstat can be found below.thanks,
greg k-h
On ARCH_BRCMSTB using 32-bit and 64-bit ARM kernels, build tested on BMIPS_GENERIC:
Signature · 1 line
Florian
# Librecast Test Results
020/020 [ OK ] liblcrq
010/010 [ OK ] libmld
120/120 [ OK ] liblibrecast
CPU/kernel: Linux auntie 6.18.37-rc1-g66e1cbcc83d0 #1 SMP PREEMPT_DYNAMIC Thu Jun 25 15:04:20 -00 2026 x86_64 AMD Ryzen 9 9950X 16-Core Processor AuthenticAMD GNU/Linux
Am 25.06.2026 um 15:02 schrieb Greg Kroah-Hartman:
Quoted 4 lines
This is the start of the stable review cycle for the 6.18.37 release. There are 60 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 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.37 release. There are 60 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 Sat, 27 Jun 2026 12:54:50 +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.18.37-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.18.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.18.37 release. There are 60 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 Sat, 27 Jun 2026 12:54:50 +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.18.37-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.18.y
and the diffstat can be found below.thanks,
greg k-h
Built and booted successfully on RISC-V RV64 (HiFive Unmatched).
Quoted 8 lines · On Thu, 25 Jun 2026 14:02:45 +0100 Greg Kroah-Hartman…
This is the start of the stable review cycle for the 6.18.37 release. There are 60 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 Sat, 27 Jun 2026 12:54:50 +0000. Anything received after that time might be too late.
Boot-tested under QEMU for Rust x86_64, arm64 and riscv64; built-tested for loongarch64 and arm32:
Thanks!
Cheers,
Miguel
Quoted 4 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.37 release. There are 60 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.
Quoted 7 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.37 release. There are 60 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 Sat, 27 Jun 2026 12:54:50 +0000. Anything received after that time might be too late.
test_progs, test_progs-no_alu32, test_progs-cpuv4, test_maps, test_verifier in BPF selftests all passes[1] on both x86_64 and aarch64.
1: https://github.com/kernel-patches/linux-stable/actions/runs/28642657596
[...]
Quoted 7 lines · Shung-Hsi Yu
Quoted 5 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.37 release. There are 60 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.
Quoted 5 lines
Quoted 2 lines
Responses should be made by Sat, 27 Jun 2026 12:54:50 +0000. Anything received after that time might be too late.
test_progs, test_progs-no_alu32, test_progs-cpuv4,test_maps,test_verifierin BPF selftests all passes[1] on bothx86_64and aarch64.
Oops, meant to reply against the 6.18.38-rc1 review. Sorry for the noise.