7.2.4-rc1 review
Subsystems (2)
Greg Kroah-Hartman
maintainerPATCH 361/713 md/raid10: fix still_degraded being inverted in raid10_sync_request()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yunye Zhao <[email protected]>
commit 47f1441b281decde6954a2fa82b4131637d685ac upstream.
Commit fe6a19d40ceb ("md/md-bitmap: merge md_bitmap_start_sync() into bitmap_operations") converted still_degraded from int to bool, but inverted the assignment in the loop that checks whether the array will still be degraded after the current device is recovered:
"still_degraded = 1" became "still_degraded = false".
As a result, recovering a device while another mirror is still missing calls md_bitmap_start_sync() with degraded == false, which clears bitmap bits that the still-missing device needs. When that device is re-added, its bitmap-based recovery finds the bits already cleared and skips every region written while the array was degraded, so it is marked In_sync while holding stale data: silent corruption.
Reproducer (raid10 near=2, 4 disks, internal bitmap):
- fail and remove one disk of each mirror pair
- write to the degraded array
- re-add both disks and let recovery finish
- "check" reports mismatch_cnt=262272 after 256 MiB of degraded writes and file contents differ; the second disk's "recovery" completes in milliseconds because everything is skipped
The same conversion in raid1 got it right (still_degraded = true). Restore the correct value.
--- drivers/md/raid10.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/md/raid10.c +1 -1
@@ -3365,7 +3365,7 @@ static sector_t raid10_sync_request(strustruct md_rdev *rdev = conf->mirrors[j].rdev;if (rdev ==NULL ||test_bit (Faulty, &rdev->flags)) { - still_degraded = false; + still_degraded = true;break ; } }
Greg Kroah-Hartman
maintainerPATCH 362/713 md: do overflow check for sb->bblog_shift in super_1_load()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Coly Li <[email protected]>
commit 35d522bd32462afcf1981dab6da8a9256c26c1e0 upstream.
In super_1_load(), sb->bblog_shift is an __u8 type value loaded from on- disk superblock. It is used for badblocks API badblocks_set() by the following sequence,
1930 rdev->badblocks.shift = sb->bblog_shift;
1931 for (i = 0 ; i < (sectors << (9-3)) ; i++, bbp++) {
1932 u64 bb = le64_to_cpu(*bbp); 1933 int count = bb & (0x3ff); 1934 u64 sector = bb >> 10; 1935 sector <<= sb->bblog_shift; 1936 count <<= sb->bblog_shift; 1937 if (bb + 1 == 0)
1938 break;
1939 if (!badblocks_set(&rdev->badblocks, sector, count, 1))
1940 return -EINVAL;
1941 }
bb->bblog_shit is in range of 0-255, variable sector is 64bit width, for an invalid bb->bblog_shit, it is possible to make sector be overflowed by the following calculation,
1935 sector <<= sb->bblog_shift;
Then in turn when call badblocks_set() at line 1939 with the invalid rdev->badblocks.shift set at line 1930, may result an overflow inside _badblocks_clear() in block/badblocks.c.
Although there are many places to call badblocks APIs, the non-zero shift value is only used in super_1_load(), other places always use 0 as the shift value. Therefore it is unnecessary to do a general shift value overflow check inside badblock API, and just check here as the caller.
This may avoid unnecessary check, make the badblocks API code more simple and elegant.
--- drivers/md/md.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/md/md.c +7 -0
@@ -1920,6 +1920,13 @@ static int super_1_load(struct md_rdev * rdev->bb_page, REQ_OP_READ, true))return -EIO ; bbp = (__le64 *)page_address (rdev->bb_page); + +/* check for badblocks api. */ +if (sb->bblog_shift >=BITS_PER_TYPE (sector_t)) { +pr_err ("md: %pg: bogus bblog_shift %u for badblocks.\n" , + rdev->bdev, sb->bblog_shift); +return -EINVAL ; + } rdev->badblocks.shift = sb->bblog_shift;for (i =0 ; i < (sectors << (9 -3 )) ; i++, bbp++) {u64 bb =le64_to_cpu (*bbp);
Greg Kroah-Hartman
maintainerPATCH 363/713 module: validate string table section types
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thiébaud Weksteen <[email protected]>
commit 9a5ff45689329835f874cefe5174e577d141d423 upstream.
In elf_validity_cache_sechdrs, section sizes and offsets are validated, unless the section type is SHT_NULL or SHT_NOBITS.
Later, elf_validity_cache_secstrings and elf_validity_cache_index_str access the section name table (.shstrtab) and symbol string table (.strtab) headers without first ensuring that their types are SHT_STRTAB. If a section type is SHT_NULL or SHT_NOBITS, sh_offset has not been validated and may reference out-of-bounds memory when dereferenced in elf_validity_cache_secstrings or elf_validity_cache_strtab.
Validate that both string section headers are of type SHT_STRTAB before caching them.
--- kernel/module/main.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
kernel/module/main.c +13 -1
@@ -2011,6 +2011,7 @@ static int elf_validity_cache_sechdrs(st* Specifically checks: * * * Section name table index is inbounds of section headers +* * Section name table type is SHT_STRTAB * * Section name table is not empty * * Section name table is NUL terminated * * All section name offsets are inbounds of the section
@@ -2038,6 +2039,11 @@ static int elf_validity_cache_secstrings strhdr = &info->sechdrs[info->hdr->e_shstrndx]; +if (strhdr->sh_type !=SHT_STRTAB ) { +pr_err ("Invalid ELF section name table type: %u\n" , strhdr->sh_type); +return -ENOEXEC ; + } +/* * The section name table must be NUL-terminated, as required * by the spec. This makes strcmp and pr_* calls that access
@@ -2204,7 +2210,7 @@ static int elf_validity_cache_index_sym(* Must have &load_info->index.sym populated. * * Looks at the symbol table's associated string table, makes sure it is -* in-bounds, and caches it. +* in-bounds and of type SHT_STRTAB, and caches it. * * Return: %0 if valid, %-ENOEXEC on failure. */
@@ -2218,6 +2224,12 @@ static int elf_validity_cache_index_str(return -ENOEXEC ; } +if (info->sechdrs[str_idx].sh_type !=SHT_STRTAB ) { +pr_err ("Invalid ELF symbol string table type: %u\n" , + info->sechdrs[str_idx].sh_type); +return -ENOEXEC ; + } + info->index.str = str_idx;return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 364/713 mpls: reload header after pskb_may_pull()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qing Ming <[email protected]>
commit 29e63b8d9fc150cc191b1c6eb7e16e1247e1b650 upstream.
mpls_select_multipath() calls mpls_multipath_hash() to choose a nexthop when an MPLS route has multiple nexthops. While walking the MPLS label stack, the hash routine caches hdr for the current label. After finding the bottom-of-stack label, it calls pskb_may_pull() before reading the inner IP header.
If an skb is constructed with the inner IP header in nonlinear data and insufficient tailroom in the linear head, pskb_may_pull() calls pskb_expand_head() to replace the skb head and free the old one. This leaves hdr pointing to freed memory. The IPv6 path can invalidate hdr again when it performs a second pull for the larger header.
The issue was found through static analysis. A reproducer sending a legal Geneve packet through a bareudp/MPLS multipath setup triggered the same KASAN report in 2 of 2 unpatched runs:
BUG: KASAN: slab-use-after-free in mpls_select_multipath Read of size 1 at addr ffff88800ecc6e20 by task ksoftirqd/1/23 Call Trace: mpls_select_multipath mpls_forward __netif_receive_skb_list_core netif_receive_skb_list_internal napi_complete_done gro_cell_poll __napi_poll net_rx_action Freed by task 23: kfree pskb_expand_head __pskb_pull_tail mpls_select_multipath
Reload hdr from the current skb head after each successful pull before deriving the inner IPv4 or IPv6 header pointer.
--- net/mpls/af_mpls.c | 2 ++ 1 file changed, 2 insertions(+)
net/mpls/af_mpls.c +2 -0
@@ -221,6 +221,7 @@ static u32 mpls_multipath_hash(struct mpif (pskb_may_pull (skb, mpls_hdr_len +sizeof (struct iphdr ))) {const struct iphdr *v4hdr; + hdr =mpls_hdr (skb) + label_index; v4hdr = (const struct iphdr *)(hdr +1 );if (v4hdr->version ==4 ) { hash = jhash_3words(ntohl(v4hdr->saddr),
@@ -231,6 +232,7 @@ static u32 mpls_multipath_hash(struct mp sizeof(struct ipv6hdr))) {const struct ipv6hdr *v6hdr; + hdr =mpls_hdr (skb) + label_index; v6hdr = (const struct ipv6hdr *)(hdr +1 ); hash =__ipv6_addr_jhash (&v6hdr->saddr, hash); hash =__ipv6_addr_jhash (&v6hdr->daddr, hash);
Greg Kroah-Hartman
maintainerPATCH 365/713 mptcp: fix uninitialized local_id in syncookie MP_JOIN reconstruction
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harshit Varu <[email protected]>
commit b878dfdd12d7a5b8722a78d35e313506140ca3d9 upstream.
mptcp_token_join_cookie_init_state() restores remote_nonce, local_nonce, backup, join_id, token and msk from the saved cookie entry when rebuilding the request socket for a MP_JOIN 4th-ACK handled under SYN cookies, but it does not restore local_id, even though the SYN path saved it. subflow_ulp_clone() then reads that uninitialized field and stores it as the joined subflow's address-ID. Because the request-sock slab is SLAB_TYPESAFE_BY_RCU and not zeroed on allocation, the value is the stale byte of a previously freed request socket, which an off-path peer can influence by sending concurrent MP_JOIN SYNs. This corrupts the path manager's id-based subflow bookkeeping for the connection.
Restore subflow_req->local_id from the cookie entry, as done for the other fields.
--- net/mptcp/syncookies.c | 1 + 1 file changed, 1 insertion(+)
net/mptcp/syncookies.c +1 -0
@@ -118,6 +118,7 @@ bool mptcp_token_join_cookie_init_state( subflow_req->local_nonce = e->local_nonce; subflow_req->backup = e->backup; subflow_req->remote_id = e->join_id; + subflow_req->local_id = e->local_id; subflow_req->token = e->token; subflow_req->msk = msk;spin_unlock_bh (&join_entry_locks[i]);
Greg Kroah-Hartman
maintainerPATCH 366/713 module/kallsyms: fix nextval for data symbol lookup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stanislaw Gruszka <[email protected]>
commit 0e9f090a4e9bfae5a190ccf89eab3bf14f6b0f96 upstream.
The symbol lookup code assumes the queried address resides in either MOD_TEXT or MOD_INIT_TEXT. This breaks for addresses in other module memory regions (e.g. rodata or data), resulting in incorrect upper bounds and wrong symbol size.
Select the module memory region the address belongs to instead of hardcoding text sections. Also initialize the lower bound to the start of that region, as searching from address 0 is unnecessary.
--- kernel/module/kallsyms.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-)
kernel/module/kallsyms.c +16 -8
@@ -258,17 +258,25 @@ static const char *find_kallsyms_symbol(unsigned int i, best =0 ;unsigned long nextval, bestval;struct mod_kallsyms *kallsyms =rcu_dereference (mod->kallsyms); -struct module_memory *mod_mem; +struct module_memory *mod_mem =NULL ; -/* At worse, next value is at end of module */ -if (within_module_init (addr, mod)) - mod_mem = &mod->mem[MOD_INIT_TEXT ]; -else - mod_mem = &mod->mem[MOD_TEXT ]; +for_each_mod_mem_type (type) { +#ifndef CONFIG_KALLSYMS_ALL +if (!mod_mem_type_is_text (type)) +continue ; +#endif +if (within_module_mem_type (addr, mod, type)) { + mod_mem = &mod->mem[type]; +break ; + } + } - nextval = (unsigned long )mod_mem->base + mod_mem->size; +if (!mod_mem) +return NULL ; - bestval =kallsyms_symbol_value (&kallsyms->symtab[best]); +/* Initialize bounds within memory region the address belongs to. */ + nextval = (unsigned long )mod_mem->base + mod_mem->size; + bestval = (unsigned long )mod_mem->base -1 ;/* * Scan for closest preceding symbol, and next symbol. (ELF
Greg Kroah-Hartman
maintainerPATCH 367/713 nouveau/gem: reserve the bo in the info ioctl around the vma lookup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dave Airlie <[email protected]>
commit 5e17160d41d92823f3379c1982e1369680c5ce4d upstream.
In the non-uvmm path, there could be a race between the info lookup finding the vma, and the gem close path closing the vma leading to a use-after-free.
Spotted with the help of Opus 4.6.
--- drivers/gpu/drm/nouveau/nouveau_gem.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
drivers/gpu/drm/nouveau/nouveau_gem.c +10 -1
@@ -313,11 +313,20 @@ nouveau_gem_info(struct drm_file *file_p rep->offset = nvbo->offset;if (vmm->vmm.object.oclass >=NVIF_CLASS_VMM_NV50 && !nouveau_cli_uvmm (cli)) { +int ret; + + ret =ttm_bo_reserve (&nvbo->bo, false, false,NULL ); +if (ret) +return ret; + vma =nouveau_vma_find (nvbo, vmm); -if (!vma) +if (!vma) { +ttm_bo_unreserve (&nvbo->bo);return -EINVAL ; + } rep->offset = vma->addr; +ttm_bo_unreserve (&nvbo->bo); }else rep->offset =0 ;
Greg Kroah-Hartman
maintainerPATCH 368/713 params: fix charp corruption on allocation failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiacheng Yu <[email protected]>
commit 3dfaae04243cde460d82dfc2a7dd0bb6664d20ae upstream.
param_set_charp() stores charp parameters in allocated memory after slab is available, and releases the previous value when the parameter is updated.
The previous value is released before the replacement allocation succeeds. If kmalloc_parameter() fails, the setter returns -ENOMEM with the parameter left as NULL.
Failing zswap's compressor update before zswap is initialized can later trigger:
BUG: kernel NULL pointer dereference, address: 0000000000000000
RIP: 0010:strcmp+0x10/0x30
Call Trace:
zswap_setup+0x3b1/0x490
zswap_enabled_param_set+0x5b/0xa0
param_attr_store+0x93/0xe0
module_attr_store+0x1c/0x30
kernfs_fop_write_iter+0x116/0x1f0Allocate and copy the replacement first, then replace the parameter value only after allocation succeeds.
--- kernel/params.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
kernel/params.c +8 -6
@@ -261,6 +261,7 @@ EXPORT_SYMBOL_GPL(param_set_uint_minmax)int param_set_charp (const char *val,const struct kernel_param *kp) { +char *tmp;size_t len, maxlen =1024 ; len =strnlen (val, maxlen +1 );
@@ -269,19 +270,20 @@ int param_set_charp(const char *val, conreturn -ENOSPC ; } -maybe_kfree_parameter (*(char **)kp->arg); -/* * This is a hack. We can't kmalloc() in early boot, and we * don't need to; this mangled commandline is preserved. */ if (slab_is_available ()) { - *(char **)kp->arg =kmalloc_parameter (len +1 ); -if (!*(char **)kp->arg) + tmp =kmalloc_parameter (len +1 ); +if (!tmp)return -ENOMEM ; -strcpy (*(char **)kp->arg, val); +memcpy (tmp, val, len +1 ); }else - *(const char **)kp->arg = val; + tmp = (char *)val; + +maybe_kfree_parameter (*(char **)kp->arg); + *(char **)kp->arg = tmp;return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 369/713 phy: fsl-imx8mq-usb: fix typec switch leak on probe error path
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Gu <[email protected]>
commit 87a1805b1c346b092c34f96f3806f207792910e1 upstream.
If probe fails after imx95_usb_phy_get_tca() succeeds, the typec switch leaks because the only cleanup path was in .remove(), which never runs on probe failure.
Use devm_add_action_or_reset() so the switch is cleaned up on both probe failure and driver removal. The imx95_usb_phy_put_tca() is no longer needed, it will be removed in .remove() too.
--- drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-)
drivers/phy/freescale/phy-fsl-imx8mq-usb.c +7 -14
@@ -173,9 +173,9 @@ static struct typec_switch_dev *tca_blk_return sw; } -static void tca_blk_put_typec_switch (struct typec_switch_dev *sw) +static void tca_blk_put_typec_switch (void *data) { -typec_switch_unregister (sw); +typec_switch_unregister (data); } static void tca_blk_orientation_set(struct tca_blk *tca,
@@ -248,6 +248,7 @@ static struct tca_blk *imx95_usb_phy_getstruct device *dev = &pdev->dev;struct resource *res;struct tca_blk *tca; +int ret; res =platform_get_resource (pdev,IORESOURCE_MEM ,1 );if (!res)
@@ -266,17 +267,11 @@ static struct tca_blk *imx95_usb_phy_get tca->orientation =TYPEC_ORIENTATION_NORMAL ; tca->sw =tca_blk_get_typec_switch (pdev, imx_phy); -return tca; -} - -static void imx95_usb_phy_put_tca (struct imx8mq_usb_phy *imx_phy) -{ -struct tca_blk *tca = imx_phy->tca; - -if (!tca) -return ; + ret =devm_add_action_or_reset (&pdev->dev, tca_blk_put_typec_switch, tca->sw); +if (ret) +return ERR_PTR (ret); -tca_blk_put_typec_switch (tca->sw); +return tca; }static u32 phy_tx_vref_tune_from_property (u32 percent)
@@ -741,9 +736,7 @@ static int imx8mq_usb_phy_probe(struct pstatic void imx8mq_usb_phy_remove (struct platform_device *pdev) { -struct imx8mq_usb_phy *imx_phy =platform_get_drvdata (pdev); -imx95_usb_phy_put_tca (imx_phy); }static struct platform_driver imx8mq_usb_phy_driver = {
Greg Kroah-Hartman
maintainerPATCH 370/713 SUNRPC: xdr_buf_trim: clamp buf->len to avoid underflow
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 3f491306dcb673ff5e78e1044ba450c58978774e upstream.
xdr_buf_trim() trims len bytes from the tail of an xdr_buf by walking the tail, pages, and head iovecs. Each per-section step uses min_t() so it never removes more bytes than that section holds, but the final accounting at the fix_len label subtracts the total bytes actually consumed from buf->len without any clamp:
fix_len:
buf->len -= (len - trim);When the caller has set buf->len to a value smaller than the sum of the iov_lens, (len - trim) can exceed buf->len and the unsigned subtraction wraps to near UINT_MAX. gss_krb5_unwrap_v2() reaches xdr_buf_trim() in exactly that state:
buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);
xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip);buf->len is a small wire-derived value while the iov_lens are at page scale, so the per-section loops legitimately consume far more bytes than buf->len records. The wrapped buf->len then propagates as the authoritative stream bound into every downstream XDR decoder.
Fix by clamping the decrement so buf->len bottoms out at zero:
buf->len -= min_t(unsigned int, buf->len, len - trim);
On the normal path where the iov_lens sum to buf->len, (len - trim) is always <= buf->len and the result is identical to before. No callers change behavior outside the underflow case.
--- net/sunrpc/xdr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sunrpc/xdr.c +1 -1
@@ -2049,7 +2049,7 @@ void xdr_buf_trim(struct xdr_buf *buf, u trim -= cur; } fix_len: - buf->len -= (len - trim); + buf->len -= min_t(unsigned int, buf->len, len - trim); }EXPORT_SYMBOL_GPL (xdr_buf_trim);
Greg Kroah-Hartman
maintainerPATCH 371/713 SUNRPC: Zero rpc_gss_wire_cred at svcauth_gss_decode_credbody() entry
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 11539e8fcce0b0af062ae5fecf7b3676c2f7aeed upstream.
svcauth_gss_decode_credbody() writes the caller's rpc_gss_wire_cred field by field and assigns gc_ctx.len only on the success tail. The caller storage is svcdata->clcred, which lives in the per-svc_rqst gss_svc_data and is reused across requests. Early decode failures leave partially decoded state mixed with residue from the prior request.
The trailing body_len tightness check is the sharpest case:xdr_stream_decode_opaque_inline() has already written gc_ctx.data with a borrowed inline pointer into the current request's XDR pages, but gc_ctx.len retains its prior value. Once the request pages are released the pooled clcred carries a dangling pointer paired with a stale length.
Zero the caller's rpc_gss_wire_cred at function entry so that every early-return path leaves a deterministic all-zero cred. On the trailing tightness-check path, gc_ctx.len is now zero instead of stale, which neuters length-driven consumers such as gss_svc_searchbyctx() that would otherwise walk the dangling data pointer.
--- net/sunrpc/auth_gss/svcauth_gss.c | 3 +++ 1 file changed, 3 insertions(+)
net/sunrpc/auth_gss/svcauth_gss.c +3 -0
@@ -1573,6 +1573,9 @@ svcauth_gss_decode_credbody(struct xdr_su32 body_len;__be32 *p; +/* Early-return paths leave deterministic state, not stale residue. */ +memset (gc,0 ,sizeof (*gc)); + p =xdr_inline_decode (xdr,XDR_UNIT );if (!p)return false;
Greg Kroah-Hartman
maintainerPATCH 372/713 SUNRPC: svcauth_gss: enforce krb5 token minimum length
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit a919c5c88769cf8fb3ec071e6078d830bf512489 upstream.
svcauth_gss_unwrap_priv() validates only an upper bound on the wire-supplied opaque length before handing the buffer to gss_unwrap():
if (len > xdr_stream_remaining(xdr))
goto unwrap_failed;
offset = xdr_stream_pos(xdr);
...
maj_stat = gss_unwrap(ctx, offset, offset + len, buf);The wire value len flows unchanged as the upper bound into the krb5 unwrap path, so a len in [0, 16] passes this check and is handed to gss_unwrap(). For a krb5 v2 context that lands in gss_krb5_unwrap_v2(), which reads the 16-byte RFC 4121 token header fields at ptr+4 and ptr+6 and then calls rotate_left() before any integrity check. With a sub-header length the header reads run past the token, and _rotate_left()'s shift %= buf->len path can divide by zero when buf->len has been driven to zero by the truncated token. A header-only token (len == 16) is equally
invalid: with a non-zero RRC field and the opaque blob ending at the XDR buffer boundary, rotate_left() builds a zero-length subbuffer, reaching the same division.
Reject the token at the server entry point before it reaches the krb5 unwrap core. A valid sealed RFC 4121 token must contain the 16-byte header plus at least some encrypted payload.
Fix by adding a minimum-length check immediately after the existing upper-bound check:
if (len <= GSS_KRB5_TOK_HDR_LEN)
goto unwrap_failed;--- net/sunrpc/auth_gss/svcauth_gss.c | 2 ++ 1 file changed, 2 insertions(+)
net/sunrpc/auth_gss/svcauth_gss.c +2 -0
@@ -949,6 +949,8 @@ svcauth_gss_unwrap_priv(struct svc_rqst }if (len >xdr_stream_remaining (xdr)) goto unwrap_failed; +if (len <=GSS_KRB5_TOK_HDR_LEN ) + goto unwrap_failed; offset =xdr_stream_pos (xdr); saved_len = buf->len;
Greg Kroah-Hartman
maintainerPATCH 373/713 sunrpc: route to a populated pool in svc_pool_for_cpu()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
commit f6310491c4cdb88af73aa551ec9df1f10a90c709 upstream.
svc_set_num_threads() spreads the requested threads evenly across the service's pools (base = nrservs / sv_nrpools). When a service runs fewer threads than it has pools -- e.g. an nfsd configured with fewer threads than the host has NUMA nodes while running in "pernode" or "percpu" mode -- the trailing pools are left with no threads at all.
svc_xprt_enqueue() selects a pool from the CPU servicing the transport, queues the transport on that pool's sp_xprts, and only wakes a thread from the same pool. Each thread services exclusively its own pool, so a transport that lands on a threadless pool is enqueued on sp_xprts and never picked up: the connection hangs indefinitely.
Have svc_pool_for_cpu() skip pools that currently have no threads, falling back to the next populated pool. This trades NUMA locality for a guarantee that the work is actually serviced. sp_nrthreads is only updated under the service mutex; the lockless read here is a best-effort routing hint, so annotate it with data_race().
--- net/sunrpc/svc.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-)
net/sunrpc/svc.c +28 -1
@@ -402,6 +402,7 @@ struct svc_pool *svc_pool_for_cpu(structstruct svc_pool_map *m = &svc_pool_map;int cpu =raw_smp_processor_id ();unsigned int pidx =0 ; +unsigned int i;if (serv->sv_nrpools <=1 )return serv->sv_pools;
@@ -414,8 +415,34 @@ struct svc_pool *svc_pool_for_cpu(struct pidx = m->to_pool[cpu_to_node (cpu)];break ; } + pidx %= serv->sv_nrpools; -return &serv->sv_pools[pidx % serv->sv_nrpools]; +/* +* It's possible to have a pool with no threads. Userland can just set +* things up this way directly. Also, when threads are autodistributed +* they are spread evenly across the pools, but when there are fewer +* threads than pools some pools can end up with none. +* +* A transport enqueued on a threadless pool would never be picked up, +* since each thread only services its own pool. Fall back to the next +* populated pool, trading NUMA locality for a guarantee that the +* transport is serviced. +*/ +for (i =0 ; i < serv->sv_nrpools; i++) { +struct svc_pool *pool = &serv->sv_pools[pidx]; + +/* This is set under the service mutex and rarely ever +* changes. A data race here is harmless. +*/ +if (data_race (pool->sp_nrthreads)) +return pool; + +if (++pidx >= serv->sv_nrpools) + pidx =0 ; + } + +/* No pool has any threads; nothing can service the transport. */ +return &serv->sv_pools[pidx]; }static int svc_rpcb_setup (struct svc_serv *serv,struct net *net)
Greg Kroah-Hartman
maintainerPATCH 374/713 SUNRPC: Restore NUMA_NO_NODE for svc thread allocations in global mode
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ameer Hamza <[email protected]>
commit 0574da29ae12be7714df217c4ce7ff6ba9b0c23b upstream.
Commit d57e43b72bf2 ("SUNRPC: Update svcxdr_init_decode() to call xdr_set_scratch_folio()") changed svc_pool_map_get_node() to return numa_mem_id() instead of NUMA_NO_NODE, because __folio_alloc_node() cannot accept NUMA_NO_NODE. That return value is not equivalent: it is evaluated in the context of the task creating the nfsd threads, once per thread created, and it is passed to kthread_create_on_node() and to the per-thread allocations in svc_prepare_thread().
Since commit d1a89197589c ("kthread: Default affine kthread to its preferred NUMA node"), the node argument of kthread_create_on_node() no longer only places the task structure and stack: a kthread created with a real node id normally affines itself to that node's CPUs when it is first woken to run its thread function. All nfsd threads are typically started together, by one task writing to /proc/fs/nfsd/threads, so under the default pool_mode=global each nfsd thread is now affined to the local-memory node of the CPU its creating iteration happened to run on - typically the same node for every thread. The CPUs of the other nodes are then unable to run nfsd at all, and the threads' allocations - svc_rqst structures, page pointer arrays, newly allocated task stacks, and the per-RPC pages allocated at run time - all prefer that one node.
Restore the NUMA_NO_NODE behaviour that global mode has had since commit 11fd165c68b7 ("sunrpc: use better NUMA affinities"), and handle NUMA_NO_NODE at the one call site that cannot take it by resolving it to numa_mem_id() there, exactly as alloc_pages_node() did for the scratch page before the conversion. The mapped percpu and pernode branches are unchanged. Unpooled services such as lockd and the NFS client callback service also take this fallback when no percpu or pernode map is active, restoring their thread placement in that case.
A bisect of a 2x NFS READ throughput regression between v6.17 and v6.18 converged on d57e43b72bf2. On the affected 4-node server every nfsd thread comes up with its CPU affinity restricted to the CPUs of a single node; with this change the threads are runnable on all CPUs again and the observed regression is resolved.
--- net/sunrpc/svc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
net/sunrpc/svc.c +4 -2
@@ -352,7 +352,7 @@ static int svc_pool_map_get_node(unsigneif (m->mode ==SVC_POOL_PERNODE )return m->pool_to[pidx]; } -return numa_mem_id (); +return NUMA_NO_NODE ; }/* * Set the given thread's cpus_allowed mask so that it
@@ -738,7 +738,9 @@ svc_prepare_thread(struct svc_serv *serv rqstp->rq_server = serv; rqstp->rq_pool = pool; - rqstp->rq_scratch_folio =__folio_alloc_node (GFP_KERNEL ,0 , node); + rqstp->rq_scratch_folio =__folio_alloc_node (GFP_KERNEL ,0 , + node ==NUMA_NO_NODE ? +numa_mem_id () : node);if (!rqstp->rq_scratch_folio) goto out_enomem;
Greg Kroah-Hartman
maintainerPATCH 375/713 SUNRPC: always drain cache_cleaner before destroying a cache_detail
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
commit f42d0fda0c67695db6bc704b04b7c10240805377 upstream.
sunrpc_destroy_cache_detail() only cancels the global cache_cleaner delayed_work when cache_list is empty. During per-netns teardown cache_list is never empty because init_net's caches remain registered, so the cancel never fires. After unlink, the caller proceeds to cache_destroy_net() which kfrees the cache_detail while cache_clean() may still hold a dangling pointer to it. The result is a
use-after-free: cache_dequeue() takes cd->queue_lock on freed memory, and cache_put() dereferences cd->cache_put as a function pointer from freed slab.
Drop the list_empty guard so that cancel_delayed_work_sync() always runs, ensuring any in-flight cache_clean() completes before the cache_detail is freed. Re-arm the cleaner afterwards if other caches are still registered.
--- net/sunrpc/cache.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
net/sunrpc/cache.c +3 -4
@@ -430,10 +430,9 @@ void sunrpc_destroy_cache_detail(structlist_del_init (&cd->others);spin_unlock (&cd->hash_lock);spin_unlock (&cache_list_lock); -if (list_empty (&cache_list)) { -/* module must be being unloaded so its safe to kill the worker */ -cancel_delayed_work_sync (&cache_cleaner); - } +cancel_delayed_work_sync (&cache_cleaner); +if (!list_empty (&cache_list)) +queue_delayed_work (system_power_efficient_wq, &cache_cleaner,0 ); }EXPORT_SYMBOL_GPL (sunrpc_destroy_cache_detail);
Greg Kroah-Hartman
maintainerPATCH 376/713 SUNRPC: Check svc pool percpu counter allocation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit 43e11e164704dde975c9edb370de1a06bec67270 upstream.
__svc_create() initializes three per-pool percpu_counter stats and ignores every return value. On SMP, percpu_counter_init() fails when __alloc_percpu_gfp() cannot satisfy the allocation, leaving the failed counter with fbc->counters == NULL and its embedded raw_spinlock_t, list_head, and count never initialized. __svc_create() returns the half-constructed svc_serv to nfsd, lockd, or the NFS callback service anyway.
Once that service is live, the hot-path increments in svc_xprt_enqueue(), svc_handle_xprt(), and svc_pool_wake_idle_thread() reach a counter whose backing pointer is NULL. The pointer is a per-cpu offset, so the access does not fault:
it resolves to offset zero of the current CPU's per-cpu area and silently corrupts whatever variable lives there. A /proc/fs/nfsd/pool_stats read walks the same NULL per-cpu storage and returns garbage, and on CONFIG_DEBUG_SPINLOCK or lockdep it splats on the never-initialized lock.
Creating the broken service requires a percpu allocation failure during RPC server startup, so it is reachable only by a local administrator under memory pressure or fault injection; a remote peer cannot induce the bad state on its own.
Check each percpu_counter_init() return value in __svc_create() and fail when an allocation fails, unwinding the counters already set up in the current pool and in every pool initialized before it. A discrete percpu_counter_destroy() per counter at teardown frees each per-cpu allocation exactly once.
--- net/sunrpc/svc.c | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-)
net/sunrpc/svc.c +39 -6
@@ -503,6 +503,35 @@ __svc_init_bc(struct svc_serv *serv) }#endif +static int svc_pool_init_counters (struct svc_pool *pool) +{ +int err; + + err =percpu_counter_init (&pool->sp_messages_arrived,0 ,GFP_KERNEL ); +if (err) +return err; + err =percpu_counter_init (&pool->sp_sockets_queued,0 ,GFP_KERNEL ); +if (err) + goto err_sockets; + err =percpu_counter_init (&pool->sp_threads_woken,0 ,GFP_KERNEL ); +if (err) + goto err_threads; +return 0 ; + +err_threads: +percpu_counter_destroy (&pool->sp_sockets_queued); +err_sockets: +percpu_counter_destroy (&pool->sp_messages_arrived); +return err; +} + +static void svc_pool_destroy_counters (struct svc_pool *pool) +{ +percpu_counter_destroy (&pool->sp_messages_arrived); +percpu_counter_destroy (&pool->sp_sockets_queued); +percpu_counter_destroy (&pool->sp_threads_woken); +} +/* * Create an RPC service */
@@ -567,12 +596,18 @@ __svc_create(struct svc_program *prog, iINIT_LIST_HEAD (&pool->sp_all_threads);init_llist_head (&pool->sp_idle_threads); -percpu_counter_init (&pool->sp_messages_arrived,0 ,GFP_KERNEL ); -percpu_counter_init (&pool->sp_sockets_queued,0 ,GFP_KERNEL ); -percpu_counter_init (&pool->sp_threads_woken,0 ,GFP_KERNEL ); +if (svc_pool_init_counters (pool)) + goto out_err; }return serv; + +out_err: +while (i--) +svc_pool_destroy_counters (&serv->sv_pools[i]); +kfree (serv->sv_pools); +kfree (serv); +return NULL ; }/**
@@ -651,9 +686,7 @@ svc_destroy(struct svc_serv **servp)for (i =0 ; i < serv->sv_nrpools; i++) {struct svc_pool *pool = &serv->sv_pools[i]; -percpu_counter_destroy (&pool->sp_messages_arrived); -percpu_counter_destroy (&pool->sp_sockets_queued); -percpu_counter_destroy (&pool->sp_threads_woken); +svc_pool_destroy_counters (pool); }kfree (serv->sv_pools);kfree (serv);
Greg Kroah-Hartman
maintainerPATCH 377/713 SUNRPC: close backchannel before destroying callback service
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit 3674f780f47d2906b5a0f7199b66973067bdfeca upstream.
A backchannel receive can complete a request while the NFS callback service is being torn down. xprt_complete_bc_request() removes the request from bc_pa_list, drops bc_alloc_count, marks the request in use, and then asks xprt_enqueue_bc_request() to hand it to the callback service.
If teardown has already cleared xprt->bc_serv, xprt_enqueue_bc_request() currently returns without enqueueing or freeing the committed request. The xprt_get() taken on entry is leaked as well. If the producer wins the race before bc_serv is cleared, it can also enqueue onto sv_cb_list after nfs_callback_down() has stopped the callback threads, leaving the request linked to a svc_serv that is about to be freed.
Close the producer side before callback threads are stopped. Add xprt_svc_shutdown_bc() to clear xprt->bc_serv under bc_pa_lock, and call it on callback shutdown and callback-start failure before stopping the service threads. Requests that lose the NULL transition in xprt_enqueue_bc_request() are released through the normal backchannel free path after balancing bc_slot_count. Finally, drain any remaining sv_cb_list requests after the callback threads have stopped and before svc_destroy() frees the service.
--- fs/nfs/callback.c | 4 +++- include/linux/sunrpc/bc_xprt.h | 5 +++++ net/sunrpc/backchannel_rqst.c | 38 +++++++++++++++++++++++++++------- 3 files changed, 39 insertions(+), 8 deletions(-)
fs/nfs/callback.c +3 -1
@@ -231,8 +231,9 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) cb_info->users++; err_net:if (!cb_info->users) { +xprt_svc_shutdown_bc (xprt);svc_set_num_threads (cb_info->serv,0 ,0 ); -svc_destroy (&cb_info->serv); +xprt_svc_destroy_nullify_bc (xprt, &cb_info->serv); } err_create:mutex_unlock (&nfs_callback_mutex);
@@ -254,6 +255,7 @@ void nfs_callback_down(int minorversion, struct net *net, struct rpc_xprt *xprt)mutex_lock (&nfs_callback_mutex); serv = cb_info->serv; +xprt_svc_shutdown_bc (xprt);nfs_callback_down_net (minorversion, serv, net); cb_info->users--;if (cb_info->users ==0 ) {
include/linux/sunrpc/bc_xprt.h +5 -0
@@ -32,6 +32,7 @@ int xprt_setup_bc(struct rpc_xprt *xprt, unsigned int min_reqs);void xprt_destroy_bc (struct rpc_xprt *xprt,unsigned int max_reqs);void xprt_free_bc_rqst (struct rpc_rqst *req);unsigned int xprt_bc_max_slots (struct rpc_xprt *xprt); +void xprt_svc_shutdown_bc (struct rpc_xprt *xprt);void xprt_svc_destroy_nullify_bc (struct rpc_xprt *xprt,struct svc_serv **serv);/*
@@ -71,6 +72,10 @@ static inline void xprt_free_bc_request(struct rpc_rqst *req) { } +static inline void xprt_svc_shutdown_bc (struct rpc_xprt *xprt) +{ +} +static inline void xprt_svc_destroy_nullify_bc (struct rpc_xprt *xprt,struct svc_serv **serv) {svc_destroy (serv);
net/sunrpc/backchannel_rqst.c +31 -7
@@ -25,20 +25,39 @@ unsigned int xprt_bc_max_slots(struct rpc_xprt *xprt) }/* -* Helper function to nullify backchannel server pointer in transport. -* We need to synchronize setting the pointer to NULL (done so after -* the backchannel server is shutdown) with the usage of that pointer -* by the backchannel request processing routines -* xprt_complete_bc_request() and rpcrdma_bc_receive_call(). +* Close the backchannel producer side, drain any requests still +* queued on sv_cb_list, then destroy the callback service. */ void xprt_svc_destroy_nullify_bc (struct rpc_xprt *xprt,struct svc_serv **serv) { -spin_lock (&xprt->bc_pa_lock); +struct svc_serv *bc_serv = *serv; +struct rpc_rqst *req; + +xprt_svc_shutdown_bc (xprt); + while ((req = lwq_dequeue(&bc_serv->sv_cb_list, struct rpc_rqst, + rq_bc_list)) !=NULL ) { +atomic_dec (&req->rq_xprt->bc_slot_count); +xprt_free_bc_request (req); + }svc_destroy (serv); +} +EXPORT_SYMBOL_GPL (xprt_svc_destroy_nullify_bc); + +/* +* Clear the backchannel server pointer in the transport. The NULL +* store is serialized under bc_pa_lock against readers of +* xprt->bc_serv in xprt_complete_bc_request() and +* rpcrdma_bc_receive_call(). Clearing it before the callback service +* is stopped prevents a producer from enqueueing onto a service that +* is being torn down. +*/ +void xprt_svc_shutdown_bc (struct rpc_xprt *xprt) +{ +spin_lock (&xprt->bc_pa_lock); xprt->bc_serv =NULL ;spin_unlock (&xprt->bc_pa_lock); } -EXPORT_SYMBOL_GPL (xprt_svc_destroy_nullify_bc); +EXPORT_SYMBOL_GPL (xprt_svc_shutdown_bc);/* * Helper routines that track the number of preallocation elements
@@ -393,7 +412,12 @@ void xprt_enqueue_bc_request(struct rpc_rqst *req)if (bc_serv) {lwq_enqueue (&req->rq_bc_list, &bc_serv->sv_cb_list);svc_pool_wake_idle_thread (&bc_serv->sv_pools[0 ]); +spin_unlock (&xprt->bc_pa_lock); +return ; }spin_unlock (&xprt->bc_pa_lock); + +atomic_dec (&xprt->bc_slot_count); +xprt_free_bc_request (req); }EXPORT_SYMBOL_GPL (xprt_enqueue_bc_request);
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 378/713 sunrpc: defer rq_argp and rq_resp free until after RCU grace period
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
commit c479bde671cbe2f9e152834a8b0eb7c3c295bbaf upstream.
svc_rqst_free() frees rqstp->rq_argp and rqstp->rq_resp synchronously via kfree(), but defers the rqstp struct free via kfree_rcu(). After svc_exit_thread() calls list_del_rcu() and svc_rqst_free(), there is a window where RCU readers that started before list_del_rcu() can still traverse the thread list and find the rqstp. These readers (e.g. nfsd_nl_rpc_status_get_dumpit()) dereference rqstp->rq_argp, which has already been freed — a use-after-free.
Fix this by moving the kfree of rq_argp and rq_resp into an explicit call_rcu() callback alongside the struct free. Resources not accessed by RCU readers (bvec, buffer pages, scratch folio, auth_data) remain synchronously freed.
--- net/sunrpc/svc.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
net/sunrpc/svc.c +10 -3
@@ -743,6 +743,15 @@ svc_release_buffer(struct svc_rqst *rqst } } +static void svc_rqst_free_rcu (struct rcu_head *head) +{ + struct svc_rqst *rqstp = container_of(head, struct svc_rqst, rq_rcu_head); + +kfree (rqstp->rq_resp); +kfree (rqstp->rq_argp); +kfree (rqstp); +} +static void svc_rqst_free (struct svc_rqst *rqstp) {
@@ -751,10 +760,8 @@ svc_rqst_free(struct svc_rqst *rqstp)svc_release_buffer (rqstp);if (rqstp->rq_scratch_folio)folio_put (rqstp->rq_scratch_folio); -kfree (rqstp->rq_resp); -kfree (rqstp->rq_argp);kfree (rqstp->rq_auth_data); -kfree_rcu (rqstp, rq_rcu_head); +call_rcu (&rqstp->rq_rcu_head, svc_rqst_free_rcu); } static struct svc_rqst *
Greg Kroah-Hartman
maintainerPATCH 379/713 SUNRPC: fix gssx_dec_option_array error path bugs
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 5e9a94539b1ec17a89177d952badfd0d844d694a upstream.
Four coupled defects in the gssx XDR option-array decoder make the error paths unsafe: a NULL deref in the caller, a refcount leak on the decoded group_info, and a latent use-after-free that the leak fix would otherwise expose.
gssx_dec_option_array() sets oa->count = 1 before allocating oa->data. If that allocation fails, -ENOMEM is returned with oa->count == 1 and oa->data == NULL. All other error paths jump to free_oa: which frees oa->data and NULLs it but also leaves oa->count == 1. The caller trusts the count:
gssp_accept_sec_context_upcall()
gssx_dec_accept_sec_context()
gssx_dec_option_array() /* fails, count=1 data=NULL */
data = res.options.data[0].value /* NULL deref */
Independently, free_creds: releases the partially decoded svc_cred with a bare kfree(creds). gssx_dec_linux_creds() installs a groups_alloc() result into creds->cr_group_info; that object is kvmalloc-backed and refcounted, and only put_group_info() reaches kvfree(). A plain kfree(creds) drops the wrapper and leaks the group_info allocation.
The natural fix for the leak is to call free_svc_cred(creds) before kfree(creds), but free_svc_cred() invokes put_group_info() on creds->cr_group_info unconditionally when non-NULL. The existingout_free_groups: path in gssx_dec_linux_creds() already called groups_free() on that pointer without clearing it, so once free_svc_cred() is wired in, the subsequent put_group_info() would touch freed memory.
Fix all four together:
- Move the oa->count = 1 assignment below the oa->data allocation
so it is never set when oa->data is NULL.
- Reset oa->count to 0 at free_oa: so count and data stay coherent and the caller sees an empty option array.
- Call free_svc_cred(creds) before kfree(creds) at free_creds:
so the refcounted cr_group_info is released. free_svc_cred() either NULL-guards each field explicitly (cr_group_info has an if() check) or delegates to a helper that is NULL-safe itself (kfree for the string fields, gss_mech_put() which guards with if(gm) at gss_mech_switch.c:342), so it is safe to call on a partially decoded svc_cred where only cr_uid/cr_gid/cr_group_info have been written and everything else is zero from kzalloc.
- In gssx_dec_linux_creds()'s out_free_groups: path, release cr_group_info with put_group_info() rather than groups_free() so the teardown matches free_svc_cred()'s refcount-aware path, and clear the pointer so a later free_svc_cred() on the same creds does not release it a second time.
--- net/sunrpc/auth_gss/gss_rpc_xdr.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
net/sunrpc/auth_gss/gss_rpc_xdr.c +6 -3
@@ -222,7 +222,8 @@ static int gssx_dec_linux_creds(struct xreturn 0 ; out_free_groups: -groups_free (creds->cr_group_info); +put_group_info (creds->cr_group_info); + creds->cr_group_info =NULL ;return err; }
@@ -242,12 +243,12 @@ static int gssx_dec_option_array(structreturn 0 ;/* we recognize only 1 currently: CREDS_VALUE */ - oa->count =1 ; - oa->data = kmalloc_obj(struct gssx_option);if (!oa->data)return -ENOMEM ; + oa->count =1 ; + creds = kzalloc_obj(struct svc_cred);if (!creds) { err = -ENOMEM ;
@@ -294,8 +295,10 @@ static int gssx_dec_option_array(structreturn 0 ; free_creds: +free_svc_cred (creds);kfree (creds); free_oa: + oa->count =0 ;kfree (oa->data); oa->data =NULL ;return err;
Greg Kroah-Hartman
maintainerPATCH 380/713 sunrpc: fix use-after-free in __rpc_clnt_handle_event and __rpc_clnt_remove_pipedir
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luxiao Xu <[email protected]>
commit 932a8cf6abb2b2f8677b79153a823108d8861fe2 upstream.
Normal client creation goes through rpc_setup_pipedir(), which records clnt->pipefs_sb, but the mount-event path in __rpc_clnt_handle_event() calls rpc_setup_pipedir_sb() directly and never refreshes that field. The umount path also removes the directory without clearing clnt->pipefs_sb.
After a late pipefs mount or any remount, rpc_clnt_remove_pipedir() compares the current superblock against a stale pipefs_sb pointer and skips cleanup, leaving pipefs dentries whose inode private data still points at a freed rpc_clnt, leading to a potential use-after-free during subsequent rpc_info_open() or rpc_show_info() calls.
Fix this by properly updating clnt->pipefs_sb upon mount events and clearing it during unmount or failure paths.
--- net/sunrpc/clnt.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-)
net/sunrpc/clnt.c +17 -5
@@ -96,7 +96,10 @@ static void rpc_unregister_client(structstatic void __rpc_clnt_remove_pipedir (struct rpc_clnt *clnt) { -rpc_remove_client_dir (clnt); +if (clnt->pipefs_sb) { +rpc_remove_client_dir (clnt); + clnt->pipefs_sb =NULL ; + } }static void rpc_clnt_remove_pipedir (struct rpc_clnt *clnt)
@@ -177,19 +180,28 @@ static int rpc_clnt_skip_event(struct rp }static int __rpc_clnt_handle_event (struct rpc_clnt *clnt,unsigned long event, -struct super_block *sb) +struct super_block *sb) { +int err =0 ; +switch (event) {case RPC_PIPEFS_MOUNT : -return rpc_setup_pipedir_sb (sb, clnt); + clnt->pipefs_sb = sb; + err =rpc_setup_pipedir_sb (sb, clnt); +if (err) + clnt->pipefs_sb =NULL ; +break ;case RPC_PIPEFS_UMOUNT : -__rpc_clnt_remove_pipedir (clnt); +if (clnt->pipefs_sb == sb) { +__rpc_clnt_remove_pipedir (clnt); + clnt->pipefs_sb =NULL ; + }break ;default :printk (KERN_ERR "%s: unknown event: %ld\n" , __func__, event);return -ENOTSUPP ; } -return 0 ; +return err; } static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event,
Greg Kroah-Hartman
maintainerPATCH 381/713 SUNRPC: Guard svcauth_gss_release() dispatch on rq_auth_stat
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit f8870b9b75afb77986bc65940a231d54068ff2b1 upstream.
svcauth_gss_release() reads gc_proc and switches on gc_svc before consulting rq_auth_stat. On the SVC_DENIED path after a failed svcauth_gss_accept(), those fields may hold stale values from a prior request or uninitialized slab residue: svcauth_gss_accept() allocates gss_svc_data with non-zeroing kmalloc and clears only gsd_databody_offset and rsci per request, not clcred.
Because RPC_GSS_PROC_DATA is zero, a zeroed or stale-zero gc_proc passes the existing guard and falls through into the gc_svc switch, which can dispatch to svcauth_gss_wrap_integ() or svcauth_gss_wrap_priv(). Both wrap helpers call svcauth_gss_prepare_to_wrap() before any rsci->mechctx dereference, and that helper already returns early when rq_auth_stat is not rpc_auth_ok, so the downstream NULL dereference is blocked. The dispatch itself remains structurally wrong: it reads scalars that the caller has no contract to have initialized after a failed authentication.
Mirror the existing rq_auth_stat gate in svcauth_gss_prepare_to_wrap() one frame up, so svcauth_gss_release() skips the clcred dispatch entirely when authentication has not succeeded. The cleanup tail that releases rq_client, rq_gssclient, cr_group_info, and rsci still runs.
--- net/sunrpc/auth_gss/svcauth_gss.c | 2 ++ 1 file changed, 2 insertions(+)
net/sunrpc/auth_gss/svcauth_gss.c +2 -0
@@ -1947,6 +1947,8 @@ svcauth_gss_release(struct svc_rqst *rqsif (!gsd) goto out; +if (rqstp->rq_auth_stat != rpc_auth_ok) + goto out; gc = &gsd->clcred;if (gc->gc_proc !=RPC_GSS_PROC_DATA ) goto out;
Greg Kroah-Hartman
maintainerPATCH 382/713 SUNRPC: harden gss_krb5_unwrap_v2 against short tokens
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 6959297aaa9572783d620a226d73c3fb94494888 upstream.
gss_krb5_unwrap_v2() reads the EC and RRC header fields at ptr+4 and ptr+6 before validating that the token is at least GSS_KRB5_TOK_HDR_LEN
(16) bytes long, and its rotate_left() helper passes buf->len - base to xdr_buf_subsegment() without verifying that base <= buf->len. When a caller hands in a sub-16-byte token, or a token whose declared len leaves base past the end of the buffer, three distinct failures follow:
gss_krb5_unwrap_v2(offset, len, buf)
ptr = buf->head[0].iov_base + offset
ec = *(ptr + 4) /* OOB read on short head */
rrc = *(ptr + 6) /* OOB read on short head */
rotate_left(offset + 16, buf, rrc)
xdr_buf_subsegment(buf, &subbuf,
base, buf->len - base) /* u32 wrap when base > len */
_rotate_left(&subbuf, shift)
shift %= buf->len /* divide-by-zero when base == len */
After decryption, the cleanup arithmetic has the same shape:
movelen = min_t(unsigned int, buf->head[0].iov_len, len);
movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip;
BUG_ON(offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen >
buf->head[0].iov_len);The BUG_ON re-adds the value just subtracted, so it reduces to min(A, B) > A and is permanently false; it cannot catch the unsigned underflow of movelen, which then drives a ~UINT_MAX-byte memmove().
Add four defense-in-depth guards inside the unwrap core so it is safe regardless of what its callers validate:
- reject tokens with len - offset < GSS_KRB5_TOK_HDR_LEN before
touching ptr+4/ptr+6;
- bail from rotate_left() when buf->len <= base, covering both the
underflow and zero-length cases;
- return early from _rotate_left() when buf->len is zero, so the
shift %= buf->len modulo cannot fault; - replace the dead BUG_ON with a live check that returns GSS_S_DEFECTIVE_TOKEN before the movelen subtraction.
--- net/sunrpc/auth_gss/gss_krb5_wrap.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
net/sunrpc/auth_gss/gss_krb5_wrap.c +9 -2
@@ -73,6 +73,8 @@ static void _rotate_left(struct xdr_bufint shifted =0 ;int this_shift; +if (!buf->len) +return ; shift %= buf->len;while (shifted < shift) { this_shift =min (shift - shifted,LOCAL_BUF_LEN );
@@ -85,6 +87,8 @@ static void rotate_left(u32 base, struct {struct xdr_buf subbuf; +if (buf->len <= base) +return ;xdr_buf_subsegment (buf, &subbuf, base, buf->len - base);_rotate_left (&subbuf, shift); }
@@ -154,6 +158,9 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctxdprintk ("RPC: %s\n" , __func__); +if (len - offset <=GSS_KRB5_TOK_HDR_LEN ) +return GSS_S_DEFECTIVE_TOKEN ; + ptr = buf->head[0 ].iov_base + offset;if (be16_to_cpu (*((__be16 *)ptr)) !=KG2_TOK_WRAP )
@@ -220,9 +227,9 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx* head buffer space rather than that actually occupied. */ movelen = min_t(unsigned int, buf->head[0].iov_len, len); +if (movelen < offset +GSS_KRB5_TOK_HDR_LEN + headskip) +return GSS_S_DEFECTIVE_TOKEN ; movelen -= offset +GSS_KRB5_TOK_HDR_LEN + headskip; -BUG_ON (offset +GSS_KRB5_TOK_HDR_LEN + headskip + movelen > - buf->head[0 ].iov_len);memmove (ptr, ptr +GSS_KRB5_TOK_HDR_LEN + headskip, movelen); buf->head[0 ].iov_len -=GSS_KRB5_TOK_HDR_LEN + headskip; buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);
Greg Kroah-Hartman
maintainerPATCH 383/713 SUNRPC: harden gss_unwrap_resp_priv length checks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 87831b92112c81db251d46756d65daa4f91af6a2 upstream.
gss_unwrap_resp_priv() validates the RPCSEC_GSS opaque length with
offset = (u8 *)(p) - (u8 *)head->iov_base;
if (offset + opaque_len > rcv_buf->len)
goto unwrap_failed;
maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset,
offset + opaque_len, rcv_buf);Both operands are u32 and the sum is computed in u32. A reply with opaque_len near 0xffffffff makes offset + opaque_len wrap to a small value that is below rcv_buf->len, so the bound check passes and gss_unwrap() is called with end < begin. The check also lacks a lower bound, so any opaque_len in [0, GSS_KRB5_TOK_HDR_LEN) is accepted and forwarded to gss_krb5_unwrap_v2(), whose pre-decrypt header reads at ptr+4 and ptr+6 then run past the token.
A krb5p NFS server returning a crafted RPCSEC_GSS reply can drive the client into out-of-bounds reads in gss_krb5_unwrap_v2() and the rotate_left() loop that follows.
Fix by replacing the single combined check with three guards that are safe in u32 arithmetic and that enforce the RFC 4121 minimum outer token length:
if (offset > rcv_buf->len)
goto unwrap_failed;
if (opaque_len > rcv_buf->len - offset)
goto unwrap_failed;
if (opaque_len < GSS_KRB5_TOK_HDR_LEN)
goto unwrap_failed;The first guard makes the subtraction in the second guard unconditionally safe; offset is derived from a successful xdr_inline_decode() in the head kvec, so in practice it already satisfies the bound. The floor mirrors the server-side check added in commit 5b757c2e57a5 ("SUNRPC: svcauth_gss: enforce krb5 token minimum length").
--- net/sunrpc/auth_gss/auth_gss.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
net/sunrpc/auth_gss/auth_gss.c +5 -1
@@ -2072,7 +2072,11 @@ gss_unwrap_resp_priv(struct rpc_task *ta goto unwrap_failed; opaque_len =be32_to_cpup (p++); offset = (u8 *)(p) - (u8 *)head->iov_base; -if (offset + opaque_len > rcv_buf->len) +if (offset > rcv_buf->len) + goto unwrap_failed; +if (opaque_len > rcv_buf->len - offset) + goto unwrap_failed; +if (opaque_len <=GSS_KRB5_TOK_HDR_LEN ) goto unwrap_failed; maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset,
Greg Kroah-Hartman
maintainerPATCH 384/713 sunrpc: init gssp_lock before publishing proc entry
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 5ce1ed6159731a41fdd0b03eedbed4e147036a5a upstream.
create_use_gss_proxy_proc_entry() publishes /proc/net/rpc/use-gss-proxy via proc_create_data() before init_gssp_clnt() runs mutex_init() on sn->gssp_lock. Once the dentry is linked under proc_subdir_lock it is immediately reachable from userspace, so a write that lands in the window drives set_gssp_clnt() into mutex_lock() on a zero-initialized struct mutex.
create_use_gss_proxy_proc_entry(net)
proc_create_data("use-gss-proxy", ...) /* dentry live */
init_gssp_clnt(sn)
mutex_init(&sn->gssp_lock) /* too late */
write_gssp()
set_gssp_clnt(net)
mutex_lock(&sn->gssp_lock) /* uninitialized */
gssp_rpc_create(...)
sn->gssp_clnt = clnt
mutex_unlock(&sn->gssp_lock)The window spans only the two statements between proc_create_data() returning and init_gssp_clnt(), so a writer reaches it only if the registering thread is preempted there while another task is already opening the freshly published file. register_pernet_subsys() runs in preemptible context under pernet_ops_rwsem, so that preemption is possible, and the window widens on auth_rpcgss module load, when the proc entry is created for every live net namespace whose tasks are already running. A writer that wins the race locks a zero-filled struct mutex. On CONFIG_DEBUG_MUTEXES the missing magic value trips a "lock used without init" splat; on a production kernel the fast path acquires the lock via CMPXCHG(owner, 0, current). In the latter case a second writer that arrives before init_gssp_clnt() re-zeroes owner can enter set_gssp_clnt() concurrently, shut down the first writer's clnt while it is still in use, and leak the loser's clnt.
Fix by initializing sn->gssp_lock in sunrpc_init_net() so its lifetime matches the sunrpc_net it lives in. sn->gssp_clnt is already NULL from the kzalloc that backs net_generic storage, so the lazy helper is no longer needed; drop init_gssp_clnt(), its prototype, and the call from create_use_gss_proxy_proc_entry(). sunrpc.ko is a build-time dependency of auth_rpcgss.ko, so sunrpc_init_net() has always run on every netns before any auth_gss pernet init can publish the proc entry.
--- net/sunrpc/auth_gss/gss_rpc_upcall.c | 6 ------ net/sunrpc/auth_gss/gss_rpc_upcall.h | 1 - net/sunrpc/auth_gss/svcauth_gss.c | 1 - net/sunrpc/sunrpc_syms.c | 1 + 4 files changed, 1 insertion(+), 8 deletions(-)
net/sunrpc/auth_gss/gss_rpc_upcall.c +0 -6
@@ -121,12 +121,6 @@ out:return result; } -void init_gssp_clnt (struct sunrpc_net *sn) -{ -mutex_init (&sn->gssp_lock); - sn->gssp_clnt =NULL ; -} -int set_gssp_clnt (struct net *net) {struct sunrpc_net *sn =net_generic (net, sunrpc_net_id);
net/sunrpc/auth_gss/gss_rpc_upcall.h +0 -1
@@ -29,7 +29,6 @@ int gssp_accept_sec_context_upcall(struc struct gssp_upcall_data *data);void gssp_free_upcall_data (struct gssp_upcall_data *data); -void init_gssp_clnt (struct sunrpc_net *);int set_gssp_clnt (struct net *);void clear_gssp_clnt (struct sunrpc_net *);
net/sunrpc/auth_gss/svcauth_gss.c +0 -1
@@ -1468,7 +1468,6 @@ static int create_use_gss_proxy_proc_ent &use_gss_proxy_proc_ops, net);if (!*p)return -ENOMEM ; -init_gssp_clnt (sn);return 0 ; }
net/sunrpc/sunrpc_syms.c +1 -0
@@ -57,6 +57,7 @@ static __net_init int sunrpc_init_net(stINIT_LIST_HEAD (&sn->all_clients);spin_lock_init (&sn->rpc_client_lock);spin_lock_init (&sn->rpcb_clnt_lock); +mutex_init (&sn->gssp_lock);return 0 ; err_pipefs:
Greg Kroah-Hartman
maintainerPATCH 385/713 SUNRPC: reject duplicate CREDS_VALUE options
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 2e4ce62385c1b8a887c5370af058ac7b52a8eaf9 upstream.
gssx_dec_option_array() walks the wire-supplied option array and, for every entry whose name matches CREDS_VALUE, calls gssx_dec_linux_creds() on the same struct svc_cred. That helper unconditionally installs a fresh groups_alloc() result into creds->cr_group_info without releasing whatever pointer was already there:
for (i = 0; i < count; i++) {
... decode name ...
if (length == sizeof(CREDS_VALUE) &&
memcmp(p, CREDS_VALUE, sizeof(CREDS_VALUE)) == 0) {
err = gssx_dec_linux_creds(xdr, creds);
...
}
}A reply that carries two CREDS_VALUE entries therefore overwrites cr_group_info on the second iteration and orphans the group_info allocated by the first call. The earlier free_creds path only releases the last cr_group_info via free_svc_cred(), so the first allocation's refcount stays at one and its kvmalloc-backed storage is leaked. No in-tree caller of gssp_accept_sec_context_upcall() expects more than one CREDS_VALUE per reply.
Fix by tracking whether a CREDS_VALUE option has already been decoded and returning -EINVAL on any subsequent match, so the free_creds path releases the single group_info that was installed.
--- net/sunrpc/auth_gss/gss_rpc_xdr.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/sunrpc/auth_gss/gss_rpc_xdr.c +6 -0
@@ -231,6 +231,7 @@ static int gssx_dec_option_array(struct struct gssx_option_array *oa) {struct svc_cred *creds; +bool creds_decoded = false;u32 count, i;__be32 *p;int err;
@@ -281,9 +282,14 @@ static int gssx_dec_option_array(structif (length ==sizeof (CREDS_VALUE ) &&memcmp (p,CREDS_VALUE ,sizeof (CREDS_VALUE )) ==0 ) {/* We have creds here. parse them */ +if (creds_decoded) { + err = -EINVAL ; + goto free_creds; + } err =gssx_dec_linux_creds (xdr, creds);if (err) goto free_creds; + creds_decoded = true; oa->data[0 ].value.len =1 ;/* presence */ }else {/* consume uninteresting buffer */
Greg Kroah-Hartman
maintainerPATCH 386/713 SUNRPC: Reject krb5 v2 wrap tokens with oversized ec field
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit ad484748eec0a66eac0f13ab53b3fbedb7333c91 upstream.
gss_krb5_unwrap_v2() sets buf->len to a logical length, which can be much smaller than head[0].iov_len (the allocated receive-page capacity). It then calls xdr_buf_trim() with a trim length derived from the 16-bit "extra count" (ec) field in the Kerberos v2 token header.
The ec field is authenticated by the post-decrypt memcmp() against the encrypted header copy, so a randomly-mutated value is rejected. However, any peer holding a valid GSS context can legitimately encrypt a token whose ec exceeds the plaintext length. Per RFC 4121, such a token is structurally malformed.
Although xdr_buf_trim() now clamps the buf->len subtraction to avoid unsigned underflow, the buffer is still left in a semantically invalid state (zero length, inconsistent iov lengths) when ec is oversized.
Reject these tokens before calling xdr_buf_trim(), giving callers a well-defined GSS_S_DEFECTIVE_TOKEN error and keeping the xdr_buf internally consistent. The wrapped blob begins at a nonzero offset -- both callers pass len as offset + opaque_len -- so buf->len still counts the offset bytes that precede the blob. Compare the trim length against the remaining wrapped segment, buf->len - offset, rather than the whole buffer; comparing against buf->len alone leaves an offset-wide window in which an oversized ec passes the test and xdr_buf_trim() cuts into the bytes ahead of the blob.
--- net/sunrpc/auth_gss/gss_krb5_wrap.c | 2 ++ 1 file changed, 2 insertions(+)
net/sunrpc/auth_gss/gss_krb5_wrap.c +2 -0
@@ -235,6 +235,8 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);/* Trim off the trailing "extra count" and checksum blob */ +if (ec +GSS_KRB5_TOK_HDR_LEN + tailskip > buf->len - offset) +return GSS_S_DEFECTIVE_TOKEN ;xdr_buf_trim (buf, ec +GSS_KRB5_TOK_HDR_LEN + tailskip); *align =XDR_QUADLEN (GSS_KRB5_TOK_HDR_LEN + headskip);
Greg Kroah-Hartman
maintainerPATCH 387/713 SUNRPC: Reject short RFC 4121 MIC tokens in gss_krb5_verify_mic_v2
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit b94f6719dcd9f7a609bc5f459f85795900e77d25 upstream.
gss_krb5_verify_mic_v2() reads the token ID at ptr[0..1], the flags byte at ptr[2], and padding at ptr[3..7], then passes
ptr + GSS_KRB5_TOK_HDR_LEN and cksum_len to gss_krb5_mic_build_sg(). None of these accesses check read_token->len first.
The minimum safe token size is GSS_KRB5_TOK_HDR_LEN (16) plus ctx->krb5e->cksum_len (12-24, depending on the enctype). All callers accept shorter tokens from the wire:
- gss_unwrap_resp_integ() enforces only an upper bound (offset + len <= rcv_buf->len) before allocating mic.data = kmalloc(len) and passing it to gss_verify_mic(). A malicious NFS server can therefore supply a short checksum opaque, producing a small slab allocation that the Kerberos MIC verifier reads past.
- gss_validate() enforces only len <= RPC_MAX_AUTH_SIZE (400) before passing the wire-supplied length to gss_validate_seqno_mic(), which constructs a mic xdr_netobj and calls gss_verify_mic().
- svcauth_gss_verify_header() enforces only checksum.len >= XDR_UNIT (4 bytes) before dispatching to gss_verify_mic().
- svcauth_gss_unwrap_integ() checks only that the checksum fits in gsd->gsd_scratch.
Add a length guard at the top of gss_krb5_verify_mic_v2(), before any ptr[] access or scatterlist construction. Well-formed MIC tokens from gss_krb5_get_mic_v2() already have exactly GSS_KRB5_TOK_HDR_LEN + cksum_len bytes, so valid traffic is unaffected.
--- net/sunrpc/auth_gss/gss_krb5_unseal.c | 3 +++ 1 file changed, 3 insertions(+)
net/sunrpc/auth_gss/gss_krb5_unseal.c +3 -0
@@ -89,6 +89,9 @@ gss_krb5_verify_mic_v2(struct krb5_ctx *dprintk ("RPC: %s\n" , __func__); +if (read_token->len <GSS_KRB5_TOK_HDR_LEN + cksum_len) +return GSS_S_DEFECTIVE_TOKEN ; +memcpy (&be16_ptr, (char *) ptr,2 );if (be16_to_cpu (be16_ptr) !=KG2_TOK_MIC )return GSS_S_DEFECTIVE_TOKEN ;
Greg Kroah-Hartman
maintainerPATCH 388/713 SUNRPC: wait for in-flight client TLS handshake callback
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jérémy Jean <[email protected]>
commit a89dd597458848b463d284b15e42a8078beeb046 upstream.
xs_tls_handshake_sync() gives xs_tls_handshake_done() a reference to the lower transport before submitting the handshake request. On timeout or signal, the synchronous waiter drops that reference after calling tls_handshake_cancel().
handshake_req_cancel() returns false when handshake_complete() has already marked the request complete. In that case the completion callback can still be running, so dropping the callback-owned reference in the waiter can free the lower transport before xs_tls_handshake_done() stores xprt_err or drops its own reference.
If cancellation loses to completion, wait until xs_tls_handshake_done() signals handshake_done and let the callback release its reference. This mirrors the server-side handshake lifetime handling and keeps the timeout or signal return value unchanged.
--- net/sunrpc/xprtsock.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
net/sunrpc/xprtsock.c +11 -1
@@ -2648,7 +2648,17 @@ static int xs_tls_handshake_sync(struct rc =wait_for_completion_interruptible_timeout (&lower_transport->handshake_done,XS_TLS_HANDSHAKE_TO );if (rc <=0 ) { -tls_handshake_cancel (sk); +if (!tls_handshake_cancel (sk)) { +/* +* Cancellation lost to handshake_complete(): the +* callback still owns its xprt reference and is in +* flight. Wait for it to finish before returning. +*/ +wait_for_completion (&lower_transport->handshake_done); +if (rc ==0 ) + rc = -ETIMEDOUT ; + goto out; + }if (rc ==0 ) rc = -ETIMEDOUT ; goto out_put_xprt;
Greg Kroah-Hartman
maintainerPATCH 389/713 svcrdma: Reorder rpcrdma_rn_unregister before rdma_destroy_id
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit 4488e912973773d64368828acf3b8e39d93650ae upstream.
svc_rdma_free() caches rdma->sc_cm_id->device before teardown, then calls rdma_destroy_id(sc_cm_id) which frees the cm_id. rpcrdma_rn_unregister() follows, but between those two calls the transport's sc_rn entry is still installed in the device's rd_xa. A concurrent ib_unregister_device walk can dispatch svc_rdma_xprt_done() against the now-freed sc_cm_id.
Move rpcrdma_rn_unregister() before rdma_destroy_id() so the transport's notification entry is removed from the xarray before the cm_id it references is destroyed.
Also guard the sc_cm_id dereference with a NULL check: the following patches introduce paths that reach svc_rdma_free() with sc_cm_id == NULL (listener create failure, ADDR_CHANGE replacement failure).
--- net/sunrpc/xprtrdma/svc_rdma_transport.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_transport.c +10 -3
@@ -634,10 +634,15 @@ static void svc_rdma_free(struct svc_xpr {struct svcxprt_rdma *rdma = container_of(xprt, struct svcxprt_rdma, sc_xprt); -struct ib_device *device = rdma->sc_cm_id->device; +struct ib_device *device;might_sleep (); +if (!rdma->sc_cm_id) + goto out_free; + + device = rdma->sc_cm_id->device; +/* This blocks until the Completion Queues are empty */ if (rdma->sc_qp && !IS_ERR (rdma->sc_qp))ib_drain_qp (rdma->sc_qp);
@@ -662,11 +667,13 @@ static void svc_rdma_free(struct svc_xprif (rdma->sc_pd && !IS_ERR (rdma->sc_pd))ib_dealloc_pd (rdma->sc_pd); +if (!test_bit (XPT_LISTENER , &rdma->sc_xprt.xpt_flags)) +rpcrdma_rn_unregister (device, &rdma->sc_rn); +/* Destroy the CM ID */ rdma_destroy_id (rdma->sc_cm_id); -if (!test_bit (XPT_LISTENER , &rdma->sc_xprt.xpt_flags)) -rpcrdma_rn_unregister (device, &rdma->sc_rn); +out_free:kfree (rdma); }
Greg Kroah-Hartman
maintainerPATCH 390/713 svcrdma: Clear sc_cm_id when ADDR_CHANGE replacement fails
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit 01500306e1d50de7ca7a2cdcdfa28ac0523eb747 upstream.
When svc_rdma_listen_handler() handles RDMA_CM_EVENT_ADDR_CHANGE, it creates a replacement listener cm_id and returns 1, telling the CM core to destroy the old one. If the replacement allocation fails, sc_cm_id still points at the old cm_id that the CM core is about to destroy. Any subsequent dereference of sc_cm_id -- such as svc_rdma_detach()'s rdma_disconnect() call -- is a use-after-free.
NULL sc_cm_id on the failure path and guard svc_rdma_detach()'s rdma_disconnect() call against NULL so that the listener can be torn down safely when the server shuts down.
--- net/sunrpc/xprtrdma/svc_rdma_transport.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
net/sunrpc/xprtrdma/svc_rdma_transport.c +3 -1
@@ -325,6 +325,7 @@ static int svc_rdma_listen_handler(strucif (IS_ERR (listen_id)) {pr_err ("Listener dead, address change failed for device %s\n" , cma_id->device->name); + cma_xprt->sc_cm_id =NULL ; }else cma_xprt->sc_cm_id = listen_id;return 1 ;
@@ -614,7 +615,8 @@ static void svc_rdma_detach(struct svc_xstruct svcxprt_rdma *rdma = container_of(xprt, struct svcxprt_rdma, sc_xprt); -rdma_disconnect (rdma->sc_cm_id); +if (rdma->sc_cm_id) +rdma_disconnect (rdma->sc_cm_id);/* * Most close paths go through svc_rdma_xprt_deferred_close(),
Greg Kroah-Hartman
maintainerPATCH 391/713 svcrdma: Fix offset arithmetic in read_chunk_range
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 4a44c140cc2f3643a39e258bb0c0ab9d0f494f5e upstream.
svc_rdma_read_chunk_range() walks a Read chunk's segment list to build a sub-range starting at byte offset and spanning length bytes for a Position-Zero or Call chunk. Two arithmetic defects in the per-segment loop produce wrong DMA lengths and a u32 underflow:
pcl_for_each_segment(segment, chunk) {
if (offset > segment->rs_length) {
offset -= segment->rs_length;
continue;
}
dummy.rs_handle = segment->rs_handle;
dummy.rs_length = min_t(u32, length,
segment->rs_length) - offset;
dummy.rs_offset = segment->rs_offset + offset;First, the skip predicate uses '>' instead of '>='. When offset equals the segment's full rs_length, the segment is fully consumed and should be skipped, but the loop falls through into the body. The resulting dummy.rs_length is min_t(u32, length, rs_length) - rs_length, which underflows to a near-UINT_MAX u32 when length is smaller than rs_length, or is zero otherwise.
Second, the length formula subtracts offset from the min_t() result rather than from segment->rs_length before the cap. For offset > 0 the segment's residual is rs_length - offset, not rs_length, so the cap must be applied to the residual. With the current bracketing, whenever length is smaller than rs_length - offset the per-segment length becomes length - offset instead of length, silently dropping offset bytes from the rebuilt chunk. Combined with the boundary case above it also enables the u32 underflow path, which propagates a huge nr_bvec into svc_rdma_build_read_segment() and a multi-MiB kmalloc_array_node() in svc_rdma_get_rw_ctxt().
Additionally, svc_rdma_read_call_chunk() can invoke this function with length == 0 when the last Read chunk ends exactly at the end of the Call chunk. With the corrected >= predicate, every segment is skipped and the function returns the initial -EINVAL, rejecting a valid request. Return success immediately when length is zero. Also break out of the loop once length is fully consumed to avoid passing zero-length segments to svc_rdma_build_read_segment().
Fix by using '>=' so a fully-consumed segment is skipped, by moving '- offset' inside min_t() so the cap is applied to the segment's residual length, by returning success for zero-length requests, and by stopping iteration when the requested range has been consumed.
--- net/sunrpc/xprtrdma/svc_rdma_rw.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_rw.c +7 -2
@@ -996,17 +996,20 @@ static int svc_rdma_read_chunk_range(strconst struct svc_rdma_segment *segment;int ret; +if (!length) +return 0 ; + ret = -EINVAL ;pcl_for_each_segment (segment, chunk) {struct svc_rdma_segment dummy; -if (offset > segment->rs_length) { +if (offset >= segment->rs_length) { offset -= segment->rs_length;continue ; } dummy.rs_handle = segment->rs_handle; - dummy.rs_length =min_t (u32, length, segment->rs_length) - offset; + dummy.rs_length =min_t (u32, length, segment->rs_length - offset); dummy.rs_offset = segment->rs_offset + offset; ret =svc_rdma_build_read_segment (rqstp, head, &dummy);
@@ -1015,6 +1018,8 @@ static int svc_rdma_read_chunk_range(str head->rc_readbytes += dummy.rs_length; length -= dummy.rs_length; +if (!length) +break ; offset =0 ; }return ret;
Greg Kroah-Hartman
maintainerPATCH 392/713 svcrdma: Fix pcl_for_each_segment for empty chunks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit b7713a784c59515d0aba558c8f5df6a0164dd3a9 upstream.
When a parsed chunk list contains a chunk whose ch_segcount is zero, pcl_for_each_segment computes its inclusive upper bound as &chunk->ch_segments[ch_segcount - 1]. ch_segcount is u32, so the subtraction wraps to 0xFFFFFFFF and the bound lands far past the ch_segments flex array. The loop body then walks unrelated memory at sizeof(struct svc_rdma_segment) stride until it faults.
A zero-segcount chunk is reachable from the wire:xdr_check_write_chunk() only rejects segcount values greater than rc_maxpages, and pcl_alloc_write() links a freshly allocated chunk onto rc_write_pcl/rc_reply_pcl before its segment-fill loop runs, so a Write or Reply chunk advertising zero segments leaves ch_segcount == 0 on the list. When the transport has negotiated Send-With-Invalidate, svc_rdma_get_inv_rkey() iterates all four PCLs with pcl_for_each_segment and dereferences segment->rs_handle on each iteration, turning the underflow into an out-of-bounds read and a general protection fault.
xdr_check_write_list / xdr_check_reply_chunk
pcl_alloc_write()
chunk = pcl_alloc_chunk(...) /* ch_segcount = 0 */
list_add_tail(&chunk->ch_list, &pcl->cl_chunks)
/* fill loop iterates zero times for wire segcount 0 */
svc_rdma_get_inv_rkey()
pcl_for_each_chunk(rc_write_pcl)
pcl_for_each_segment(segment, chunk)
pos <= &ch_segments[0u - 1u] /* 0xFFFFFFFF */
segment->rs_handle /* OOB read -> GPF */Fix by switching the macro to a half-open upper bound that uses ch_segcount directly. For ch_segcount == 0 the loop start equals the loop end and the body is skipped; for ch_segcount > 0 the iteration range is unchanged. All six existing call sites in net/sunrpc/xprtrdma/svc_rdma_recvfrom.c and net/sunrpc/xprtrdma/svc_rdma_rw.c remain correct under the new bound, so no caller changes are needed.
--- include/linux/sunrpc/svc_rdma_pcl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/linux/sunrpc/svc_rdma_pcl.h +1 -1
@@ -97,7 +97,7 @@ pcl_next_chunk(const struct svc_rdma_pcl*/ #define pcl_for_each_segment (pos, chunk) \ for (pos = &(chunk)->ch_segments[0]; \ - pos <= &(chunk)->ch_segments[(chunk)->ch_segcount - 1]; \ + pos < &(chunk)->ch_segments[(chunk)->ch_segcount]; \ pos++)/**
Greg Kroah-Hartman
maintainerPATCH 393/713 svcrdma: Fix unmatched rn_unregister on failed accept
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 26190394c64c9429481fc88a4738f70bb92fb352 upstream.
When svc_rdma_accept() takes the errout path before rpcrdma_rn_register() has succeeded, the existing cleanup block calls rpcrdma_rn_unregister(dev, &newxprt->sc_rn) unconditionally. svcxprt_rdma is kzalloc'd, so on that path sc_rn.rn_index is 0 and sc_rn.rn_done is NULL; the unregister therefore xa_erase()s another caller's slot 0 and performs an unmatched kref_put() on the rpcrdma_device's rd_kref.
The same errout also brackets the cleanup with svc_xprt_get()/ svc_xprt_put() around the kref_init() birth reference. The kref goes 1 -> 2 -> 1 and never reaches 0, so the svcxprt_rdma (and the net/ns_tracker it pinned) is leaked on every failed accept.
rpcrdma_rn_register() writes rn->rn_done last, only after xa_alloc() and kref_get() have both succeeded, so rn_done == NULL is a natural "never registered" sentinel. Guard rpcrdma_rn_unregister() with an early return when rn_done is NULL, and clear rn_done before the matching xa_erase() so a repeated unregister is also a no-op.
With that guard in place, the accept errout drops the kref_init() birth reference via svc_xprt_put(), which dispatches svc_rdma_free(). Teardown of sc_qp, sc_sq_cq, sc_rq_cq, and sc_pd runs under existing IS_ERR/NULL guards in svc_rdma_free(); sc_rn is covered by the new rn_done sentinel; sc_cm_id is non-NULL on every errout path because svc_rdma_accept() dereferences it above the first goto errout.
svc_xprt_free() drops the module reference associated with the freed transport, and svc_handle_xprt() drops its pre-acquired reference when ->xpo_accept() returns NULL. Take a replacement module reference before svc_xprt_put() so the two module_put()s remain balanced.
The rn_done guard also covers svc_rdma_free()'s non-listener call to rpcrdma_rn_unregister() for transports whose register attempt failed or never ran.
--- net/sunrpc/xprtrdma/ib_client.c | 24 +++++++++++++++++++++++- net/sunrpc/xprtrdma/svc_rdma_transport.c | 28 +++++++++++++++++++++------- 2 files changed, 44 insertions(+), 8 deletions(-)
net/sunrpc/xprtrdma/ib_client.c +23 -1
@@ -51,7 +51,11 @@ static struct rpcrdma_device *rpcrdma_ge* to be invoked when the device is removed, unless this notification * is unregistered first. * -* On failure, a negative errno is returned. +* On failure, a negative errno is returned. rn->rn_done is left +* NULL on every failure path (it is assigned only after xa_alloc +* and kref_get have both succeeded), so the @rn may safely be +* passed to rpcrdma_rn_unregister() without a separate +* registered/unregistered flag in the caller. */ int rpcrdma_rn_register(struct ib_device *device, struct rpcrdma_notification *rn,
@@ -83,6 +87,10 @@ static void rpcrdma_rn_release(struct kr* rpcrdma_rn_unregister - stop device removal notifications * @device: monitored device * @rn: notification object that no longer wishes to be notified +* +* It is safe to call this on an @rn whose registration never +* completed or failed; rn_done == NULL is treated as +* never-registered and the call is a no-op. */ void rpcrdma_rn_unregister (struct ib_device *device,struct rpcrdma_notification *rn)
@@ -92,6 +100,20 @@ void rpcrdma_rn_unregister(struct ib_devif (!rd)return ; +/* +* rn_done is the registration sentinel: rpcrdma_rn_register +* assigns it last, after xa_alloc and kref_get have both +* succeeded. A NULL rn_done means this notification was +* never registered (or its registration failed) or has +* already been unregistered, and the call is a no-op. +* Without this guard, rn_index == 0 from a kzalloc'd +* parent would erase another caller's slot 0 and underflow +* rd_kref. +*/ +if (!rn->rn_done) +return ; + rn->rn_done =NULL ; +trace_rpcrdma_client_unregister (device, rn);xa_erase (&rd->rd_xa, rn->rn_index);kref_put (&rd->rd_kref, rpcrdma_rn_release);
net/sunrpc/xprtrdma/svc_rdma_transport.c +21 -7
@@ -43,6 +43,7 @@*/ #include <linux/interrupt.h> +#include <linux/module.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/spinlock.h>
@@ -599,13 +600,26 @@ static struct svc_xprt *svc_rdma_accept(return &newxprt->sc_xprt; errout: -/* Take a reference in case the DTO handler runs */ -svc_xprt_get (&newxprt->sc_xprt); -if (newxprt->sc_qp && !IS_ERR (newxprt->sc_qp)) -ib_destroy_qp (newxprt->sc_qp); -rdma_destroy_id (newxprt->sc_cm_id); -rpcrdma_rn_unregister (dev, &newxprt->sc_rn); -/* This call to put will destroy the transport */ +/* +* Drop the kref_init birth reference. svc_xprt_free will +* dispatch xpo_free = svc_rdma_free, which tears down sc_qp, +* sc_sq_cq, sc_rq_cq, and sc_pd under existing IS_ERR/NULL +* guards, and sc_rn under the rn_done sentinel guard inside +* rpcrdma_rn_unregister. +* +* sc_cm_id is destroyed unconditionally by svc_rdma_free; that +* is safe here because sc_cm_id is non-NULL by caller invariant +* on every path that reaches this errout: handle_connect_req +* installs newxprt->sc_cm_id before queueing the new xprt for +* accept, and svc_rdma_accept has already dereferenced it above +* the first goto errout. +* +* svc_handle_xprt() drops its pre-acquired module reference when +* ->xpo_accept() returns NULL. Take a replacement reference before +* freeing @newxprt, because svc_xprt_free() drops the module +* reference associated with @newxprt. +*/ +__module_get (newxprt->sc_xprt.xpt_class->xcl_owner);svc_xprt_put (&newxprt->sc_xprt);return NULL ; }
Greg Kroah-Hartman
maintainerPATCH 394/713 svcrdma: Reject connection when transport allocation fails
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit 0944462247dcb7de7622cdaaadf5f05c52707dab upstream.
handle_connect_req() returns without action when svc_rdma_create_xprt() fails to allocate the new transport. The CM core returns 0 for CONNECT_REQUEST events, so it does not destroy the new rdma_cm_id. Each allocation failure under memory pressure leaks one rdma_cm_id, and a remote peer driving connection attempts can amplify this.
Reject the connection by returning a non-zero status from the CM event handler, which tells the CM core to destroy the orphaned cm_id.
--- net/sunrpc/xprtrdma/svc_rdma_transport.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_transport.c +10 -6
@@ -246,12 +246,16 @@ svc_rdma_parse_connect_private(struct sv* structure for the listening endpoint. * * This function creates a new xprt for the new connection and enqueues it on -* the accept queue for the listent xprt. When the listen thread is kicked, it +* the accept queue for the listen xprt. When the listen thread is kicked, it * will call the recvfrom method on the listen xprt which will accept the new * connection. +* +* Return values: +* %0: Do not destroy @new_cma_id +* %1: Destroy @new_cma_id (allocation failure) */ -static void handle_connect_req (struct rdma_cm_id *new_cma_id, -struct rdma_conn_param *param) +static int handle_connect_req (struct rdma_cm_id *new_cma_id, +struct rdma_conn_param *param) {struct svcxprt_rdma *listen_xprt = new_cma_id->context;struct svcxprt_rdma *newxprt;
@@ -261,7 +265,7 @@ static void handle_connect_req(struct rd listen_xprt->sc_xprt.xpt_net, ibdev_to_node(new_cma_id->device));if (!newxprt) -return ; +return 1 ; newxprt->sc_cm_id = new_cma_id; new_cma_id->context = newxprt;svc_rdma_parse_connect_private (newxprt, param);
@@ -295,6 +299,7 @@ static void handle_connect_req(struct rdset_bit (XPT_CONN , &listen_xprt->sc_xprt.xpt_flags);svc_xprt_enqueue (&listen_xprt->sc_xprt); +return 0 ; }/**
@@ -318,8 +323,7 @@ static int svc_rdma_listen_handler(strucswitch (event->event) {case RDMA_CM_EVENT_CONNECT_REQUEST : -handle_connect_req (cma_id, &event->param.conn); -break ; +return handle_connect_req (cma_id, &event->param.conn);case RDMA_CM_EVENT_ADDR_CHANGE : listen_id =svc_rdma_create_listen_id (cma_rdma->xpt_net, sap, cma_xprt);
Greg Kroah-Hartman
maintainerPATCH 395/713 svcrdma: Reject inline replies that overflow the pull-up buffer
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit 0fbe20dfe74b783d255bf389a6ea77aa25dc7860 upstream.
An RPC-over-RDMA client can request a reply, such as an NFS READ payload, without providing a Write list or a Reply chunk to carry it. When such a reply needs more scatter/gather entries than the device's Send Queue supports, svc_rdma_pull_up_needed() selects pull-up and svc_rdma_pull_up_reply_msg() linearizes the whole reply into sctxt->sc_xprt_buf. That buffer is only sc_max_req_size bytes, while the reply on this path is bounded only by the client's request, so svc_rdma_xb_linearize() copies past the end of the buffer and corrupts adjacent slab memory. The oversized length is then stored in sc_sges[0].length and posted, so the device also reads beyond the mapped region.
The SGE-exhaustion branch is the only pull-up path that can exceed the buffer: the threshold branch pulls up only replies smaller than RPCRDMA_PULLUP_THRESH, and replies that fit the device's SGE budget are sent directly without linearization. Make svc_rdma_pull_up_needed() report -E2BIG when the reply it would pull up cannot fit sc_max_req_size, and fail the request with ERR_CHUNK as RFC 8166 Section 4.5.3 directs rather than dropping the connection.
The helper no longer answers a simple yes/no question: it now reports pull-up, no pull-up, or -E2BIG for a reply too large to linearize. Rename svc_rdma_pull_up_needed() to svc_rdma_check_pull_up() so its name no longer implies a boolean predicate.
--- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 47 +++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 15 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_sendto.c +32 -15
@@ -825,20 +825,21 @@ static int svc_rdma_xb_count_sges(const }/** -* svc_rdma_pull_up_needed - Determine whether to use pull-up +* svc_rdma_check_pull_up - Determine whether to use pull-up * @rdma: controlling transport * @sctxt: send_ctxt for the Send WR * @write_pcl: Write chunk list provided by client * @xdr: xdr_buf containing RPC message to transmit * * Returns: -* %true if pull-up must be used -* %false otherwise +* %1 if pull-up must be used +* %0 if pull-up is not needed +* %-E2BIG if the reply is too large to be pulled up */ -static bool svc_rdma_pull_up_needed (const struct svcxprt_rdma *rdma, -const struct svc_rdma_send_ctxt *sctxt, -const struct svc_rdma_pcl *write_pcl, -const struct xdr_buf *xdr) +static int svc_rdma_check_pull_up (const struct svcxprt_rdma *rdma, +const struct svc_rdma_send_ctxt *sctxt, +const struct svc_rdma_pcl *write_pcl, +const struct xdr_buf *xdr) {/* Resources needed for the transport header */ struct svc_rdma_pullup_data args = {
@@ -850,11 +851,22 @@ static bool svc_rdma_pull_up_needed(cons ret =pcl_process_nonpayloads (write_pcl, xdr, svc_rdma_xb_count_sges, &args);if (ret <0 ) -return false; +return 0 ;if (args.pd_length <RPCRDMA_PULLUP_THRESH ) -return true; -return args.pd_num_sges >= rdma->sc_max_send_sges; +return 1 ; +if (args.pd_num_sges < rdma->sc_max_send_sges) +return 0 ; + +/* +* The reply has too many SGEs to Send inline, so it has to be +* linearized into sc_xprt_buf. That buffer holds only +* sc_max_req_size bytes, so a larger reply cannot be pulled up. +* RFC 8166 Section 4.5.3 requires responding with ERR_CHUNK. +*/ +if (args.pd_length > rdma->sc_max_req_size) +return -E2BIG ; +return 1 ; }/**
@@ -910,7 +922,7 @@ static int svc_rdma_xb_linearize(const s* Assemble the elements of @xdr into the transport header buffer. * * Assumptions: -* pull_up_needed has determined that @xdr will fit in the buffer. +* check_pull_up has determined that @xdr will fit in the buffer. * * Returns: * %0 if pull-up was successful
@@ -945,6 +957,7 @@ static int svc_rdma_pull_up_reply_msg(co* * Returns: * %0 if DMA mapping was successful. +* %-E2BIG if the reply is too large to be pulled up * %-EMSGSIZE if a buffer manipulation problem occurred * %-EIO if DMA mapping failed *
@@ -960,6 +973,7 @@ int svc_rdma_map_reply_msg(struct svcxpr .md_rdma = rdma, .md_ctxt = sctxt, }; +int ret;/* Set up the (persistently-mapped) transport header SGE. */ sctxt->sc_send_wr.num_sge =1 ;
@@ -974,7 +988,10 @@ int svc_rdma_map_reply_msg(struct svcxpr/* For pull-up, svc_rdma_send() will sync the transport header. * No additional DMA mapping is necessary. */ -if (svc_rdma_pull_up_needed (rdma, sctxt, write_pcl, xdr)) + ret =svc_rdma_check_pull_up (rdma, sctxt, write_pcl, xdr); +if (ret <0 ) +return ret; +if (ret)return svc_rdma_pull_up_reply_msg (rdma, sctxt, write_pcl, xdr); return pcl_process_nonpayloads(write_pcl, xdr,
@@ -1162,7 +1179,7 @@ int svc_rdma_sendto(struct svc_rqst *rqs &rctxt->rc_reply_pcl, sctxt, &rqstp->rq_res);if (ret <0 ) - goto reply_chunk; + goto send_err; rc_size = ret; }
@@ -1183,10 +1200,10 @@ int svc_rdma_sendto(struct svc_rqst *rqs ret =svc_rdma_send_reply_msg (rdma, sctxt, rctxt, rqstp);if (ret <0 ) - goto put_ctxt; + goto send_err;return 0 ; -reply_chunk: +send_err:if (ret != -E2BIG && ret != -EINVAL ) goto put_ctxt;
Greg Kroah-Hartman
maintainerPATCH 396/713 svcrdma: Reject oversized Read segments at decode time
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit af6f0e06bed818ee7fc8b869915964410020a1c5 upstream.
The RPC/RDMA Read list decoder stores wire-supplied segment lengths without validation. xdr_count_read_segments() checks 4-byte alignment for non-zero position values but does not cap the segment length.
An oversized rs_length reaches svc_rdma_build_read_segment(), which derives nr_bvec from it and can drive a large dynamic bvec allocation before verifying that enough rq_pages remain. If the post-allocation page-overrun guard fires, the freshly acquired rw context is not returned, leaking the resource.
Reject any segment whose length exceeds the receive context's page budget during Read list decoding, consistent with how xdr_check_write_chunk() bounds Write segment counts against rc_maxpages. Also return the rw context on the existing post-allocation overrun path in svc_rdma_build_read_segment(), keeping that defensive guard balanced.
--- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 2 ++ net/sunrpc/xprtrdma/svc_rdma_rw.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +2 -0
@@ -458,6 +458,8 @@ static bool xdr_count_read_segments(struct svc_rdma_recv_ctxt *rctxt, __be32 *p)xdr_decode_read_segment (p, &position, &handle, &length, &offset); +if (length > rctxt->rc_maxpages <<PAGE_SHIFT ) +return false;if (position) {if (position &3 )return false;
net/sunrpc/xprtrdma/svc_rdma_rw.c +3 -2
@@ -795,7 +795,7 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp, len -= seg_len;if (len && ((head->rc_curpage +1 ) > rqstp->rq_maxpages)) - goto out_overrun; + goto out_put; } ret = svc_rdma_rw_ctx_init(rdma, ctxt, segment->rs_offset,
@@ -809,7 +809,8 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp, cc->cc_sqecount += ret;return 0 ; -out_overrun: +out_put: +svc_rdma_put_rw_ctxt (rdma, ctxt);trace_svcrdma_page_overrun_err (&cc->cc_cid, head->rc_curpage);return -EINVAL ; }
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 397/713 svcrdma: Reject Read lists that exceed the page budget
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit 0ca487abb3bdf581851664b5db21f364caf57682 upstream.
Individual Read segment lengths are validated at decode time, but nothing prevents a requester from sending multiple segments whose cumulative length exceeds the rq_pages array budget. When one segment fills the page array exactly, the runtime guard in svc_rdma_build_read_segment() is bypassed because len reaches zero. A subsequent segment then accesses the NULL sentinel slot at rq_pages[rq_maxpages], resulting in a NULL pointer dereference during DMA mapping.
Accumulate pages across all Read segments and reject the message at decode time when the total would overflow the page budget.
--- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +9 -3
@@ -440,11 +440,14 @@ static void svc_rdma_build_arg_xdr(struc* to the first byte past the Read list. rc_read_pcl and * rc_call_pcl cl_count fields are set to the number of * Read segments in the list. -* %false: Read list is corrupt. @rctxt's xdr_stream is left in an -* unknown state. +* %false: Read list is corrupt or exceeds the page budget. @rctxt's +* xdr_stream is left in an unknown state. */ static bool xdr_count_read_segments (struct svc_rdma_recv_ctxt *rctxt,__be32 *p) { +unsigned int maxlen = rctxt->rc_maxpages <<PAGE_SHIFT ; +unsigned int total_len =0 ; + rctxt->rc_call_pcl.cl_count =0 ; rctxt->rc_read_pcl.cl_count =0 ;while (xdr_item_is_present (p)) {
@@ -458,7 +461,10 @@ static bool xdr_count_read_segments(struxdr_decode_read_segment (p, &position, &handle, &length, &offset); -if (length > rctxt->rc_maxpages <<PAGE_SHIFT ) +if (length > maxlen) +return false; + total_len += length; +if (PAGE_ALIGN (total_len) > maxlen)return false;if (position) {if (position &3 )
Greg Kroah-Hartman
maintainerPATCH 398/713 svcrdma: Reject Write/Reply chunks with segcount 0
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 9808eb7656666acc7291bae9ab6b987bd16e47e0 upstream.
A peer can send a Write or Reply chunk whose segcount field is zero. xdr_check_write_chunk() only rejects segcount > rc_maxpages, so zero passes the range check, and xdr_inline_decode(stream, 0) returns the current (non-NULL) cursor without advancing. The function returns true and pcl_alloc_write() then links a struct svc_rdma_chunk with ch_segcount == 0 onto rc_write_pcl or rc_reply_pcl.
An earlier patch in this series made pcl_for_each_segment() safe for ch_segcount == 0, so this no longer drives the memory walk it used to. Rejecting the malformed frame at the decode boundary is still worthwhile as defense in depth: it keeps degenerate zero-segment chunks off the parsed chunk lists entirely, so any future consumer that walks ch_segments directly cannot observe one, and it makes the zero-floor easy to backport to trees where the macro change is more intrusive. RFC 8166 has no meaning for a Write/Reply chunk that describes no remote buffer, so no legitimate client is affected.
xdr_check_reply_chunk() funnels Reply chunks through xdr_check_write_chunk() and inherits the same rejection.
pcl_alloc_write() also links each chunk onto the parsed chunk list before filling its segment array. If a future change weakens the segcount-0 rejection, an incomplete chunk is visible to consumers during the fill loop. Reorder so that list_add_tail() follows the segment fill loop, ensuring only fully-populated chunks appear on the list.
--- net/sunrpc/xprtrdma/svc_rdma_pcl.c | 2 +- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_pcl.c +1 -1
@@ -213,7 +213,6 @@ bool pcl_alloc_write(struct svc_rdma_rec chunk =pcl_alloc_chunk (segcount,0 );if (!chunk)return false; -list_add_tail (&chunk->ch_list, &pcl->cl_chunks);for (j =0 ; j < segcount; j++) { segment = &chunk->ch_segments[j];
@@ -225,6 +224,7 @@ bool pcl_alloc_write(struct svc_rdma_rec chunk->ch_length += segment->rs_length; chunk->ch_segcount++; } +list_add_tail (&chunk->ch_list, &pcl->cl_chunks); }return true; }
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +6 -3
@@ -516,10 +516,13 @@ static bool xdr_check_write_chunk(structreturn false;/* Before trusting the segcount value enough to use it in -* a computation, perform a simple range check. This is an -* arbitrary but sensible limit (ie, not architectural). +* a computation, perform a simple range check. A zero +* segcount describes no remote buffer and is rejected so +* downstream consumers never see a degenerate ch_segcount==0 +* chunk. The upper bound is an arbitrary but sensible limit +* (ie, not architectural). */ -if (unlikely (segcount > rctxt->rc_maxpages)) +if (segcount ==0 ||unlikely (segcount > rctxt->rc_maxpages))return false; p = xdr_inline_decode(&rctxt->rc_stream,
Greg Kroah-Hartman
maintainerPATCH 399/713 svcrdma: Use svc_xprt_put to free listener on create failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit e346ef7bcb137f50c49f969330ab7dcf64ea1654 upstream.
svc_rdma_create() calls kfree(cma_xprt) when svc_rdma_create_listen_id() fails. svc_xprt_init() has already acquired a net namespace reference via get_net_track(); kfree bypasses svc_xprt_free() which releases it.
Replace the kfree() with svc_xprt_put() so the kref_init birth reference drops to zero and svc_xprt_free() dispatches svc_rdma_free() to clean up properly. sc_cm_id is still NULL at that point; the preceding patch added the necessary NULL guard in svc_rdma_free().
svc_xprt_free() also drops the module reference via module_put(), but the caller _svc_xprt_create() does the same on xpo_create failure, double-putting the single try_module_get() it acquired. Take a compensating __module_get() before the svc_xprt_put() to keep the count balanced, matching the convention in svc_rdma_accept()'s error path.
--- net/sunrpc/xprtrdma/svc_rdma_transport.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
net/sunrpc/xprtrdma/svc_rdma_transport.c +7 -1
@@ -393,7 +393,13 @@ static struct svc_xprt *svc_rdma_create( listen_id =svc_rdma_create_listen_id (net, sa, cma_xprt);if (IS_ERR (listen_id)) { -kfree (cma_xprt); +/* _svc_xprt_create() acquired one module reference and +* puts it on xpo_create failure. svc_xprt_free() puts +* a second one when the kref drops to zero. Take a +* compensating reference so both puts are balanced. +*/ +__module_get (cma_xprt->sc_xprt.xpt_class->xcl_owner); +svc_xprt_put (&cma_xprt->sc_xprt);return ERR_CAST (listen_id); } cma_xprt->sc_cm_id = listen_id;
Greg Kroah-Hartman
maintainerPATCH 400/713 svcrdma: Validate Read chunk positions before reconstruction
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit 3779b7b9e7d1c8ba4738f9d327de3b0288cefe9b upstream.
The RPC/RDMA Read chunk position field is supplied by the remote client and stored verbatim in the parsed chunk list. xdr_count_read_segments() checks only 4-byte alignment; it never compares the position against the received inline body length.
In the single-chunk path, svc_rdma_read_complete_one() splits the head and tail kvecs at ch_position. A position past the inline body underflows the tail length, exposing adjacent slab memory to the upper XDR decoder.
In the multi-chunk path, svc_rdma_read_multiple_chunks() computes gap lengths between chunks as unsigned subtractions from ch_position. Overlapping Read chunks cause these subtractions to underflow. A final position past the inline body likewise underflows the trailing gap length. svc_rdma_copy_inline_range() then copies past the receive buffer into request pages that are returned to the client through the Reply channel.
Bound inline-range copies in svc_rdma_copy_inline_range() against the decoded inline RPC body saved in rc_saved_arg. Reject a single Read chunk positioned beyond that body, and reject multi-chunk lists where accumulated read bytes exceed the next chunk's position. Apply the same position and overlap checks in the call-chunk interleaving path.
--- net/sunrpc/xprtrdma/svc_rdma_rw.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_rw.c +30 -8
@@ -848,7 +848,7 @@ static int svc_rdma_build_read_chunk(str* svc_rdma_copy_inline_range - Copy part of the inline content into pages * @rqstp: RPC transaction context * @head: context for ongoing I/O -* @offset: offset into the Receive buffer of region to copy +* @offset: offset into the inline content of region to copy * @remaining: length of region to copy * * Take a page at a time from rqstp->rq_pages and copy the inline
@@ -865,9 +865,13 @@ static int svc_rdma_copy_inline_range(stunsigned int offset, unsigned int remaining) { - unsigned char *dst, *src = head->rc_recv_buf; + unsigned char *dst, *src = head->rc_saved_arg.head[0].iov_base; +unsigned int inline_len = head->rc_saved_arg.head[0 ].iov_len;unsigned int page_no, numpages; +if (offset > inline_len || remaining > inline_len - offset) +return -EINVAL ; + numpages =PAGE_ALIGN (head->rc_pageoff + remaining) >>PAGE_SHIFT ;for (page_no =0 ; page_no < numpages; page_no++) {unsigned int page_len;
@@ -918,9 +922,10 @@ svc_rdma_read_multiple_chunks(struct svc {const struct svc_rdma_pcl *pcl = &head->rc_read_pcl;struct svc_rdma_chunk *chunk, *next; -unsigned int start, length; +unsigned int inline_len, start, length;int ret; + inline_len = head->rc_saved_arg.head[0 ].iov_len; start =0 ; chunk =pcl_first_chunk (pcl); length = chunk->ch_position;
@@ -938,6 +943,8 @@ svc_rdma_read_multiple_chunks(struct svcbreak ; start += length; +if (head->rc_readbytes > next->ch_position) +return -EINVAL ; length = next->ch_position - head->rc_readbytes; ret =svc_rdma_copy_inline_range (rqstp, head, start, length);if (ret <0 )
@@ -945,7 +952,9 @@ svc_rdma_read_multiple_chunks(struct svc } start += length; - length = head->rc_byte_len - start; +if (start > inline_len) +return -EINVAL ; + length = inline_len - start;return svc_rdma_copy_inline_range (rqstp, head, start, length); }
@@ -970,8 +979,12 @@ svc_rdma_read_multiple_chunks(struct svcstatic int svc_rdma_read_data_item (struct svc_rqst *rqstp,struct svc_rdma_recv_ctxt *head) { -return svc_rdma_build_read_chunk (rqstp, head, -pcl_first_chunk (&head->rc_read_pcl)); +struct svc_rdma_chunk *chunk =pcl_first_chunk (&head->rc_read_pcl); + +if (chunk->ch_position > head->rc_saved_arg.head[0 ].iov_len) +return -EINVAL ; + +return svc_rdma_build_read_chunk (rqstp, head, chunk); }/**
@@ -1045,14 +1058,17 @@ static int svc_rdma_read_call_chunk(strupcl_first_chunk (&head->rc_call_pcl);const struct svc_rdma_pcl *pcl = &head->rc_read_pcl;struct svc_rdma_chunk *chunk, *next; -unsigned int start, length; +unsigned int call_len, start, length;int ret;if (pcl_is_empty (pcl))return svc_rdma_build_read_chunk (rqstp, head, call_chunk); + call_len = call_chunk->ch_length; start =0 ; chunk =pcl_first_chunk (pcl); +if (chunk->ch_position > call_len) +return -EINVAL ; length = chunk->ch_position; ret =svc_rdma_read_chunk_range (rqstp, head, call_chunk, start, length);
@@ -1069,6 +1085,10 @@ static int svc_rdma_read_call_chunk(strubreak ; start += length; +if (next->ch_position > call_len) +return -EINVAL ; +if (head->rc_readbytes > next->ch_position) +return -EINVAL ; length = next->ch_position - head->rc_readbytes; ret =svc_rdma_read_chunk_range (rqstp, head, call_chunk, start, length);
@@ -1077,7 +1097,9 @@ static int svc_rdma_read_call_chunk(stru } start += length; - length = call_chunk->ch_length - start; +if (start > call_len) +return -EINVAL ; + length = call_len - start;return svc_rdma_read_chunk_range (rqstp, head, call_chunk, start, length); }
Greg Kroah-Hartman
maintainerPATCH 401/713 udf: reject VAT indexes equal to the entry count
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Lee <[email protected]>
commit cac0cb07f29ccfb373fd4a36c81e908ef3ce608c upstream.
UDF 1.50 virtual partition mapping uses the VAT as an array of physical block mappings. s_num_entries stores the number of entries in that array, not the highest valid index. The valid VAT indexes are therefore below s_num_entries.
udf_get_pblock_virt15() currently rejects only indexes greater than s_num_entries. A crafted image can request index s_num_entries, pass the bounds check, and make the kernel read one entry past the allocated VAT table.
Change the check to reject block >= s_num_entries, so the count is handled as an exclusive upper bound.
A crafted UDF image reproduced this on origin/master commit 0e35b9b6ec0ffcc5e23cbdec09f5c622ad532b53 with a KASAN slab-out-of-bounds report in udf_get_pblock_virt15().
Trail of Bits has a reproducer that triggers kernel panic demonstrating the bug, and can share it if needed.
--- fs/udf/partition.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/udf/partition.c +1 -1
@@ -55,7 +55,7 @@ uint32_t udf_get_pblock_virt15(struct su map = &sbi->s_partmaps[partition]; vdata = &map->s_type_specific.s_virtual; -if (block > vdata->s_num_entries) { +if (block >= vdata->s_num_entries) {udf_debug ("Trying to access block beyond end of VAT (%u max %u)\n" , block, vdata->s_num_entries);return 0xFFFFFFFF ;
Greg Kroah-Hartman
maintainerPATCH 402/713 wifi: ath6kl: clamp assoc request/response lengths before subtracting IE offsets
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit 3bbd05723d15dd06f0560bcd94fbf9a91b5f5613 upstream.
ath6kl_cfg80211_connect_event() subtracts fixed IE offsets from assoc_req_len (-= 4) and assoc_resp_len (-= 6), both u8, with no lower bound. The aggregate check recently added to ath6kl_wmi_connect_event_rx() bounds the declared lengths from above (their sum must fit the received event), but an assoc request/response shorter than its fixed offset still underflows here: the u8 wraps to ~250, and cfg80211_connect_result() / cfg80211_roamed() then treat that wrapped value as the IE length and copy that many bytes out of the small assoc_info buffer to user space via nl80211, disclosing adjacent slab memory.
Clamp both lengths to their offsets before subtracting.
Found by 0sec (https://0sec.ai) using automated source analysis; the missing lower bound is evident from source. Compile-tested.
--- drivers/net/wireless/ath/ath6kl/cfg80211.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/net/wireless/ath/ath6kl/cfg80211.c +5 -0
@@ -754,6 +754,11 @@ void ath6kl_cfg80211_connect_event(strucu8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len + assoc_resp_ie_offset; +if (assoc_req_len < assoc_req_ie_offset) + assoc_req_len = assoc_req_ie_offset; +if (assoc_resp_len < assoc_resp_ie_offset) + assoc_resp_len = assoc_resp_ie_offset; + assoc_req_len -= assoc_req_ie_offset; assoc_resp_len -= assoc_resp_ie_offset;
Greg Kroah-Hartman
maintainerPATCH 403/713 wifi: mt76: mt7925: cancel pending mlo_pm_work
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Guan <[email protected]>
commit 2889e84282dda147f10b10d94cf0efd90a349c53 upstream.
If the device is reset, suspended or unregistered within that window, the pending work can still run and access vif/bss data that may already be freed, or send MCU commands while the firmware is not available.
Add cancel_delayed_work_sync(&dev->mlo_pm_work) in all relevant teardown and suspend paths:
- mt7925_mac_reset_work() (chip reset recovery) - mt7925e_unregister_device() (PCIe unbind) - mt7925_pci_suspend() (PCIe bus suspend) - mt7925_suspend() (mac80211 suspend) - mt7925u_suspend() (USB bus / runtime suspend)
This ensures the work is stopped before the device state becomes invalid.
--- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 1 + drivers/net/wireless/mediatek/mt76/mt7925/main.c | 1 + drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 2 ++ drivers/net/wireless/mediatek/mt76/mt7925/usb.c | 1 + 4 files changed, 5 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/mac.c +1 -0
@@ -1323,6 +1323,7 @@ void mt7925_mac_reset_work(struct work_scancel_delayed_work_sync (&dev->mphy.mac_work);cancel_delayed_work_sync (&pm->ps_work); +cancel_delayed_work_sync (&dev->mlo_pm_work);cancel_work_sync (&pm->wake_work);for (i =0 ; i <10 ; i++) {
drivers/net/wireless/mediatek/mt76/mt7925/main.c +1 -0
@@ -1640,6 +1640,7 @@ static int mt7925_suspend(struct ieee802cancel_delayed_work_sync (&phy->mt76->mac_work);cancel_delayed_work_sync (&dev->pm.ps_work); +cancel_delayed_work_sync (&dev->mlo_pm_work);mt76_connac_free_pending_tx_skbs (&dev->pm,NULL );mt792x_mutex_acquire (dev);
drivers/net/wireless/mediatek/mt76/mt7925/pci.c +2 -0
@@ -48,6 +48,7 @@ static void mt7925e_unregister_device(stmt76_for_each_q_rx (&dev->mt76, i)napi_disable (&dev->mt76.napi[i]);cancel_delayed_work_sync (&pm->ps_work); +cancel_delayed_work_sync (&dev->mlo_pm_work);cancel_work_sync (&pm->wake_work);cancel_work_sync (&dev->reset_work);
@@ -517,6 +518,7 @@ static int mt7925_pci_suspend(struct dev dev->hif_resumed = false;flush_work (&dev->reset_work);cancel_delayed_work_sync (&pm->ps_work); +cancel_delayed_work_sync (&dev->mlo_pm_work);cancel_work_sync (&pm->wake_work);mt7925_roc_abort_sync (dev);
drivers/net/wireless/mediatek/mt76/mt7925/usb.c +1 -0
@@ -269,6 +269,7 @@ static int mt7925u_suspend(struct usb_in pm->suspended = true; dev->hif_resumed = false;flush_work (&dev->reset_work); +cancel_delayed_work_sync (&dev->mlo_pm_work);mt76_connac_mcu_set_hif_suspend (&dev->mt76, true, false); ret = wait_event_timeout(dev->wait,
Greg Kroah-Hartman
maintainerPATCH 404/713 staging: media: tegra-video: fix of_node_put() on VIP parse errors
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hao-Qun Huang <[email protected]>
commit 7393372f79db940acff206b43e2905685a0c57ad upstream.
tegra_vip_channel_of_parse() initializes np from dev->of_node without taking a reference, but its error paths drop one through the err_node_put label. This underflows the refcount of the VIP device's OF node when endpoint parsing fails on a malformed device tree.
The only reference the function takes on np is the success-path of_node_get() stored in vip->chan.of_node, and that one is already released by the tegra_vip_init() error path and by tegra_vip_exit().
Return errors directly instead of jumping to the bogus cleanup label.
--- drivers/staging/media/tegra-video/vip.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
drivers/staging/media/tegra-video/vip.c +3 -7
@@ -126,7 +126,7 @@ static int tegra_vip_channel_of_parse(stif (!ep) { err = -EINVAL ;dev_err_probe (dev, err,"%pOF: error getting endpoint node\n" , np); - goto err_node_put; +return err; } fwh =of_fwnode_handle (ep);
@@ -134,14 +134,14 @@ static int tegra_vip_channel_of_parse(stof_node_put (ep);if (err) {dev_err_probe (dev, err,"%pOF: failed to parse v4l2 endpoint\n" , np); - goto err_node_put; +return err; } num_pads =of_graph_get_endpoint_count (np);if (num_pads !=TEGRA_VIP_PADS_NUM ) { err = -EINVAL ;dev_err_probe (dev, err,"%pOF: need 2 pads, got %d\n" , np, num_pads); - goto err_node_put; +return err; } vip->chan.of_node =of_node_get (np);
@@ -149,10 +149,6 @@ static int tegra_vip_channel_of_parse(st vip->chan.pads[TEGRA_VIP_PAD_SOURCE ].flags =MEDIA_PAD_FL_SOURCE ;return 0 ; - -err_node_put: -of_node_put (np); -return err; }static int tegra_vip_channel_init (struct tegra_vip *vip)
Greg Kroah-Hartman
maintainerPATCH 405/713 staging: media: tegra-video: vi: fix probe failure on skipped last port
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hao-Qun Huang <[email protected]>
commit ae15adeed9f7ec54989175fe3c9e0815186821bc upstream.
tegra_vi_channels_alloc() iterates over port nodes and skips those whose reg property cannot be read or whose remote endpoint fails v4l2_fwnode_endpoint_parse(), leaving the negative result of the failed call in ret. If that happens on the last port node, the loop ends with ret still negative and tegra_vi_init() fails the whole VI probe.
The same defective port earlier in the ports node is skipped silently, so probing succeeds or fails depending on the order of the port nodes. The CSI equivalent, tegra_csi_channels_alloc(), returns 0 unconditionally after its loop and does not have this problem.
Use a separate variable for the per-port checks so that only fatal errors end up in ret.
--- drivers/staging/media/tegra-video/vi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/staging/media/tegra-video/vi.c +5 -4
@@ -1257,6 +1257,7 @@ static int tegra_vi_channels_alloc(strucstruct device_node *parent;struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type =0 };unsigned int lanes; +int err;int ret =0 ; ports =of_get_child_by_name (node,"ports" );
@@ -1267,8 +1268,8 @@ static int tegra_vi_channels_alloc(strucif (!of_node_name_eq (port,"port" ))continue ; - ret =of_property_read_u32 (port,"reg" , &port_num); -if (ret <0 ) + err =of_property_read_u32 (port,"reg" , &port_num); +if (err <0 )continue ;if (port_num > vi->soc->vi_max_channels) {
@@ -1289,10 +1290,10 @@ static int tegra_vi_channels_alloc(struc ep =of_graph_get_endpoint_by_regs (parent,0 ,0 );of_node_put (parent); - ret =v4l2_fwnode_endpoint_parse (of_fwnode_handle (ep), + err =v4l2_fwnode_endpoint_parse (of_fwnode_handle (ep), &v4l2_ep);of_node_put (ep); -if (ret) +if (err)continue ; lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
Greg Kroah-Hartman
maintainerPATCH 406/713 media: staging/ipu7: fix async notifier UAF on probe error path
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cong Nguyen <[email protected]>
commit d7f48aa7d60c65d3e6d5312c27f17d5525a245fb upstream.
isys_register_devices() registers the V4L2 async notifier via isys_notifier_init(). If a subsequent probe step such as isys_fw_log_init() fails, isys_probe() jumps to the out_cleanup label which only calls isys_unregister_devices(). That helper tears down the video devices, subdevices, V4L2 device and media device, but never unregisters or cleans up the async notifier.
As a result the notifier stays chained in the global notifier_list while the enclosing struct ipu7_isys is freed by devres, leading to list corruption and a use-after-free the next time the list is walked.
The remove path already does the right thing by calling isys_notifier_cleanup() before isys_unregister_devices(). Mirror that on the probe error path so the notifier is unregistered and cleaned up before the device is torn down.
--- drivers/staging/media/ipu7/ipu7-isys.c | 1 + 1 file changed, 1 insertion(+)
drivers/staging/media/ipu7/ipu7-isys.c +1 -0
@@ -773,6 +773,7 @@ static int isys_probe(struct auxiliary_dreturn 0 ; out_cleanup: +isys_notifier_cleanup (isys);isys_unregister_devices (isys); out_cleanup_fw:ipu7_fw_isys_release (isys);
Greg Kroah-Hartman
maintainerPATCH 407/713 sched/core: Handle pick_task() releasing the rq lock
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
commit c10b216a072ff5c57bc880a05f87eb519aecc529 upstream.
Core scheduling's pick_next_task() breaks when a ->pick_task() implementation can release the rq lock. The selection state derived on entry is only valid while the lock is held continuously. Once a pick can drop the lock, an interleaving selection can invalidate all of it: the single-CPU fast path can commit an uncookied pick although the core went cookied during the release, and forceidle committed by the interleaving selection skews the restarted pass's accounting.
Fix it by restarting the whole selection when a pick returns RETRY_TASK after releasing the lock: a single restart point above the state derivation replaces the per-loop restart labels, so a retry picks up state committed by interleaving selections and accounts and resets forceidle like a fresh selection would.
need_sync and fi_before latch across retries. Clock validity can't be re-derived - there is no program-ordered way to tell whether the own and core rq clocks are still updated after the lock was released, as other lockers' pin cycles may or may not have invalidated them. When restarting, clear core_clock_updated so that the sibling loop re-updates the core rq, and update the own rq clock if invalidated.
--- kernel/sched/core.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)
kernel/sched/core.c +19 -8
@@ -6223,7 +6223,7 @@ pick_next_task(struct rq *rq, struct rq_unsigned long cookie;int i, cpu, occ =0 ;struct rq *rq_i; -bool need_sync; +bool need_sync = false;if (!sched_core_enabled (rq))return __pick_next_task (rq, rf);
@@ -6266,7 +6266,9 @@ pick_next_task(struct rq *rq, struct rq_prev_balance (rq, rf); smt_mask =cpu_smt_mask (cpu); - need_sync = !!rq->core->core_cookie; + +restart: + need_sync |= !!rq->core->core_cookie;/* reset state */ rq->core->core_cookie =0UL ;
@@ -6301,10 +6303,15 @@ pick_next_task(struct rq *rq, struct rq_* and there are no cookied tasks running on siblings. */ if (!need_sync) { -restart_single: next =pick_task (rq, rf); -if (unlikely (next ==RETRY_TASK )) - goto restart_single; +if (unlikely (next ==RETRY_TASK )) { +/* rq lock may have been dropped, clocks invalidated */ + core_clock_updated = false; +if (!(rq->clock_update_flags &RQCF_UPDATED )) +update_rq_clock (rq); + goto restart; + } +if (!next->core_cookie) { rq->core_pick =NULL ; rq->core_dl_server =NULL ;
@@ -6324,7 +6331,6 @@ restart_single:* * Tie-break prio towards the current CPU */ -restart_multi: max =NULL ;for_each_cpu_wrap (i, smt_mask, cpu) { rq_i =cpu_rq (i);
@@ -6338,8 +6344,13 @@ restart_multi:update_rq_clock (rq_i); p =pick_task (rq_i, rf); -if (unlikely (p ==RETRY_TASK )) - goto restart_multi; +if (unlikely (p ==RETRY_TASK )) { +/* rq lock may have been dropped, clocks invalidated */ + core_clock_updated = false; +if (!(rq->clock_update_flags &RQCF_UPDATED )) +update_rq_clock (rq); + goto restart; + } rq_i->core_pick = p; rq_i->core_dl_server = rq_i->dl_server;
Greg Kroah-Hartman
maintainerPATCH 408/713 sched/core: Make core-sched flips wait for in-flight selections
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
commit f3629c63a4af3e491381780bc6c123cb498c4c40 upstream.
Core scheduling's pick_next_task() operates on all sibling rqs under one acquisition of the shared core-wide lock. A ->pick_task() that releases the rq lock leaves every sibling __lock momentarily free, letting __sched_core_flip(false) complete mid-selection and rebind rq_lockp() under it. The selection resumes on the split locks, touching sibling state it no longer protects, and __schedule() finally releases a lock that was never taken while leaking the one that was.
Count in-flight core-wide selections in the leader's rq->core_pick_in_flight and make __sched_core_flip() wait for the count to drain. The count only changes under the shared lock, which the flip holds while sampling, so no other ordering is needed. The wait can repeat while selections overlap, but the flip backs off between samples and flips are rare cookie-lifetime events.
sched_core_cpu_deactivate() moves the count to the new leader - a stale copy left behind would bias it forever if that CPU later returns as its own leader.
--- kernel/sched/core.c | 22 ++++++++++++++++++++++ kernel/sched/sched.h | 1 + 2 files changed, 23 insertions(+)
kernel/sched/core.c +22 -0
@@ -443,6 +443,17 @@ static void __sched_core_flip(bool enablsched_core_lock (cpu, &flags); +/* +* A core-wide selection may have the shared rq lock temporarily +* released by a lock-dropping ->pick_task(). Flipping would +* rebind rq_lockp() under it. Wait it out. +*/ +while (cpu_rq (cpu)->core->core_pick_in_flight) { +sched_core_unlock (cpu, &flags); +cpu_relax (); +sched_core_lock (cpu, &flags); + } +for_each_cpu (t, smt_mask)cpu_rq (t)->core_enabled = enabled;
@@ -6242,6 +6253,8 @@ pick_next_task(struct rq *rq, struct rq_return __pick_next_task (rq, rf); } + rq->core->core_pick_in_flight++; +/* * If there were no {en,de}queues since we picked (IOW, the task * pointers are all still valid), and we haven't scheduled the last
@@ -6456,6 +6469,7 @@ restart: } out_set_next: + rq->core->core_pick_in_flight--;put_prev_set_next_task (rq, rq->donor, next);if (rq->core->core_forceidle_count && next == rq->idle)queue_core_balance (rq);
@@ -6651,6 +6665,13 @@ static void sched_core_cpu_deactivate(un core_rq->core_forceidle_occupation = rq->core_forceidle_occupation;/* +* A stale leftover would bias the count forever if this CPU later +* returns as its own leader. Move, don't copy. +*/ + core_rq->core_pick_in_flight = rq->core_pick_in_flight; + rq->core_pick_in_flight =0 ; + +/* * Accounting edge for forced idle is handled in pick_next_task(). * Don't need another one here, since the hotplug thread shouldn't * have a cookie.
@@ -9064,6 +9085,7 @@ void __init sched_init(void) rq->core_forceidle_count =0 ; rq->core_forceidle_occupation =0 ; rq->core_forceidle_start =0 ; + rq->core_pick_in_flight =0 ; rq->core_cookie =0UL ;#endif
kernel/sched/sched.h +1 -0
@@ -1358,6 +1358,7 @@ struct rq {unsigned int core_forceidle_seq;unsigned int core_forceidle_occupation;u64 core_forceidle_start; +unsigned int core_pick_in_flight;#endif /* CONFIG_SCHED_CORE */ /* Scratch cpumask to be temporarily used under rq_lock */
Greg Kroah-Hartman
maintainerPATCH 409/713 sched_ext: Replace SCX_RQ_BAL_KEEP with a dispatch verdict return
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
commit ffaab58d217581cb75353168f8812a16e10463fc upstream.
SCX_RQ_BAL_KEEP tells the pick to keep running the previous task, a leftover from when balancing and picking were separate operations. An rq-level flag only works while dispatches and picks pair up one to one, which core scheduling breaks: selections interleave through dispatch's lock drops and a pick can consume a stale flag, keeping a task that has since been dequeued. Fixing core scheduling support requires the decision to travel with the dispatch that made it. Make scx_dispatch_sched() and balance_one() return an explicit verdict instead and drop the flag's plumbing from the tools autogen enum headers.
Also factor the pick-side invocation, its follow-up queueing and the post-dispatch checks out of do_pick_task_scx() into dispatch_pick(). No functional changes intended.
v2: Drop the SCX_RQ_BAL_KEEP plumbing from the tools autogen enum headers
as well (Andrea).
--- kernel/sched/ext/ext.c | 124 +++++++++++------- kernel/sched/sched.h | 1 - .../sched_ext/include/scx/enum_defs.autogen.h | 1 - .../sched_ext/include/scx/enums.autogen.bpf.h | 3 - tools/sched_ext/include/scx/enums.autogen.h | 1 - 5 files changed, 73 insertions(+), 57 deletions(-)
kernel/sched/ext/ext.c +73 -51
@@ -2774,12 +2774,19 @@ static inline void maybe_queue_balance_callback(struct rq *rq) rq->scx.flags &= ~SCX_RQ_BAL_CB_PENDING ; } +/* what dispatch concluded, consumed by the pick that follows */ +enum scx_dsp_verdict { +SCX_DSP_NONE ,/* nothing to run */ +SCX_DSP_LOCAL ,/* local DSQ has tasks */ +SCX_DSP_PREV ,/* keep running @prev */ +}; +/* * One user of this function is scx_bpf_dispatch() which can be called * recursively as sub-sched dispatches nest. Always inline to reduce stack usage * from the call frame. */ -static __always_inline bool +static __always_inline enum scx_dsp_verdictscx_dispatch_sched (struct scx_sched *sch,struct rq *rq,struct task_struct *prev,bool nested) {
@@ -2790,12 +2797,15 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq,scx_task_on_sched (sch, prev);if (consume_global_dsq (sch, rq)) -return true; +return SCX_DSP_LOCAL ;if (bypass_dsp_enabled (sch)) {/* if @sch is bypassing, only the bypass DSQs are active */ -if (scx_bypassing (sch, cpu)) -return consume_dispatch_q (sch, rq,bypass_dsq (sch, cpu),0 ); +if (scx_bypassing (sch, cpu)) { +if (consume_dispatch_q (sch, rq,bypass_dsq (sch, cpu),0 )) +return SCX_DSP_LOCAL ; +return SCX_DSP_NONE ; + }#ifdef CONFIG_EXT_SUB_SCHED /*
@@ -2815,13 +2825,13 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq,if (!(pcpu->bypass_host_seq++ %SCX_BYPASS_HOST_NTH ) &&consume_dispatch_q (sch, rq,bypass_dsq (sch, cpu),0 )) {__scx_add_event (sch,SCX_EV_SUB_BYPASS_DISPATCH ,1 ); -return true; +return SCX_DSP_LOCAL ; }#endif /* CONFIG_EXT_SUB_SCHED */ }if (unlikely (!SCX_HAS_OP (sch, dispatch)) || !scx_rq_online (rq)) -return false; +return SCX_DSP_NONE ; dspc->rq = rq;
@@ -2848,14 +2858,12 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq,flush_dispatch_buf (sch, rq); -if ((prev->scx.flags &SCX_TASK_QUEUED ) && prev->scx.slice) { - rq->scx.flags |=SCX_RQ_BAL_KEEP ; -return true; - } +if ((prev->scx.flags &SCX_TASK_QUEUED ) && prev->scx.slice) +return SCX_DSP_PREV ;if (rq->scx.local_dsq.nr) -return true; +return SCX_DSP_LOCAL ;if (consume_global_dsq (sch, rq)) -return true; +return SCX_DSP_LOCAL ;/* * ops.dispatch() can trap us in this loop by repeatedly
@@ -2877,20 +2885,20 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq,* queued. Without this fallback, bypassed tasks could stall if the host * scheduler's ops.dispatch() doesn't yield any tasks. */ -if (bypass_dsp_enabled (sch)) -return consume_dispatch_q (sch, rq,bypass_dsq (sch, cpu),0 ); +if (bypass_dsp_enabled (sch) &&consume_dispatch_q (sch, rq,bypass_dsq (sch, cpu),0 )) +return SCX_DSP_LOCAL ; -return false; +return SCX_DSP_NONE ; } -static int balance_one (struct rq *rq,struct task_struct *prev) +static enum scx_dsp_verdict balance_one (struct rq *rq,struct task_struct *prev) {struct scx_sched *sch = scx_root; +enum scx_dsp_verdict verdict;s32 cpu =cpu_of (rq);lockdep_assert_rq_held (rq); rq->scx.flags |=SCX_RQ_IN_BALANCE ; - rq->scx.flags &= ~SCX_RQ_BAL_KEEP ;if ((sch->ops.flags &SCX_OPS_HAS_CPU_PREEMPT ) &&unlikely (rq->scx.cpu_released)) {
@@ -2920,16 +2928,19 @@ static int balance_one(struct rq *rq, struct task_struct *prev)*/ if ((prev->scx.flags &SCX_TASK_QUEUED ) && prev->scx.slice && !scx_bypassing (sch, cpu)) { - rq->scx.flags |=SCX_RQ_BAL_KEEP ; + verdict =SCX_DSP_PREV ; goto has_tasks; } }/* if there already are tasks to run, nothing to do */ -if (rq->scx.local_dsq.nr) +if (rq->scx.local_dsq.nr) { + verdict =SCX_DSP_LOCAL ; goto has_tasks; + } -if (scx_dispatch_sched (sch, rq, prev, false)) + verdict =scx_dispatch_sched (sch, rq, prev, false); +if (verdict !=SCX_DSP_NONE ) goto has_tasks;/*
@@ -2938,12 +2949,12 @@ static int balance_one(struct rq *rq, struct task_struct *prev)*/ if ((prev->scx.flags &SCX_TASK_QUEUED ) && (!(sch->ops.flags &SCX_OPS_ENQ_LAST ) ||scx_bypassing (sch, cpu))) { - rq->scx.flags |=SCX_RQ_BAL_KEEP ;__scx_add_event (sch,SCX_EV_DISPATCH_KEEP_LAST ,1 ); + verdict =SCX_DSP_PREV ; goto has_tasks; } rq->scx.flags &= ~SCX_RQ_IN_BALANCE ; -return false; +return SCX_DSP_NONE ; has_tasks:/*
@@ -2960,7 +2971,7 @@ static int balance_one(struct rq *rq, struct task_struct *prev)schedule_reenq_local (rq,0 ); rq->scx.flags &= ~SCX_RQ_IN_BALANCE ; -return true; +return verdict; }static void set_next_task_scx (struct rq *rq,struct task_struct *p,bool first)
@@ -3179,27 +3190,23 @@ static struct task_struct *first_local_task(struct rq *rq) struct task_struct, scx.dsq_list.node); } -static struct task_struct * -do_pick_task_scx (struct rq *rq,struct rq_flags *rf,bool force_scx) +/* +* Run dispatch and queue the follow-up work for a pick. +*/ +static enum scx_dsp_verdict dispatch_pick (struct rq *rq,struct rq_flags *rf, +struct task_struct *prev) { -struct task_struct *prev = rq->curr; -bool keep_prev; -struct task_struct *p; - -/* see kick_sync_wait_bal_cb() */ -smp_store_release (&rq->scx.kick_sync, rq->scx.kick_sync +1 ); - -rq_modified_begin (rq, &ext_sched_class); +enum scx_dsp_verdict verdict;rq_unpin_lock (rq, rf); -balance_one (rq, prev); + verdict =balance_one (rq, prev);rq_repin_lock (rq, rf);maybe_queue_balance_callback (rq);/* -* Defer to a balance callback which can drop rq lock and enable -* IRQs. Waiting directly in the pick path would deadlock against -* CPUs sending us IPIs (e.g. TLB flushes) while we wait for them. +* Defer to a balance callback which can drop rq lock and enable IRQs. +* Waiting directly in the pick path would deadlock against CPUs sending +* us IPIs (e.g. TLB flushes) while we wait for them. */ if (unlikely (rq->scx.kick_sync_pending)) { rq->scx.kick_sync_pending = false;
@@ -3207,10 +3214,32 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx) kick_sync_wait_bal_cb); } +if (unlikely (verdict ==SCX_DSP_PREV && prev->sched_class != &ext_sched_class)) { +WARN_ON_ONCE (scx_enable_state () ==SCX_ENABLED ); + verdict =SCX_DSP_LOCAL ; + } + +return verdict; +} + +static struct task_struct * +do_pick_task_scx (struct rq *rq,struct rq_flags *rf,bool force_scx) +{ +struct task_struct *prev = rq->curr; +enum scx_dsp_verdict verdict; +struct task_struct *p; + +/* see kick_sync_wait_bal_cb() */ +smp_store_release (&rq->scx.kick_sync, rq->scx.kick_sync +1 ); + +rq_modified_begin (rq, &ext_sched_class); + + verdict =dispatch_pick (rq, rf, prev); +/* -* If any higher-priority sched class enqueued a runnable task on -* this rq during balance_one(), abort and return RETRY_TASK, so -* that the scheduler loop can restart. +* If any higher-priority sched class enqueued a runnable task on this +* rq during balance_one(), abort and return RETRY_TASK, so that the +* scheduler loop can restart. * * If @force_scx is true, always try to pick a SCHED_EXT task, * regardless of any higher-priority sched classes activity.
@@ -3218,19 +3247,12 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)if (!force_scx &&rq_modified_above (rq, &ext_sched_class))return RETRY_TASK ; - keep_prev = rq->scx.flags &SCX_RQ_BAL_KEEP ; -if (unlikely (keep_prev && - prev->sched_class != &ext_sched_class)) { -WARN_ON_ONCE (scx_enable_state () ==SCX_ENABLED ); - keep_prev = false; - } -/* * If balance_one() is telling us to keep running @prev, replenish slice * if necessary and keep running @prev. Otherwise, pop the first one * from the local DSQ. */ -if (keep_prev) { +if (verdict ==SCX_DSP_PREV ) { p = prev;if (!p->scx.slice)refill_task_slice_dfl (scx_task_sched (p), p);
@@ -5573,7 +5595,7 @@ static void disable_bypass_dsp(struct scx_sched *sch)* * - ops.dispatch() is ignored. * -* - balance_one() does not set %SCX_RQ_BAL_KEEP on non-zero slice as slice +* - balance_one() does not report %SCX_DSP_PREV on non-zero slice as slice * can't be trusted. Whenever a tick triggers, the running task is rotated to * the tail of the queue with core_sched_at touched. *
@@ -9201,8 +9223,8 @@ __bpf_kfunc bool scx_bpf_sub_dispatch(u64 cgroup_id, const struct bpf_prog_aux * return false; } -return scx_dispatch_sched (child, this_rq, this_rq->scx.sub_dispatch_prev, - true); +return scx_dispatch_sched (child, this_rq, this_rq->scx.sub_dispatch_prev, true) != +SCX_DSP_NONE ; }#endif /* CONFIG_EXT_SUB_SCHED */
kernel/sched/sched.h +0 -1
@@ -784,7 +784,6 @@ enum scx_rq_flags {*/ SCX_RQ_ONLINE =1 <<0 ,SCX_RQ_CAN_STOP_TICK =1 <<1 , -SCX_RQ_BAL_KEEP =1 <<3 ,/* balance decided to keep current */ SCX_RQ_CLK_VALID =1 <<5 ,/* RQ clock is fresh and valid */ SCX_RQ_BAL_CB_PENDING = 1 << 6, /* must queue a cb after dispatching */
tools/sched_ext/include/scx/enum_defs.autogen.h +0 -1
@@ -143,7 +143,6 @@#define HAVE___SCX_REENQ_TSR_MASK #define HAVE_SCX_RQ_ONLINE #define HAVE_SCX_RQ_CAN_STOP_TICK -#define HAVE_SCX_RQ_BAL_KEEP #define HAVE_SCX_RQ_CLK_VALID #define HAVE_SCX_RQ_BAL_CB_PENDING #define HAVE_SCX_RQ_IN_WAKEUP
tools/sched_ext/include/scx/enums.autogen.bpf.h +0 -3
@@ -22,9 +22,6 @@ const volatile u64 __SCX_RQ_CAN_STOP_TICK __weak;const volatile u64 __SCX_RQ_BAL_PENDING__weak ;#define SCX_RQ_BAL_PENDING __SCX_RQ_BAL_PENDING -const volatile u64 __SCX_RQ_BAL_KEEP__weak ; -#define SCX_RQ_BAL_KEEP __SCX_RQ_BAL_KEEP -const volatile u64 __SCX_RQ_BYPASSING__weak ;#define SCX_RQ_BYPASSING __SCX_RQ_BYPASSING
tools/sched_ext/include/scx/enums.autogen.h +0 -1
@@ -11,7 +11,6 @@SCX_ENUM_SET (skel, scx_rq_flags,SCX_RQ_ONLINE ); \SCX_ENUM_SET (skel, scx_rq_flags,SCX_RQ_CAN_STOP_TICK ); \SCX_ENUM_SET (skel, scx_rq_flags,SCX_RQ_BAL_PENDING ); \ -SCX_ENUM_SET (skel, scx_rq_flags,SCX_RQ_BAL_KEEP ); \SCX_ENUM_SET (skel, scx_rq_flags,SCX_RQ_BYPASSING ); \SCX_ENUM_SET (skel, scx_rq_flags,SCX_RQ_CLK_VALID ); \SCX_ENUM_SET (skel, scx_rq_flags,SCX_RQ_IN_WAKEUP ); \
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 410/713 sched_ext: Fix this_rq() assumptions in dispatch kfuncs
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
commit 3dd52416e44a70bc993adb96d2e0d71b9ea21359 upstream.
Under core scheduling, dispatch runs from within the core-wide pick and can target a sibling rq, so ops.dispatch() may execute on a CPU different from the dispatched rq's. Several kfunc paths assumed the two always coincide:
- scx_dsq_move() decided whether an rq lock is held by testing this_rq()'s rq flags and lock-danced accordingly. A dispatch for a sibling took the unlocked-context branch and acquired the source rq lock on top of the already held dispatched rq lock which could deadlock.
- scx_bpf_sub_dispatch() dispatched this_rq() with its stashed sub_dispatch_prev, which is NULL when dispatching for a sibling.
- finish_dispatch(), scx_bpf_dsq_reenq() and scx_bpf_dsq_nr_queued() resolved SCX_DSQ_LOCAL to this CPU's local DSQ rather than the dispatched rq's. The latter two are callable from other rq-locked operations too, where SCX_DSQ_LOCAL now likewise resolves to the op's rq. This changes behavior also without core scheduling, e.g. for ops.enqueue() running a remote wakeup on the waking CPU, and is intended: which CPU happens to execute an operation is incidental, the op's rq is what it is operating on, and the resolution now matches the insert side where SCX_DSQ_LOCAL dispatches land on the task's rq.
Use the rq tracked by scx_locked_rq(), which is set to the dispatched rq around ops invocations and NULL in unlocked contexts.
--- kernel/sched/ext/ext.c | 58 +++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 23 deletions(-)
kernel/sched/ext/ext.c +35 -23
@@ -2737,7 +2737,7 @@ static void finish_dispatch(struct scx_sched *sch, struct rq *rq,BUG_ON (!(p->scx.flags &SCX_TASK_QUEUED )); - dsq =find_dsq_for_dispatch (sch,this_rq (), dsq_id,task_cpu (p)); + dsq =find_dsq_for_dispatch (sch, rq, dsq_id,task_cpu (p));if (dsq->id ==SCX_DSQ_LOCAL )dispatch_to_local_dsq (sch, rq, dsq, p, enq_flags);
@@ -8887,9 +8887,8 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, {struct scx_dispatch_q *src_dsq = kit->dsq, *dst_dsq;struct scx_sched *sch; -struct rq *this_rq, *src_rq, *locked_rq; +struct rq *p_rq, *src_rq, *locked_rq;bool dispatched = false; -bool in_balance;unsigned long flags;/*
@@ -8919,24 +8918,28 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, }/* -* Can be called from either ops.dispatch() locking this_rq() or any -* context where no rq lock is held. If latter, lock @p's task_rq which -* we'll likely need anyway. +* Can be called from either ops.dispatch() holding the dispatched rq's +* lock or any context where no rq lock is held. If latter, lock @p's +* task_rq which we'll likely need anyway. */ src_rq =task_rq (p);local_irq_save (flags); - this_rq =this_rq (); - in_balance = this_rq->scx.flags &SCX_RQ_IN_BALANCE ; -if (in_balance) { -if (this_rq != src_rq) -switch_rq_lock (this_rq, src_rq); +/* +* Under core scheduling, dispatch can run for a sibling rq, so the +* locked rq is not necessarily this CPU's. +*/ + locked_rq =scx_locked_rq (); + +if (locked_rq) { +if (locked_rq != src_rq) +switch_rq_lock (locked_rq, src_rq); }else {raw_spin_rq_lock (src_rq); } - locked_rq = src_rq; + p_rq = src_rq;raw_spin_lock (&src_dsq->lock);/* did someone else get to it while we dropped the locks? */
@@ -8946,7 +8949,7 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, }/* @p is still on $src_dsq and stable, determine the destination */ - dst_dsq =find_dsq_for_dispatch (sch, this_rq, dsq_id,task_cpu (p)); + dst_dsq =find_dsq_for_dispatch (sch, locked_rq ?:this_rq (), dsq_id,task_cpu (p));/* * Apply vtime and slice updates before moving so that the new time is
@@ -8959,14 +8962,14 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, p->scx.slice = kit->slice;/* execute move */ - locked_rq =move_task_between_dsqs (sch, p, enq_flags, src_dsq, dst_dsq); + p_rq =move_task_between_dsqs (sch, p, enq_flags, src_dsq, dst_dsq); dispatched = true; out: -if (in_balance) { -if (this_rq != locked_rq) -switch_rq_lock (locked_rq, this_rq); +if (locked_rq) { +if (locked_rq != p_rq) +switch_rq_lock (p_rq, locked_rq); }else { -raw_spin_rq_unlock_irqrestore (locked_rq, flags); +raw_spin_rq_unlock_irqrestore (p_rq, flags); } kit->cursor.flags &= ~(__SCX_DSQ_ITER_HAS_SLICE |
@@ -9204,7 +9207,7 @@ __bpf_kfunc bool scx_bpf_dsq_move_vtime(struct bpf_iter_scx_dsq *it__iter,*/ __bpf_kfunc bool scx_bpf_sub_dispatch(u64 cgroup_id, const struct bpf_prog_aux *aux) { -struct rq *this_rq =this_rq (); +struct rq *rq =scx_locked_rq ();struct scx_sched *parent, *child;guard (rcu)();
@@ -9223,7 +9226,7 @@ __bpf_kfunc bool scx_bpf_sub_dispatch(u64 cgroup_id, const struct bpf_prog_aux * return false; } -return scx_dispatch_sched (child, this_rq, this_rq->scx.sub_dispatch_prev, true) != +return scx_dispatch_sched (child, rq, rq->scx.sub_dispatch_prev, true) !=SCX_DSP_NONE ; }#endif /* CONFIG_EXT_SUB_SCHED */
@@ -9518,6 +9521,10 @@ __bpf_kfunc s32 scx_bpf_kick_cid(s32 cid, u64 flags, const struct bpf_prog_aux ** * Return the number of tasks in the DSQ matching @dsq_id. If not found, * -%ENOENT is returned. +* +* %SCX_DSQ_LOCAL resolves to the local DSQ of the rq the current scheduler +* operation is locked to - e.g. the rq being dispatched for in ops.dispatch() - +* or the calling CPU's when no rq is locked. */ __bpf_kfunc s32 scx_bpf_dsq_nr_queued(u64 dsq_id, const struct bpf_prog_aux *aux) {
@@ -9534,7 +9541,7 @@ __bpf_kfunc s32 scx_bpf_dsq_nr_queued(u64 dsq_id, const struct bpf_prog_aux *aux }if (dsq_id ==SCX_DSQ_LOCAL ) { - ret =READ_ONCE (this_rq ()->scx.local_dsq.nr); + ret =READ_ONCE ((scx_locked_rq () ?:this_rq ())->scx.local_dsq.nr); goto out; }else if ((dsq_id &SCX_DSQ_LOCAL_ON ) ==SCX_DSQ_LOCAL_ON ) {s32 cpu =scx_cpu_ret (sch, dsq_id &SCX_DSQ_LOCAL_CPU_MASK );
@@ -9713,10 +9720,15 @@ __bpf_kfunc struct task_struct *scx_bpf_dsq_peek(u64 dsq_id,* - User DSQs * * Re-enqueues are performed asynchronously. Can be called from anywhere. +* +* %SCX_DSQ_LOCAL resolves to the local DSQ of the rq the current scheduler +* operation is locked to - e.g. the rq being dispatched for in ops.dispatch() - +* or the calling CPU's when no rq is locked. */ __bpf_kfunc void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags,const struct bpf_prog_aux *aux) { +struct rq *locked_rq =scx_locked_rq ();struct scx_sched *sch;struct scx_dispatch_q *dsq;
@@ -9735,8 +9747,8 @@ __bpf_kfunc void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags,if (!(reenq_flags & __SCX_REENQ_FILTER_MASK)) reenq_flags |=SCX_REENQ_ANY ; - dsq =find_dsq_for_dispatch (sch,this_rq (), dsq_id,smp_processor_id ()); -schedule_dsq_reenq (sch, dsq, reenq_flags,scx_locked_rq ()); + dsq =find_dsq_for_dispatch (sch, locked_rq ?:this_rq (), dsq_id,smp_processor_id ()); +schedule_dsq_reenq (sch, dsq, reenq_flags, locked_rq); }/**
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 411/713 sched_ext: Fix rq->core_pick corruption under core scheduling
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
commit d954004205c1a1d3f59ce8482b559266c15600fa upstream.
Core scheduling's pick_next_task() picks what to run on every SMT sibling of the core in a single pass under the shared core-wide rq lock. The selection state is consistent only while the lock is held continuously, so ->pick_task() originally could not release it. However, since 4c95380701f5 ("sched/ext: Fold balance_scx() into pick_task_scx()"), sched_ext runs dispatch from inside the pick and dispatching can drop the rq lock. To support this, pick_next_task() has been updated to restart the whole selection when a pick returns RETRY_TASK after releasing the lock.
When selections on the same core interleave through the dropped lock, they corrupt each other's state: one clears the other's rq->core_pick leading to a NULL deref, or invalidates its keep-the-previous-task decision leaving a dequeued task running, which deadlocks the next wakeup and matches the reported hard hangs. A cookied ping-pong load on an SMT machine makes the interleavings frequent and kills the kernel within seconds.
Fix it by making the pick return RETRY_TASK whenever dispatch released the rq lock, so that a selection only ever commits picks made under a continuously held lock. The previous patch's rq->scx.lock_drop_seq counts the releases. A dispatch that touched nothing never releases the lock and its verdict, including "nothing to run", stands: retries are bounded, each following a dispatch that actually did something, and an idle CPU does not loop.
If another dispatch is already in flight on the rq, skip dispatching and pick from what is already queued locally - the in-flight dispatch has released the lock, so its own selection will retry and re-pick this rq, while returning RETRY_TASK here would only spin on the lock that dispatch needs to finish.
Balance callbacks must run in the context that queued them, so they can only be queued on the CPU's own rq. When dispatching for another rq, run the deferred work directly instead - that rq may consume all its picks through the core-sched fast path and never queue the callback itself.
The put_prev_task_scx() warning about a runnable task being left behind assumed that dispatch ran as part of the very pick that is switching away. That now only holds on the non-core path, so gate it and drop the cookie-match test, which is always true without core scheduling, from its condition.
--- kernel/sched/ext/ext.c | 86 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 8 deletions(-)
kernel/sched/ext/ext.c +78 -8
@@ -2779,6 +2779,7 @@ enum scx_dsp_verdict {SCX_DSP_NONE ,/* nothing to run */ SCX_DSP_LOCAL ,/* local DSQ has tasks */ SCX_DSP_PREV ,/* keep running @prev */ + SCX_DSP_RETRY, /* pick helpers only: restart the pick */ };/*
@@ -3124,12 +3125,12 @@ static void put_prev_task_scx(struct rq* ops.enqueue() that @p is the only one available for this cpu, * which should trigger an explicit follow-up scheduling event. * -* Core scheduling can force this CPU idle while @p stays -* runnable. @p's cookie then won't match the core's, so skip -* the warning in that case. +* Under core scheduling, a pick dispatches only when nothing is +* locally runnable and can legitimately go idle with @p still +* runnable (see do_pick_task_scx()). */ if (next &&sched_class_above (&ext_sched_class, next->sched_class)) { -WARN_ON_ONCE (sched_cpu_cookie_match (rq, p) && +WARN_ON_ONCE (!sched_core_enabled (rq) && !(sch->ops.flags &SCX_OPS_ENQ_LAST ));do_enqueue_task (rq, p,SCX_ENQ_LAST ,-1 ); }else {
@@ -3222,6 +3223,70 @@ static enum scx_dsp_verdict dispatch_picreturn verdict; } +#ifdef CONFIG_SCHED_CORE +/* +* Dispatch for a pick when core scheduling is enabled. The selection picks for +* all SMT siblings and the rq_i->core_pick state it builds must stay atomic +* throughout. If the dispatch released the rq lock, anything can have happened +* in between - return %SCX_DSP_RETRY to restart the selection against current +* state. +*/ +static enum scx_dsp_verdict dispatch_core_pick (struct rq *rq,struct rq_flags *rf, +struct task_struct *prev) +{ +enum scx_dsp_verdict verdict; +u32 seq = rq->scx.lock_drop_seq; + +/* another dispatch is in flight on @rq, let that handle it */ +if (rq->scx.flags &SCX_RQ_IN_BALANCE ) +return SCX_DSP_NONE ; + +rq_unpin_lock (rq, rf); + + verdict =balance_one (rq, prev); + +if (cpu_of (rq) ==smp_processor_id ()) { +maybe_queue_balance_callback (rq); + +/* see dispatch_pick() */ +if (unlikely (rq->scx.kick_sync_pending)) { + rq->scx.kick_sync_pending = false; +queue_balance_callback (rq, &rq->scx.kick_sync_bal_cb, + kick_sync_wait_bal_cb); + } + }else if (unlikely (rq->scx.flags &SCX_RQ_BAL_CB_PENDING )) { +/* +* Balance callbacks must run in the context that queued them, +* so they can't be queued on another CPU's rq. Run the deferred +* work directly instead. +*/ + rq->scx.flags &= ~SCX_RQ_BAL_CB_PENDING ; +run_deferred (rq); + } + +rq_repin_lock (rq, rf); + +/* if balance_one() released the rq lock, restart the selection */ +if (rq->scx.lock_drop_seq != seq) +return SCX_DSP_RETRY ; + +/* see dispatch_pick() */ +if (unlikely (verdict ==SCX_DSP_PREV && + prev->sched_class != &ext_sched_class)) { +WARN_ON_ONCE (scx_enable_state () ==SCX_ENABLED ); + verdict =SCX_DSP_LOCAL ; + } + +return verdict; +} +#else /* CONFIG_SCHED_CORE */ +static enum scx_dsp_verdict dispatch_core_pick (struct rq *rq,struct rq_flags *rf, +struct task_struct *prev) +{ +return SCX_DSP_NONE ; +} +#endif /* CONFIG_SCHED_CORE */ +static struct task_struct *do_pick_task_scx (struct rq *rq,struct rq_flags *rf,bool force_scx) {
@@ -3234,7 +3299,13 @@ do_pick_task_scx(struct rq *rq, struct rrq_modified_begin (rq, &ext_sched_class); - verdict =dispatch_pick (rq, rf, prev); +if (sched_core_enabled (rq)) + verdict =dispatch_core_pick (rq, rf, prev); +else + verdict =dispatch_pick (rq, rf, prev); + +if (verdict ==SCX_DSP_RETRY ) +return RETRY_TASK ;/* * If any higher-priority sched class enqueued a runnable task on this
@@ -3248,9 +3319,8 @@ do_pick_task_scx(struct rq *rq, struct rreturn RETRY_TASK ;/* -* If balance_one() is telling us to keep running @prev, replenish slice -* if necessary and keep running @prev. Otherwise, pop the first one -* from the local DSQ. +* If we're keeping @prev, replenish slice if necessary and keep running +* @prev. Otherwise, pop the first one from the local DSQ. */ if (verdict ==SCX_DSP_PREV ) { p = prev;
Greg Kroah-Hartman
maintainerPATCH 412/713 sched_ext: Count rq lock releases in rq->scx.lock_drop_seq
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
commit f2da9587118d5da41a3f81a59642d5bb4782ddcc upstream.
Under core scheduling, pick_next_task() selects for all SMT siblings under one continuous hold of the shared core-wide rq lock, and sched_ext's dispatch can release that lock from inside the pick. In preparation for making the core-sched pick detect the releases and retry, add rq->scx.lock_drop_seq and bump it at every site that can release an rq lock while a dispatch may be in flight. The counter is only maintained while core scheduling is enabled. No functional changes.
--- kernel/sched/ext/ext.c | 38 ++++++++++++++++++++++++++++++++++++-- kernel/sched/sched.h | 3 +++ 2 files changed, 39 insertions(+), 2 deletions(-)
kernel/sched/ext/ext.c +36 -2
@@ -479,12 +479,27 @@ static bool rq_is_open(struct rq *rq, u6*/ DEFINE_PER_CPU(struct rq *, scx_locked_rq_state); +/* +* Under core scheduling, a pick that releases the rq lock invalidates the +* core-wide selection it is part of. Count the releases so that the core-sched +* pick can tell whether one happened across dispatch. +*/ +static void scx_rq_lock_drop (struct rq *rq) +{ +lockdep_assert_rq_held (rq); +#ifdef CONFIG_SCHED_CORE +if (sched_core_enabled (rq)) + rq->scx.lock_drop_seq++; +#endif +} +static void switch_rq_lock (struct rq *from,struct rq *to) {bool tracked =scx_locked_rq () == from;if (tracked)update_locked_rq (NULL ); +scx_rq_lock_drop (from);raw_spin_rq_unlock (from);raw_spin_rq_lock (to);if (tracked)
@@ -1133,6 +1148,7 @@ static void deferred_irq_workfn(struct iraw_spin_rq_lock (rq);run_deferred (rq); +scx_rq_lock_drop (rq);raw_spin_rq_unlock (rq); }
@@ -2430,12 +2446,14 @@ static bool consume_remote_task(struct r struct task_struct *p, u64 enq_flags, struct scx_dispatch_q *dsq, struct rq *src_rq) { +scx_rq_lock_drop (this_rq );raw_spin_rq_unlock (this_rq);if (unlink_dsq_and_lock_src_rq (p, dsq, src_rq)) {move_remote_task_to_local_dsq (p, enq_flags, src_rq, this_rq);return true; }else { +scx_rq_lock_drop (src_rq);raw_spin_rq_unlock (src_rq);raw_spin_rq_lock (this_rq);return false;
@@ -3172,6 +3190,7 @@ retry:continue ; } +scx_rq_lock_drop (rq);raw_spin_rq_unlock_irq (rq);while (READ_ONCE (cpu_rq (cpu)->scx.kick_sync) == ksyncs[cpu]) {smp_store_release (&rq->scx.kick_sync, rq->scx.kick_sync +1 );
@@ -4350,8 +4369,10 @@ static void reenq_user(struct rq *rq, st task_rq =task_rq (p);if (locked_rq != task_rq) { -if (locked_rq) +if (locked_rq) { +scx_rq_lock_drop (locked_rq);raw_spin_rq_unlock (locked_rq); + }if (unlikely (!raw_spin_rq_trylock (task_rq))) {raw_spin_unlock (&dsq->lock);raw_spin_rq_lock (task_rq);
@@ -4377,6 +4398,7 @@ static void reenq_user(struct rq *rq, st p->scx.flags &= ~SCX_TASK_REENQ_REASON_MASK ;if (!(++nr_enqueued %SCX_TASK_ITER_BATCH )) { +scx_rq_lock_drop (locked_rq);raw_spin_rq_unlock (locked_rq); locked_rq =NULL ;cpu_relax ();
@@ -4389,8 +4411,10 @@ static void reenq_user(struct rq *rq, straw_spin_unlock (&dsq->lock);if (locked_rq != rq) { -if (locked_rq) +if (locked_rq) { +scx_rq_lock_drop (locked_rq);raw_spin_rq_unlock (locked_rq); + }raw_spin_rq_lock (rq); } }
@@ -5442,6 +5466,7 @@ resume:if (!(nr_balanced %SCX_BYPASS_LB_BATCH ) && n) {list_move_tail (&cursor.node, &n->scx.dsq_list.node);raw_spin_unlock (&donor_dsq->lock); +scx_rq_lock_drop (donor_rq);raw_spin_rq_unlock_irq (donor_rq);cpu_relax ();raw_spin_rq_lock_irq (donor_rq);
@@ -5452,6 +5477,7 @@ resume:list_del_init (&cursor.node);raw_spin_unlock (&donor_dsq->lock); +scx_rq_lock_drop (donor_rq);raw_spin_rq_unlock_irq (donor_rq);return nr_balanced;
@@ -5742,6 +5768,7 @@ static void scx_bypass(struct scx_sched* sees scx_bypassing() before moving tasks to SCX. */ if (!scx_enabled ()) { +scx_rq_lock_drop (rq);raw_spin_rq_unlock (rq);continue ; }
@@ -5768,6 +5795,7 @@ static void scx_bypass(struct scx_schedif (cpu_online (cpu) || cpu ==smp_processor_id ())resched_curr (rq); +scx_rq_lock_drop (rq);raw_spin_rq_unlock (rq); }
@@ -8495,6 +8523,7 @@ static bool kick_one_cpu(s32 cpu, structcpumask_clear_cpu (cpu, this_scx->cpus_to_wait); } +scx_rq_lock_drop (rq);raw_spin_rq_unlock_irqrestore (rq, flags);return should_wait;
@@ -8511,6 +8540,7 @@ static void kick_one_cpu_if_idle(s32 cpu (cpu_online(cpu) || cpu == cpu_of(this_rq)))resched_curr (rq); +scx_rq_lock_drop (rq);raw_spin_rq_unlock_irqrestore (rq, flags); }
@@ -8548,6 +8578,7 @@ static void kick_cpus_irq_workfn(structraw_spin_rq_lock (this_rq); this_scx->kick_sync_pending = true;resched_curr (this_rq); +scx_rq_lock_drop (this_rq);raw_spin_rq_unlock (this_rq); } }
@@ -9039,6 +9070,7 @@ out:if (locked_rq != p_rq)switch_rq_lock (p_rq, locked_rq); }else { +scx_rq_lock_drop (p_rq);raw_spin_rq_unlock_irqrestore (p_rq, flags); }
@@ -9518,9 +9550,11 @@ static void scx_kick_cpu(struct scx_scheif (raw_spin_rq_trylock (target_rq)) {if (can_skip_idle_kick (target_rq)) { +scx_rq_lock_drop (target_rq);raw_spin_rq_unlock (target_rq); goto out; } +scx_rq_lock_drop (target_rq);raw_spin_rq_unlock (target_rq); }cpumask_set_cpu (cpu, this_rq->scx.cpus_to_kick_if_idle);
kernel/sched/sched.h +3 -0
@@ -803,6 +803,9 @@ struct scx_rq {bool cpu_released;u32 flags;u32 nr_immed;/* ENQ_IMMED tasks on local_dsq */ +#ifdef CONFIG_SCHED_CORE +u32 lock_drop_seq;/* nr dispatch lock releases */ +#endif u64 clock;/* current per-rq clock -- see scx_bpf_now() */ cpumask_var_t cpus_to_kick;cpumask_var_t cpus_to_kick_if_idle;
Greg Kroah-Hartman
maintainerPATCH 413/713 sched_ext: Fix scx_bpf_dsq_move_to_local___v2 compat detection
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: fangqiurong <[email protected]>
commit 56bbc91219c0711f1580a8ba7cd60471b6e1684e upstream.
libbpf strips the last ___flavor suffix when resolving kfunc externs, so the bare ___v2 declaration resolves to scx_bpf_dsq_move_to_local, whose BTF proto lacks @enq_flags. The extern never matches, bpf_ksym_exists() returns false on every kernel that has the ___v2 kfunc, and the macro falls back to ___v1, silently dropping @enq_flags.
Add the trailing ___compat suffix used by the other versioned externs in this file (scx_bpf_dsq_insert___v2, scx_bpf_reenqueue_local___v2).
Any caller passing non-zero @enq_flags through the compat macro silently loses them.
--- tools/sched_ext/include/scx/compat.bpf.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
tools/sched_ext/include/scx/compat.bpf.h +3 -3
@@ -31,7 +31,7 @@ struct cgroup *scx_bpf_task_cgroup___new(struct task_struct *p) __ksym __weak;* * v7.1: scx_bpf_dsq_move_to_local___v2() to add @enq_flags. */ -bool scx_bpf_dsq_move_to_local___v2(u64 dsq_id, u64 enq_flags) __ksym __weak; +bool scx_bpf_dsq_move_to_local___v2___compat(u64 dsq_id, u64 enq_flags) __ksym __weak; bool scx_bpf_dsq_move_to_local___v1(u64 dsq_id) __ksym __weak; void scx_bpf_dsq_move_set_slice___new(struct bpf_iter_scx_dsq *it__iter, u64 slice) __ksym __weak; void scx_bpf_dsq_move_set_vtime___new(struct bpf_iter_scx_dsq *it__iter, u64 vtime) __ksym __weak;
@@ -45,8 +45,8 @@ bool scx_bpf_dispatch_from_dsq___old(struct bpf_iter_scx_dsq *it__iter, struct t bool scx_bpf_dispatch_vtime_from_dsq___old(struct bpf_iter_scx_dsq *it__iter, struct task_struct *p, u64 dsq_id, u64 enq_flags) __ksym __weak;#define scx_bpf_dsq_move_to_local (dsq_id, enq_flags) \ - (bpf_ksym_exists(scx_bpf_dsq_move_to_local___v2) ? \ - scx_bpf_dsq_move_to_local___v2((dsq_id), (enq_flags)) : \ + (bpf_ksym_exists(scx_bpf_dsq_move_to_local___v2___compat) ? \ + scx_bpf_dsq_move_to_local___v2___compat((dsq_id), (enq_flags)) : \ (bpf_ksym_exists(scx_bpf_dsq_move_to_local___v1) ? \ scx_bpf_dsq_move_to_local___v1((dsq_id)) : \ scx_bpf_consume___old((dsq_id))))
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 414/713 sched_ext: Fix exit_task leak on fork failure during enable
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: fangqiurong <[email protected]>
commit 03506edca637a8465dba9f635c50e9884fbcaf4e upstream.
scx_fork() initializes tasks when scx_init_task_enabled is set, but scx_cancel_fork() only exits them when scx_enabled() is true. A fork that fails in the enable window (between releasing scx_fork_rwsem and setting __scx_enabled) runs ops.init_task() but never ops.exit_task().
Gate scx_cancel_fork() on scx_init_task_enabled.
--- kernel/sched/ext/ext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/sched/ext/ext.c +1 -1
@@ -3988,7 +3988,7 @@ void scx_post_fork(struct task_struct *pvoid scx_cancel_fork (struct task_struct *p) { -if (scx_enabled ()) { +if (scx_init_task_enabled) {struct rq *rq;struct rq_flags rf;
Greg Kroah-Hartman
maintainerPATCH 415/713 sched_ext: Fix inverted ops.core_sched_before() invocation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
commit f7b6d128dd49a6eec09066ecfd29095f12588786 upstream.
scx_prio_less() implements prio_less() semantics - %true means that @a is the lower priority and should run after @b. ops.core_sched_before() is documented to return %true when @a should run before @b. scx_prio_less() returns the op's value as-is, inverting the documented semantics at runtime.
Call the op with the arguments swapped.
scx_qmap followed the wiring instead of the documentation and returned %true for the younger task, so the two inversions canceled out and it behaved as intended. Flip its comparison to match. scx_qmap is likely the only current user in or out of the kernel tree. Any scheduler written the same way needs the same flip, while schedulers following the documentation are fixed by this change.
--- kernel/sched/ext/ext.c | 10 ++++++++-- tools/sched_ext/scx_qmap.bpf.c | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-)
kernel/sched/ext/ext.c +8 -2
@@ -3416,6 +3416,8 @@ void ext_server_init(struct rq *rq)* * When ops.core_sched_before() is enabled, @p->scx.core_sched_at is used to * implement FIFO ordering within each local DSQ. See pick_task_scx(). +* +* Return: %true if @a should run after @b. */ bool scx_prio_less (const struct task_struct *a,const struct task_struct *b,bool in_fi)
@@ -3424,6 +3426,10 @@ bool scx_prio_less(const struct task_strstruct scx_sched *sch_b =scx_task_sched (b);/* +* scx_prio_less() returns whether @a should run after @b while +* ops.core_sched_before() returns whether its first argument should run +* before the second. Swap the arguments. +* * The const qualifiers are dropped from task_struct pointers when * calling ops.core_sched_before(). Accesses are controlled by the * verifier.
@@ -3432,8 +3438,8 @@ bool scx_prio_less(const struct task_str !scx_bypassing(sch_a, task_cpu(a)))return SCX_CALL_OP_2TASKS_RET (sch_a, core_sched_before,task_rq (a), - (struct task_struct *)a, - (struct task_struct *)b); + (struct task_struct *)b, + (struct task_struct *)a); else return time_after64(a->scx.core_sched_at, b->scx.core_sched_at); }
tools/sched_ext/scx_qmap.bpf.c +1 -1
@@ -749,7 +749,7 @@ static s64 task_qdist(struct task_structbool BPF_STRUCT_OPS (qmap_core_sched_before ,struct task_struct *a,struct task_struct *b) { -return task_qdist (a) >task_qdist (b); +return task_qdist (a) <task_qdist (b); }/*
Greg Kroah-Hartman
maintainerPATCH 416/713 sched_ext: Dont BUG_ON a destroyed DSQ in process_deferred_reenq_users
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tao Cui <[email protected]>
commit 8d8dd8ae89eaa78b37fc85528e926029f5facbdf upstream.
scx_bpf_dsq_reenq() queues a deferred reenq (dru) that runs from run_deferred(), not ops.dispatch(). If the DSQ is destroyed before the dru runs, process_deferred_reenq_users() sees dsq->id == SCX_DSQ_INVALID and hits the BUG_ON. destroy_dsq() doesn't flush pending drus, so just skip.
tj: Read dsq->id once with READ_ONCE(). Reading it separately in the INVALID
check and the BUG_ON would leave a window where destroy_dsq() can invalidate the id between the two reads and still trigger the BUG_ON.
--- kernel/sched/ext/ext.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
kernel/sched/ext/ext.c +7 -2
@@ -4431,7 +4431,7 @@ static void process_deferred_reenq_userswhile (true) {struct scx_dispatch_q *dsq; -u64 reenq_flags; +u64 dsq_id, reenq_flags; scoped_guard (raw_spinlock, &rq->scx.deferred_reenq_lock) { struct scx_deferred_reenq_user *dru =
@@ -4454,7 +4454,12 @@ static void process_deferred_reenq_users/* see schedule_dsq_reenq() */ smp_mb (); -BUG_ON (dsq->id &SCX_DSQ_FLAG_BUILTIN ); +/* destroy_dsq() may have raced and invalidated @dsq, nothing to reenq */ + dsq_id =READ_ONCE (dsq->id); +if (unlikely (dsq_id ==SCX_DSQ_INVALID )) +continue ; + +BUG_ON (dsq_id &SCX_DSQ_FLAG_BUILTIN );reenq_user (rq, dsq, reenq_flags); } }
Greg Kroah-Hartman
maintainerPATCH 417/713 sched_ext: Fix scx_bpf_dsq_reenq___compat kfunc extern prototype
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
commit 62f3d531e41a7e6e0dc8d37ea377c50488decb56 upstream.
scx_bpf_dsq_reenq() is registered with KF_IMPLICIT_ARGS and its kernel BTF prototype omits the trailing bpf_prog_aux argument. The ___compat extern declares the argument explicitly, so libbpf never matches the prototype and the weak extern silently stays unresolved on every kernel. The wrapper always takes the old fallback path, which disables generic reenq users like scx_qmap's lowpri mechanism and fails non-local reenq with "kernel too old" even on kernels that have the kfunc.
Drop the explicit aux argument. Also correct the stale v6.20 reference, the kfunc was added in v7.1.
--- tools/sched_ext/include/scx/compat.bpf.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
tools/sched_ext/include/scx/compat.bpf.h +3 -3
@@ -414,10 +414,10 @@ static inline void scx_bpf_reenqueue_loc }/* -* v6.20: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This +* v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This * will eventually deprecate scx_bpf_reenqueue_local(). */ -void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags, const struct bpf_prog_aux *aux__prog) __ksym __weak; +void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak;static inline bool __COMPAT_has_generic_reenq (void ) {
@@ -427,7 +427,7 @@ static inline bool __COMPAT_has_generic_static inline void scx_bpf_dsq_reenq (u64 dsq_id,u64 reenq_flags) {if (bpf_ksym_exists (scx_bpf_dsq_reenq___compat)) -scx_bpf_dsq_reenq___compat (dsq_id, reenq_flags,NULL ); +scx_bpf_dsq_reenq___compat (dsq_id, reenq_flags);else if (dsq_id ==SCX_DSQ_LOCAL && reenq_flags ==0 )scx_bpf_reenqueue_local ();else
Greg Kroah-Hartman
maintainerPATCH 418/713 sched_ext: Keep kick_sync waiting on the rqs own CPU
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
commit e0253dd04beb03e79477c5ef4768b11135687206 upstream.
kick_sync_wait_bal_cb() assumes it runs on the rq's CPU from the __schedule() tail: the snapshots it compares against live in that CPU's percpu area and the busy-wait runs with the rq lock dropped and IRQs enabled.
However, dispatch can now drop the rq lock while the callback sits queued, and rq lock takers in that window (the sched class change paths, the scx task iterator) flush pending balance callbacks on release, running the callback on a foreign CPU. Such a run compares against unrelated snapshots and can deadlock when the executing CPU is itself a wait target.
Bail on a foreign CPU and leave the wait state alone. The wait only observes progress that the resched kicks already guarantee and the rq's next wait picks up the stale cpus_to_sync bits.
--- kernel/sched/ext/ext.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
kernel/sched/ext/ext.c +17 -2
@@ -3163,12 +3163,27 @@ switch_class:static void kick_sync_wait_bal_cb (struct rq *rq) { -struct scx_kick_syncs __rcu *ks =__this_cpu_read (scx_kick_syncs); -unsigned long *ksyncs =rcu_dereference_sched (ks)->syncs; +struct scx_kick_syncs __rcu *ks; +unsigned long *ksyncs;bool waited;s32 cpu;/* +* This callback is queued and normally flushed within @rq's own +* scheduling pass. However, dispatch can drop the rq lock while it sits +* queued, and lock takers in that window (the sched class change paths, +* the scx task iterator) flush pending balance callbacks on release, +* running this one on a foreign CPU whose snapshots are unrelated. The +* kicked CPUs are already on their way to advance the kick_syncs being +* waited on. Don't get in the way. +*/ +if (unlikely (cpu_of (rq) !=smp_processor_id ())) +return ; + + ks =__this_cpu_read (scx_kick_syncs); + ksyncs =rcu_dereference_sched (ks)->syncs; + +/* * Drop rq lock and enable IRQs while waiting. IRQs must be enabled * — a target CPU may be waiting for us to process an IPI (e.g. TLB * flush) while we wait for its kick_sync to advance.
Greg Kroah-Hartman
maintainerPATCH 419/713 scsi: core: Fill in DMA padding bytes in scsi_alloc_sgtables()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Petr Vaganov <[email protected]>
commit 626147717bea776b61ed3631d2c26283760c4cc4 upstream.
During fuzz testing, the following issue was discovered:
BUG: KMSAN: uninit-value in __dma_map_sg_attrs+0x217/0x310 __dma_map_sg_attrs+0x217/0x310 dma_map_sg_attrs+0x4a/0x70 ata_qc_issue+0x9f8/0x1420 __ata_scsi_queuecmd+0x1657/0x1740 ata_scsi_queuecmd+0x79a/0x920 scsi_queue_rq+0x4472/0x4f40 blk_mq_dispatch_rq_list+0x1cca/0x3ee0 __blk_mq_sched_dispatch_requests+0x458/0x630 blk_mq_sched_dispatch_requests+0x15b/0x340 __blk_mq_run_hw_queue+0xe5/0x250 __blk_mq_delay_run_hw_queue+0x138/0x780 blk_mq_run_hw_queue+0x4bb/0x7e0 blk_mq_sched_insert_request+0x2a7/0x4c0 blk_execute_rq+0x497/0x8a0 sg_io+0xbe0/0xe20 scsi_ioctl+0x2b36/0x3c60 sr_block_ioctl+0x319/0x440 blkdev_ioctl+0x80f/0xd70 __se_sys_ioctl+0x219/0x420 __x64_sys_ioctl+0x93/0xe0 x64_sys_call+0x1d6c/0x3ad0 do_syscall_64+0x4c/0xa0 entry_SYSCALL_64_after_hwframe+0x6e/0xd8
Uninit was created at:
__alloc_pages+0x5c0/0xc80 alloc_pages+0xe0e/0x1050 blk_rq_map_user_iov+0x2b77/0x6100 blk_rq_map_user_io+0x2fa/0x4d0 sg_io+0xad6/0xe20 scsi_ioctl+0x2b36/0x3c60 sr_block_ioctl+0x319/0x440 blkdev_ioctl+0x80f/0xd70 __se_sys_ioctl+0x219/0x420 __x64_sys_ioctl+0x93/0xe0 x64_sys_call+0x1d6c/0x3ad0 do_syscall_64+0x4c/0xa0 entry_SYSCALL_64_after_hwframe+0x6e/0xd8
Bytes 14-15 of 16 are uninitialized
Memory access of size 16 starts at ffff88800cbdb000
When processing the last unaligned element of the scatterlist, it is supplemented with missing bytes in the amount of pad_len. These bytes remain uninitialized, which leads to a problem.
Extend last_sg->length by pad_len first, then use sg_zero_buffer() to zero those pad_len bytes. sg_zero_buffer() uses sg_miter internally, which correctly handles sg entries spanning multiple pages and padding that crosses a page boundary.
Found by Linux Verification Center (linuxtesting.org) with Syzkaller.
--- drivers/scsi/scsi_lib.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/scsi/scsi_lib.c +2 -0
@@ -1187,8 +1187,10 @@ blk_status_t scsi_alloc_sgtables(structif (blk_rq_bytes (rq) & rq->q->limits.dma_pad_mask) {unsigned int pad_len = (rq->q->limits.dma_pad_mask & ~blk_rq_bytes (rq)) +1 ; +unsigned int data_len = last_sg->length; last_sg->length += pad_len; +sg_zero_buffer (last_sg,1 , pad_len, data_len); cmd->extra_len += pad_len; }
Greg Kroah-Hartman
maintainerPATCH 420/713 scsi: fnic: Use GFP_ATOMIC for VLAN alloc under spinlock
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linkai Gong <[email protected]>
commit 9639c6324524ea3f934908bd51f02430000954ab upstream.
fnic_fcoe_process_vlan_resp() allocates a VLAN descriptor with kzalloc_obj() (default GFP_KERNEL) while holding vlans_lock via spin_lock_irqsave(). GFP_KERNEL may sleep, which is not allowed in this atomic context and can trigger a sleeping-from-invalid-context warning or deadlock.
Pass GFP_ATOMIC so the allocation is safe under the IRQ-safe spinlock.
--- drivers/scsi/fnic/fip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/fnic/fip.c +1 -1
@@ -139,7 +139,7 @@ void fnic_fcoe_process_vlan_resp(structFNIC_FIP_DBG (KERN_INFO , fnic->host, fnic->fnic_num,"process_vlan_resp: FIP VLAN %d\n" , vid); - vlan =kzalloc_obj (*vlan); + vlan =kzalloc_obj (*vlan,GFP_ATOMIC );if (!vlan) {/* retry from timer */
Greg Kroah-Hartman
maintainerPATCH 421/713 rpmsg: glink: smem: order FIFO read after availability check
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chunkai Deng <[email protected]>
commit 786439ad58763e04b91bc2ec5f590e463939f197 upstream.
glink_smem_rx_peek() reads the RX FIFO payload after the caller has determined data is available via glink_smem_rx_avail(), which reads the remote-updated head index. A control dependency between the head read and the subsequent payload read does not order the two loads, so the CPU may speculatively read the FIFO before observing the head update and consume stale data the remote has not yet published.
Add rmb() in glink_smem_rx_peek() before the memcpy_fromio() so the availability (head) read is ordered ahead of the FIFO payload read, matching the consumer pattern in Documentation/core-api/circular-buffers.rst.
--- drivers/rpmsg/qcom_glink_smem.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/rpmsg/qcom_glink_smem.c +7 -0
@@ -103,6 +103,13 @@ static void glink_smem_rx_peek(struct qcif (tail >= pipe->native.length) tail -= pipe->native.length; +/* +* Order the availability (head) read in glink_smem_rx_avail() +* against the FIFO payload read below, so APPS never consumes +* stale data the remote has not yet published. +*/ +rmb (); + len =min_t (size_t, count, pipe->native.length - tail);if (len)memcpy_fromio (data, pipe->fifo + tail, len);
Greg Kroah-Hartman
maintainerPATCH 422/713 Revert "arm64: dts: rockchip: Further describe the WiFi for the Pinephone Pro"
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oren Klopfer <[email protected]>
commit 5f19535351bf28f28d702f452d11a1216ec2bd28 upstream.
This reverts commit 096bd8c679185f898cae9933c6a68650fa26ea4f.
Just as with the Pinebook Pro, there are multiple chipset variants for the Pinephone Pro, and multiple firmware binaries for different distributions. The change causes issues with some of these combinations, and reverting it resolves the issues. See the Closes below for the full report.
Similarly with the Pinebook Pro adjustment, the original commit only indicates "further description" and not indicative of fixing any existing issues, so reverting should not kick any back up.
--- arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts | 18 ------------------ 1 file changed, 18 deletions(-)
arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +0 -18
@@ -734,12 +734,6 @@ }; }; - wifi { - wifi_host_wake_l: wifi-host-wake-l { - rockchip,pins = <4 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - wireless-bluetooth { bt_wake_pin: bt-wake-pin { rockchip,pins = <2 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>;
@@ -766,19 +760,7 @@ pinctrl-names = "default"; pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>; sd-uhs-sdr104; - #address-cells = <1>; - #size-cells = <0>; status = "okay"; - - brcmf: wifi@1 { - compatible = "brcm,bcm4329-fmac"; - reg = <1>; - interrupt-parent = <&gpio4>; - interrupts = <RK_PD0 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "host-wake"; - pinctrl-names = "default"; - pinctrl-0 = <&wifi_host_wake_l>; - }; }; &pwm0 {
Greg Kroah-Hartman
maintainerPATCH 423/713 arm64: dts: qcom: kodiak: avoid EFI overlap for ADSP remote heap
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jianping Li <[email protected]>
commit 9d7d847ff1c502802ac764120f9a1800b94ce497 upstream.
On KODIAK platforms boot can fail when the DT "adsp-rpc-remote-heap" reserved-memory region overlaps with firmware allocations (UEFI/EFI runtime). The kernel then reports failure to reserve the region and subsequent EFI runtime activity may trigger aborts.
The remote heap node was described as a fixed "no-map" region, which turns it into a hard carveout. Replace it with a "shared-dma-pool" reserved memory region with reusable CMA-backed allocation, specifying alignment and size.
This avoids hard carveouts and reduces the chance of conflicting with firmware memory maps while keeping an explicit pool for ADSP remote heap usage.
--- arch/arm64/boot/dts/qcom/kodiak.dtsi | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
arch/arm64/boot/dts/qcom/kodiak.dtsi +6 -3
@@ -191,9 +191,12 @@ rmtfs_mem: rmtfs@9c900000 { qcom,vmid = <QCOM_SCM_VMID_MSS_MSA>; }; - adsp_rpc_remote_heap_mem: adsp-rpc-remote-heap@9cb80000 { - reg = <0x0 0x9cb80000 0x0 0x800000>; - no-map; + adsp_rpc_remote_heap_mem: adsp-rpc-remote-heap { + compatible = "shared-dma-pool"; + alloc-ranges = <0x0 0x80000000 0x0 0x80000000>; + reusable; + alignment = <0x0 0x400000>; + size = <0x0 0x800000>; }; };
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 424/713 arm64: dts: qcom: sm6115-pro1x: Correct touchscreen GPIO flags
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Kozlowski <[email protected]>
commit 8e73ae5c34e4fbbd25a8324e3c0eb1e845d7f01e upstream.
IRQ_TYPE_xxx flags are not correct in the context of GPIO flags. These are simple defines so they could be used in DTS but they will not have the same meaning: IRQ_TYPE_LEVEL_LOW = 8 = GPIO_TRANSITORY.
Correct the touchscreen irq-gpios to use proper flags, assuming the author of the code wanted similar logical behavior:
IRQ_TYPE_LEVEL_LOW => GPIO_ACTIVE_LOW
--- arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts +1 -1
@@ -151,7 +151,7 @@ interrupts-extended = <&tlmm 80 IRQ_TYPE_LEVEL_LOW>; - irq-gpios = <&tlmm 80 IRQ_TYPE_LEVEL_LOW>; + irq-gpios = <&tlmm 80 GPIO_ACTIVE_LOW>; reset-gpios = <&tlmm 71 GPIO_ACTIVE_HIGH>; AVDD28-supply = <&ts_vdd_supply>; VDDIO-supply = <&ts_vddio_supply>;
Greg Kroah-Hartman
maintainerPATCH 425/713 arm64: dts: qcom: x1-dell-thena: mark l12b and l15b always-on
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Scott <[email protected]>
commit 06c8fc3e132ce8659bc9f0877b5c6daaf41aadbd upstream.
The l12b and l15b supplies are used by components that are not (fully) described (and some never will be) and must never be disabled.
Mark the regulators as always-on to prevent them from being disabled, for example, when consumers probe defer or suspend.
Note that these supplies currently have no consumers described in mainline for dell-thena beyond the audio codec (vdd-buck/vdd-rxtx/ vdd-io on wcd938x), which can release them when the codec goes idle. The board-level gpio-fixed regulators that feed the Type-C retimer's VDDIO and other rails are not described with a vin-supply link, so the kernel cannot keep their parent LDOs alive on its own.
This mirrors the same change Johan Hovold applied to every other X1E80100 board in a March 2025 series; commit 63169c07d740 ("arm64: dts: qcom: x1e80100-dell-xps13-9345: mark l12b and l15b always-on") is representative. The dell-thena board file was introduced four months later and did not inherit that change; this patch closes the gap.
--- arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi | 2 ++ 1 file changed, 2 insertions(+)
arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi +2 -0
@@ -589,6 +589,7 @@ regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + regulator-always-on; }; vreg_l13b_3p0: ldo13 {
@@ -610,6 +611,7 @@ regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + regulator-always-on; }; };
Greg Kroah-Hartman
maintainerPATCH 426/713 arm64: dts: rockchip: fix eMMC reset polarity on PP-1516
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Quentin Schulz <[email protected]>
commit 2a08921edcab6a462fa6ddb02c91b90b5ac92429 upstream.
According to the Jedec 5.1 specification, the device is held in reset when RST_n is low, therefore the polarity of the line must be that, as specified in the Device Tree binding (mmc/mmc-pwrseq-emmc.yaml).
Due to the wrong polarity, eMMC devices with RST_n_FUNCTION[162] bitfield [1:0] set to 0x1 (the default is 0x0) will be held in reset forever.
--- arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi +1 -1
@@ -33,7 +33,7 @@ compatible = "mmc-pwrseq-emmc"; pinctrl-0 = <&emmc_reset>; pinctrl-names = "default"; - reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_LOW>; }; gpio-leds {
Greg Kroah-Hartman
maintainerPATCH 427/713 arm64: dts: rockchip: fix eMMC reset polarity on PX30 Ringneck
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Quentin Schulz <[email protected]>
commit dfe078755706ed50651ebbe0442843ecd4ae8389 upstream.
According to the Jedec 5.1 specification, the device is held in reset when RST_n is low, therefore the polarity of the line must be that, as specified in the Device Tree binding (mmc/mmc-pwrseq-emmc.yaml).
Due to the wrong polarity, eMMC devices with RST_n_FUNCTION[162] bitfield [1:0] set to 0x1 (the default is 0x0) will be held in reset forever.
--- arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi +1 -1
@@ -26,7 +26,7 @@ compatible = "mmc-pwrseq-emmc"; pinctrl-0 = <&emmc_reset>; pinctrl-names = "default"; - reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_LOW>; }; leds {
Greg Kroah-Hartman
maintainerPATCH 428/713 arm64: dts: rockchip: fix emmc reset polarity on px30-cobra
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakob Unterwurzacher <[email protected]>
commit 85babf47515e2adf266dcc3be9804e31f752083e upstream.
Technically, the reset signal is active low - it's called RST_n after all.
But it is ignored completely unless RST_n_FUNCTION=1 (byte 162 in extcsd) is set in the emmc. It is 0 per default.
For emmcs that have RST_n_FUNCTION=1 we failed like this:
[ 3.074480] mmc1: Failed to initialize a non-removable card
With this change they work normally.
--- arch/arm64/boot/dts/rockchip/px30-cobra.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/boot/dts/rockchip/px30-cobra.dtsi +1 -1
@@ -35,7 +35,7 @@ compatible = "mmc-pwrseq-emmc"; pinctrl-0 = <&emmc_reset>; pinctrl-names = "default"; - reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_LOW>; }; gpio-leds {
Greg Kroah-Hartman
maintainerPATCH 429/713 arm64: dts: rockchip: Fix rk3399-roc-pc-plus analog audio
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fabio Estevam <[email protected]>
commit 4f7259ebe1eba4778768a4f5a0bbbe439d10f3f3 upstream.
The ES8388 sound card on the rk3399-roc-pc-plus fails to probe because i2s1 cannot claim its MCLK pin:
pinctrl: pin gpio4-0 already requested by ff880000.i2s; cannot claim for ff890000.i2s
pinctrl: error -EINVAL: pin-128 (ff890000.i2s)
pinctrl: error -EINVAL: could not request pin 128 (gpio4-0) from group i2s-8ch-mclk-pin on device rockchip-pinctrl
GPIO4_A0 is routed as SCLK_I2S_8CH_OUT and is used by i2s1 as the external MCLK for the ES8388 codec. The board dts already removes GPIO4_A0 from the i2s0_8ch_bus pin group, but i2s0 still claims the same pin through its bclk_off state.
Since the i2s driver requests both states, this blocks i2s1 pinctrl setup and leaves the simple-audio-card deferred with a parse error.
Override i2s0_8ch_bus_bclk_off as well, matching the existing i2s0_8ch_bus override, so GPIO4_A0 is left for i2s1/ES8388 audio.
--- arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts | 12 ++++++++++++ 1 file changed, 12 insertions(+)
arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts +12 -0
@@ -136,6 +136,18 @@ <3 RK_PD7 1 &pcfg_pull_none>; }; +&i2s0_8ch_bus_bclk_off { + rockchip,pins = + <3 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>, + <3 RK_PD1 1 &pcfg_pull_none>, + <3 RK_PD2 1 &pcfg_pull_none>, + <3 RK_PD3 1 &pcfg_pull_none>, + <3 RK_PD4 1 &pcfg_pull_none>, + <3 RK_PD5 1 &pcfg_pull_none>, + <3 RK_PD6 1 &pcfg_pull_none>, + <3 RK_PD7 1 &pcfg_pull_none>; +}; + &i2s1 { pinctrl-names = "default"; pinctrl-0 = <&i2s_8ch_mclk_pin>, <&i2s1_2ch_bus>;
Greg Kroah-Hartman
maintainerPATCH 430/713 arm64: dts: rockchip: Fix rk3588s-roc-pc audio description
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fabio Estevam <[email protected]>
commit b4db45b3ec97a9d0d23446084f45e095b2ba2020 upstream.
The rk3588s-roc-pc ES8388 codec is connected to the i2s0_8ch audio interface. Use the matching I2S0 MCLK output for the codec clock instead of I2S1.
Using the I2S1 MCLK can leave the ALSA PCM running while the codec has no usable master clock for the active audio path, resulting in silent headphone output.
Also make the CPU DAI provide bitclock and frame clock. This matches the active Rockchip I2S controller side and avoids relying on the codec to drive the bus clocks.
Route the headphone output to LOUT2 and ROUT2, matching the old 5.10 BSP device tree. LOUT1 and ROUT1 are used for the speaker route there, so using them for the headphone widget can leave the headphone jack silent even while the ALSA path is active.
The old BSP also used hp-con-gpio on GPIO1_A4. Model that GPIO as a simple audio amplifier so DAPM enables the headphone connection when the headphone path is active.
--- arch/arm64/boot/dts/rockchip/rk3588s-roc-pc.dts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-)
arch/arm64/boot/dts/rockchip/rk3588s-roc-pc.dts +16 -7
@@ -23,16 +23,19 @@ compatible = "simple-audio-card"; pinctrl-names = "default"; pinctrl-0 = <&hp_detect>; + simple-audio-card,aux-devs = <&headphones_amp>; simple-audio-card,name = "rockchip,es8388"; - simple-audio-card,bitclock-master = <&masterdai>; + simple-audio-card,bitclock-master = <&cpudai>; simple-audio-card,format = "i2s"; - simple-audio-card,frame-master = <&masterdai>; + simple-audio-card,frame-master = <&cpudai>; simple-audio-card,hp-det-gpios = <&gpio1 RK_PA6 GPIO_ACTIVE_LOW>; simple-audio-card,mclk-fs = <256>; simple-audio-card,pin-switches = "Headphones"; simple-audio-card,routing = - "Headphones", "LOUT1", - "Headphones", "ROUT1", + "Headphones", "Headphone Amp OUTL", + "Headphones", "Headphone Amp OUTR", + "Headphone Amp INL", "LOUT2", + "Headphone Amp INR", "ROUT2", "LINPUT1", "Microphone Jack", "RINPUT1", "Microphone Jack", "LINPUT2", "Onboard Microphone",
@@ -47,11 +50,17 @@ system-clock-frequency = <12288000>; }; - simple-audio-card,cpu { + cpudai: simple-audio-card,cpu { sound-dai = <&i2s0_8ch>; }; }; + headphones_amp: audio-amplifier-headphones { + compatible = "simple-audio-amplifier"; + enable-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>; + sound-name-prefix = "Headphone Amp"; + }; + chosen { stdout-path = "serial2:1500000n8"; };
@@ -327,12 +336,12 @@ es8388: audio-codec@11 { compatible = "everest,es8388", "everest,es8328"; reg = <0x11>; - clocks = <&cru I2S1_8CH_MCLKOUT>; + clocks = <&cru I2S0_8CH_MCLKOUT>; AVDD-supply = <&vcc_3v3_s0>; DVDD-supply = <&vcc_1v8_s0>; HPVDD-supply = <&vcc_3v3_s0>; PVDD-supply = <&vcc_3v3_s0>; - assigned-clocks = <&cru I2S1_8CH_MCLKOUT>; + assigned-clocks = <&cru I2S0_8CH_MCLKOUT>; assigned-clock-rates = <12288000>; #sound-dai-cells = <0>; };
Greg Kroah-Hartman
maintainerPATCH 431/713 riscv: dts: spacemit: k1-bananapi-f3: fix maximum CPU core voltage
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aurelien Jarno <[email protected]>
commit b496282ee4ad07311a13489e15b74fb7078d9a01 upstream.
On the Banana Pi BPI-F3 board, the buck1 and buck2 voltage regulators, which supply the CPU core voltage, are configured with a maximum output voltage of 3.45V, while the highest operating point requires only 1.050V. This means that a kernel bug, OPP misconfiguration, or privileged userspace could request a voltage above the safe operating limit, causing possible permanent CPU damage.
Set the maximum regulator voltage to 1.050V instead.
--- arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +2 -2
@@ -204,14 +204,14 @@ regulators { buck1 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; buck2 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; };
Greg Kroah-Hartman
maintainerPATCH 432/713 riscv: dts: spacemit: k1-milkv-jupiter: fix maximum CPU core voltage
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aurelien Jarno <[email protected]>
commit 56a77ddae794ca0516fc146f6e59bb8cdf4fd9d2 upstream.
On the Milk-V Jupiter board, the buck1 and buck2 voltage regulators, which supply the CPU core voltage, are configured with a maximum output voltage of 3.45V, while the highest operating point requires only 1.050V. This means that a kernel bug, OPP misconfiguration, or privileged userspace could request a voltage above the safe operating limit, causing possible permanent CPU damage.
Set the maximum regulator voltage to 1.050V instead.
--- arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts +2 -2
@@ -211,14 +211,14 @@ pmic@41 { regulators { buck1 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; buck2 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; };
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 433/713 riscv: dts: spacemit: k1-musepi-pro: fix maximum CPU core voltage
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aurelien Jarno <[email protected]>
commit f045253a03589b2616332560b4120e584342a987 upstream.
On the MusePi Pro board, the buck1 and buck2 voltage regulators, which supply the CPU core voltage, are configured with a maximum output voltage of 3.45V, while the highest operating point requires only 1.050V. This means that a kernel bug, OPP misconfiguration, or privileged userspace could request a voltage above the safe operating limit, causing possible permanent CPU damage.
Set the maximum regulator voltage to 1.050V instead.
--- arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts +2 -2
@@ -180,14 +180,14 @@ pmic@41 { regulators { buck1 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; buck2 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; };
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 434/713 riscv: dts: spacemit: k1-orangepi-r2s: fix maximum CPU core voltage
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aurelien Jarno <[email protected]>
commit d66267c35032c46c18b675601d03c80239bcd913 upstream.
On the OrangePi R2S board, the buck1 and buck2 voltage regulators, which supply the CPU core voltage, are configured with a maximum output voltage of 3.45V, while the highest operating point requires only 1.050V. This means that a kernel bug, OPP misconfiguration, or privileged userspace could request a voltage above the safe operating limit, causing possible permanent CPU damage.
Set the maximum regulator voltage to 1.050V instead.
--- arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts +2 -2
@@ -126,14 +126,14 @@ regulators { buck1 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; buck2 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; };
Greg Kroah-Hartman
maintainerPATCH 435/713 riscv: dts: spacemit: k1-orangepi-rv2: fix maximum CPU core voltage
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aurelien Jarno <[email protected]>
commit ae0c38f0a24f95c8759b6b0eaaa5a0d36a7c6af2 upstream.
On the OrangePi RV2 board, the buck1 and buck2 voltage regulators, which supply the CPU core voltage, are configured with a maximum output voltage of 3.45V, while the highest operating point requires only 1.050V. This means that a kernel bug, OPP misconfiguration, or privileged userspace could request a voltage above the safe operating limit, causing possible permanent CPU damage.
Set the maximum regulator voltage to 1.050V instead.
--- arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts +2 -2
@@ -164,14 +164,14 @@ pmic@41 { regulators { buck1 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; buck2 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; };
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 436/713 RISC-V: KVM: Fix PMU event info array size overflow
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guidong Han <[email protected]>
commit 735bc20c24187ca419c9d5e63860a54b91be34bd upstream.
SBI PMU EVENT_GET_INFO stores guest-controlled num_events * sizeof(*einfo) in a 32-bit integer. On RV64, num_events = 0x10000001 makes 0x100000010 truncate to 16. KVM then allocates one entry but loops over the original num_events, causing out-of-bounds reads and writes. A nested guest triggered:
BUG: KASAN: slab-out-of-bounds in kvm_riscv_vcpu_pmu_event_info+0xa4/0x142 Read of size 4 at addr ff600000074d46b0 by task init/1 Call Trace: [<ffffffff8006471c>] kvm_riscv_vcpu_pmu_event_info+0xa4/0x142 [<ffffffff800690c0>] kvm_sbi_ext_pmu_handler+0xca/0x268 [<ffffffff8006779e>] kvm_riscv_vcpu_sbi_ecall+0xec/0x1e6 [<ffffffff8006008c>] kvm_riscv_vcpu_exit+0x48c/0x540 [<ffffffff8005ea0a>] kvm_arch_vcpu_ioctl_run+0x37e/0xc80 Allocated by task 1: __kmalloc_noprof+0x19e/0x4b0 kvm_riscv_vcpu_pmu_event_info+0x72/0x142 kvm_sbi_ext_pmu_handler+0xca/0x268 kvm_riscv_vcpu_sbi_ecall+0xec/0x1e6 kvm_riscv_vcpu_exit+0x48c/0x540 kvm_arch_vcpu_ioctl_run+0x37e/0xc80
The buggy address is located 0 bytes to the right of
allocated 16-byte region [ff600000074d46a0, ff600000074d46b0)
Store the shared-memory size in size_t and reject multiplication overflow. Allocate the guest-driven array with GFP_KERNEL_ACCOUNT so it is charged to kmemcg, and use __GFP_NOWARN to suppress allocation failure warnings. Use kvcalloc() to allow vmalloc fallback and an unsigned long loop index to match num_events.
--- arch/riscv/kvm/vcpu_pmu.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
arch/riscv/kvm/vcpu_pmu.c +9 -5
@@ -12,7 +12,9 @@#include <linux/err.h> #include <linux/kvm_host.h> #include <linux/nospec.h> +#include <linux/overflow.h> #include <linux/perf/riscv_pmu.h> +#include <linux/slab.h> #include <asm/csr.h> #include <asm/kvm_isa.h> #include <asm/kvm_vcpu_sbi.h>
@@ -479,13 +481,14 @@ int kvm_riscv_vcpu_pmu_event_info(struct unsigned long flags, struct kvm_vcpu_sbi_return *retdata) { struct riscv_pmu_event_info *einfo = NULL; -int shmem_size = num_events *sizeof (*einfo); +size_t shmem_size;gpa_t shmem;u32 eidx, etype;u64 econfig;int ret; -if (flags !=0 || (saddr_low & (SZ_16 -1 ) || num_events ==0 )) { +if (flags !=0 || (saddr_low & (SZ_16 -1 )) || num_events ==0 || +check_mul_overflow (num_events,sizeof (*einfo), &shmem_size)) { ret =SBI_ERR_INVALID_PARAM ; goto out; }
@@ -500,7 +503,8 @@ int kvm_riscv_vcpu_pmu_event_info(struct } } - einfo =kzalloc (shmem_size,GFP_KERNEL ); + einfo =kvcalloc (num_events,sizeof (*einfo), +GFP_KERNEL_ACCOUNT | __GFP_NOWARN);if (!einfo) { ret =SBI_ERR_FAILURE ; goto out;
@@ -512,7 +516,7 @@ int kvm_riscv_vcpu_pmu_event_info(struct goto free_mem; } -for (int i =0 ; i < num_events; i++) { +for (unsigned long i =0 ; i < num_events; i++) { eidx = einfo[i].event_idx; etype =kvm_pmu_get_perf_event_type (eidx); econfig =kvm_pmu_get_perf_event_config (eidx, einfo[i].event_data);
@@ -525,7 +529,7 @@ int kvm_riscv_vcpu_pmu_event_info(struct ret =SBI_ERR_INVALID_ADDRESS ; free_mem: -kfree (einfo); +kvfree (einfo); out: retdata->err_val = ret;
Greg Kroah-Hartman
maintainerPATCH 437/713 riscv: acpi: Handle LPI architectural context loss flags
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peixin Xie <[email protected]>
commit 7e4cb63d61a7e0bef20f0d00e831c7fac06e4a1c upstream.
Commit 4785aa802853 ("cpuidle, ACPI: Evaluate LPI arch_flags for broadcast timer") replaced the generic nonzero check for LPI architectural context loss flags with arch_get_idle_state_flags(). RISC-V does not implement the helper, so it falls back to the stub that returns 0. Consequently, CPUIDLE_FLAG_TIMER_STOP is not set when an LPI state loses the hart timer context, preventing cpuidle from using a broadcast timer for that state.
Implement the RISC-V helper and map the hart timer context loss flag to CPUIDLE_FLAG_TIMER_STOP.
--- arch/riscv/include/asm/acpi.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
arch/riscv/include/asm/acpi.h +19 -0
@@ -12,6 +12,8 @@#ifndef _ASM_ACPI_H#define _ASM_ACPI_H +#include <linux/cpuidle.h> +/* Basic configuration for ACPI */ #ifdef CONFIG_ACPI
@@ -67,6 +69,23 @@ int acpi_get_riscv_isa(struct acpi_tablevoid acpi_get_cbo_block_size (struct acpi_table_header *table,u32 *cbom_size,u32 *cboz_size,u32 *cbop_size); + +/* +* RISC-V Functional Fixed Hardware Specification Version v1.0.1, +* Chapter 3.1.2, Table 4: Arch. Context Lost Flags +*/ +#define RISCV_LPI_HART_TIMER_CTXT_LOST BIT(0) + +static inline unsigned int arch_get_idle_state_flags (u32 arch_flags) +{ +if (arch_flags &RISCV_LPI_HART_TIMER_CTXT_LOST ) +return CPUIDLE_FLAG_TIMER_STOP ; + +return 0 ; +} + +#define arch_get_idle_state_flags arch_get_idle_state_flags +#else static inline void acpi_init_rintc_map (void ) { }static inline struct acpi_madt_rintc *acpi_cpu_get_madt_rintc (int cpu)
Greg Kroah-Hartman
maintainerPATCH 438/713 riscv: unaligned: stop using kthread for check_vector_unaligned_access()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nam Cao <[email protected]>
commit 34c9cfcde29b938c416924ee6ec3519270bc2238 upstream.
A kthread is used to run check_vector_unaligned_access() to optimize boot time, allowing the kernel to continue booting without waiting for the unaligned vector speed probe to finish.
However, this asynchronous approach introduces several complications. First, the kthread may not complete before a user reads vDSO data, resulting in incorrect values. This was previously addressed by commit 5d15d2ad36b0 ("riscv: hwprobe: Fix stale vDSO data for late-initialized keys at boot"), which added complex synchronization between the kthread and vDSO reads.
Second, it was discovered that the kthread may not finish before vec_check_unaligned_access_speed_all_cpus() (marked with __init) is freed, triggering a page fault.
These issues raise the question of whether the kthread is worth the added complexity. A past boot time regression report was actually unrelated to synchronous probing; it was caused by the probe running serially. Since switching to a parallel probe, no further complaints have been made. Furthermore, the unaligned scalar access speed probe takes the same amount of time, runs synchronously, and has caused no issues.
Testing shows no noticeable boot time slowdown when running the vector probe synchronously (0.464474s with kthread vs. 0.457991s without).
Remove the kthread usage and run the probe synchronously. This simplifies the boot flow and allows for the revert of commit 5d15d2ad36b0 ("riscv:
hwprobe: Fix stale vDSO data for late-initialized keys at boot")
--- arch/riscv/kernel/unaligned_access_speed.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-)
arch/riscv/kernel/unaligned_access_speed.c +2 -17
@@ -6,7 +6,6 @@#include <linux/cpu.h> #include <linux/cpumask.h> #include <linux/jump_label.h> -#include <linux/kthread.h> #include <linux/mm.h> #include <linux/smp.h> #include <linux/types.h>
@@ -288,18 +287,9 @@ free: __free_pages(page, MISALIGNED_BUFFER_ORDER); } -/* Measure unaligned access speed on all CPUs present at boot in parallel. */ -static int vec_check_unaligned_access_speed_all_cpus (void *unused__always_unused ) -{ -schedule_on_each_cpu (check_vector_unaligned_access); -riscv_hwprobe_complete_async_probe (); - -return 0 ; -}#else /* CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS */ -static int vec_check_unaligned_access_speed_all_cpus (void *unused__always_unused ) +static void check_vector_unaligned_access (struct work_struct *work__always_unused ) { -return 0 ; }#endif
@@ -387,12 +377,7 @@ static int __init check_unaligned_accessper_cpu (vector_misaligned_access, cpu) = unaligned_vector_speed_param; }else if (!check_vector_unaligned_access_emulated_all_cpus () &&IS_ENABLED (CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS )) { -riscv_hwprobe_register_async_probe (); -if (IS_ERR (kthread_run (vec_check_unaligned_access_speed_all_cpus, -NULL ,"vec_check_unaligned_access_speed_all_cpus" ))) { -pr_warn ("Failed to create vec_unalign_check kthread\n" ); -riscv_hwprobe_complete_async_probe (); - } +schedule_on_each_cpu (check_vector_unaligned_access); }/*
Greg Kroah-Hartman
maintainerPATCH 439/713 remoteproc: scp: Fix device reference leak on failed lookup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 22f9efb3ae07f966a1901d929d16df1388cce65c upstream.
Make sure to drop the reference taken to the SCP device when attempting to look up its driver data before the driver has been bound.
Note that holding a reference to a device does not prevent its driver data from going away.
--- drivers/remoteproc/mtk_scp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/remoteproc/mtk_scp.c +8 -1
@@ -36,6 +36,7 @@ struct mtk_scp *scp_get(struct platform_struct device *dev = &pdev->dev;struct device_node *scp_node;struct platform_device *scp_pdev; +struct mtk_scp *scp; scp_node =of_parse_phandle (dev->of_node,"mediatek,scp" ,0 );if (!scp_node) {
@@ -51,7 +52,13 @@ struct mtk_scp *scp_get(struct platform_return NULL ; } -return platform_get_drvdata (scp_pdev); + scp =platform_get_drvdata (scp_pdev); +if (!scp) { +put_device (&scp_pdev->dev); +return NULL ; + } + +return scp; }EXPORT_SYMBOL_GPL (scp_get);
Greg Kroah-Hartman
maintainerPATCH 440/713 ptp: vmclock: prevent read-only mappings from becoming writable
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdifatah Suruur <[email protected]>
commit a5edadbae57e2298a56cf7a4e774a027905a331f upstream.
vmclock_miscdev_mmap() rejects writable mappings of the shared vmclock ABI page with -EROFS, but leaves VM_MAYWRITE set. Userspace can map the page read-only and then upgrade it to writable with mprotect(), after which the guest can corrupt the host-written timekeeping data (sequence counter, UTC time, TSC offset) that the vmclock ABI defines as read-only.
Clear VM_MAYWRITE on the read-only path so the mapping cannot be upgraded, as i915 does for its read-only objects and as fixed in drm/vc4 (CVE-2026-68445) and drm/panthor (CVE-2024-53071).
--- drivers/ptp/ptp_vmclock.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/ptp/ptp_vmclock.c +6 -0
@@ -378,6 +378,12 @@ static int vmclock_miscdev_mmap(struct f*/ vm_flags_clear (vma,VM_MAYWRITE ); +/* +* Restrict the read-only mapping so it cannot be upgraded to +* writable later with mprotect(). +*/ +vm_flags_clear (vma,VM_MAYWRITE ); +if (vma->vm_end - vma->vm_start !=PAGE_SIZE || vma->vm_pgoff)return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 441/713 qede: Fix NULL pointer dereference in TPA fragment processing
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vaibhav Nagare <[email protected]>
commit 06aa3d26327f24edd039ff249672fdf6f2ba5695 upstream.
Under memory pressure, the qede driver encounters NULL pointer dereferences when processing TPA continuation fragments.
Commit 8a8633978b84 ("qede: Add build_skb() support.") accidentally dropped the assignment of tpa_info->buffer.data in qede_tpa_start().
When memory pressure causes an SKB allocation failure in qede_tpa_start(), the driver sets tpa_start_fail = true and attempts to recycle the physical page later in qede_tpa_end() via qede_reuse_page(). However, because buffer.data was left uninitialized (NULL), qede_reuse_page() pushes a "ghost" BD (valid DMA mapping but NULL data pointer) back into the active Rx ring.
The next time the hardware uses this ring slot, it passes a NULL page to qede_fill_frag_skb(), causing a kernel panic.
Example crash from production system:
BUG: unable to handle kernel NULL pointer dereference at 0x8 RIP: qede_fill_frag_skb+0x96/0x430 [qede] Call Trace: qede_rx_int+0xb06/0x1de0 qede_poll+0x2f4/0x6c0 __napi_poll+0x2d/0x130
Fix the root cause by restoring the tpa_info->buffer.data assignment in qede_tpa_start(), ensuring valid pages are correctly tracked and recycled. Additionally, update the stale comment for struct qede_agg_info::buffer to reflect its current usage.
--- drivers/net/ethernet/qlogic/qede/qede.h | 8 ++++---- drivers/net/ethernet/qlogic/qede/qede_fp.c | 1 + 2 files changed, 5 insertions(+), 4 deletions(-)
drivers/net/ethernet/qlogic/qede/qede.h +4 -4
@@ -303,10 +303,10 @@ enum qede_agg_state { };struct qede_agg_info { -/* rx_buf is a data buffer that can be placed / consumed from rx bd -* chain. It has two purposes: We will preallocate the data buffer -* for each aggregation when we open the interface and will place this -* buffer on the rx-bd-ring when we receive TPA_START. We don't want +/* buffer is used to retain the Rx consumer descriptor when a TPA +* session starts. If the SKB allocation fails during TPA_START, +* we use this saved buffer to safely recycle the physical page +* back into the rx-bd-ring via qede_reuse_page(). We don't want * to be in a state where allocation fails, as we can't reuse the * consumer buffer in the rx-chain since FW may still be writing to it * (since header needs to be modified for TPA).
drivers/net/ethernet/qlogic/qede/qede_fp.c +1 -0
@@ -850,6 +850,7 @@ static void qede_tpa_start(struct qede_d pad, false); tpa_info->buffer.page_offset = sw_rx_data_cons->page_offset; tpa_info->buffer.mapping = sw_rx_data_cons->mapping; + tpa_info->buffer.data = sw_rx_data_cons->data;if (unlikely (!tpa_info->skb)) {DP_NOTICE (edev,"Failed to allocate SKB for gro\n" );
Greg Kroah-Hartman
maintainerPATCH 442/713 RDMA/cxgb4: Cancel reg_work before freeing device on remove
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit a7100601aa1a39f799a566acce10db20eaf4b7f2 upstream.
c4iw_uld_state_change() queues reg_work to register the RDMA device. c4iw_remove() can free ctx->dev while this work is pending or running, leaving c4iw_register_device() accessing the freed device.
Cancel reg_work before removing the device. The registration work can tear down ctx->dev when registration fails, so do not unregister or deallocate it again in that case.
This issue was found by an in-house static analysis tool.
--- drivers/infiniband/hw/cxgb4/device.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/infiniband/hw/cxgb4/device.c +6 -0
@@ -951,6 +951,12 @@ void c4iw_dealloc(struct uld_ctx *ctx)static void c4iw_remove (struct uld_ctx *ctx) {pr_debug ("c4iw_dev %p\n" , ctx->dev); + +/* c4iw_register_device() may still be using ctx->dev. */ +cancel_work_sync (&ctx->reg_work); +if (!ctx->dev) +return ; +debugfs_remove_recursive (ctx->dev->debugfs_root);c4iw_unregister_device (ctx->dev);c4iw_dealloc (ctx);
Greg Kroah-Hartman
maintainerPATCH 443/713 RDMA/ionic: Cap eq_count to the eth drivers interrupt vector budget
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Brett Creeley <[email protected]>
commit 1d0f877d593438a494ca5b05cc8699150409005a upstream.
ionic_fill_lif_cfg() reads eq_count from firmware uncapped, but the eth driver only reserves ionic->neqs_per_lif MSI-X vectors for RDMA event queues. Since ionic_rdma probes via the auxiliary bus before the netdev is brought up, it can exhaust the shared interrupt bitmap, causing ionic_open() to fail with -ENOSPC when allocating rx/tx interrupts.
Cap RDMA eq_count to neqs_per_lif, which is populated by ionic_lif_size() at PCI probe before the RDMA aux device registers.
--- drivers/infiniband/hw/ionic/ionic_lif_cfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/infiniband/hw/ionic/ionic_lif_cfg.c +1 -1
@@ -70,7 +70,7 @@ void ionic_fill_lif_cfg(struct ionic_lif* eq_count is tunable; see ionic_eq_count */ cfg->aq_count =le32_to_cpu (ident->rdma.aq_qtype.qid_count); - cfg->eq_count =le32_to_cpu (ident->rdma.eq_qtype.qid_count); + cfg->eq_count = lif->ionic->neqs_per_lif; cfg->cq_count =le32_to_cpu (ident->rdma.cq_qtype.qid_count); cfg->qp_count =le32_to_cpu (ident->rdma.sq_qtype.qid_count); cfg->dbid_count =le32_to_cpu (lif->ionic->ident.dev.ndbpgs_per_lif);
Greg Kroah-Hartman
maintainerPATCH 444/713 RDMA/ionic: Embed counter driver data in rdma_counter allocation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abhijit Gangurde <[email protected]>
commit cf3ebd89e754015625fee90aa938f6bc79a2c974 upstream.
Commit 7e53b31acc7f ("RDMA/core: Create and destroy rdma_counter using rdma_zalloc_drv_obj()") requires drivers implementing counter ops to embed struct rdma_counter in a driver-specific struct, register its size via INIT_RDMA_OBJ_SIZE, and provide a counter_init callback.
The ionic driver was merged without this adaptation, causing a NULL pointer dereference in alloc_and_bind() since rdma_zalloc_drv_obj() allocates zero bytes when size_rdma_counter is unset.
Consolidate struct ionic_counter into a new struct ionic_rdma_counter that embeds struct rdma_counter, replace the xarray with a lightweight ida for ID allocation, and add the required counter_init and INIT_RDMA_OBJ_SIZE declarations.
--- drivers/infiniband/hw/ionic/ionic_hw_stats.c | 101 ++++++++++++--------------- drivers/infiniband/hw/ionic/ionic_ibdev.h | 11 ++ 2 files changed, 54 insertions(+), 58 deletions(-)
drivers/infiniband/hw/ionic/ionic_hw_stats.c +45 -56
@@ -235,35 +235,34 @@ err_dma:static struct rdma_hw_stats *ionic_counter_alloc_stats (struct rdma_counter *counter) { +struct ionic_rdma_counter *cntr =to_ionic_rdma_counter (counter);struct ionic_ibdev *dev =to_ionic_ibdev (counter->device); -struct ionic_counter *cntr; -int err; +struct rdma_hw_stats *stats; +int id; - cntr =kzalloc_obj (*cntr); -if (!cntr) -return NULL ; - -/* buffer for current values from the device */ cntr->vals =kzalloc (PAGE_SIZE ,GFP_KERNEL );if (!cntr->vals) - goto err_vals; - - err =xa_alloc (&dev->counter_stats->xa_counters, &counter->id, - cntr, -XA_LIMIT (0 ,IONIC_MAX_QPID ), -GFP_KERNEL ); -if (err) - goto err_xa; - -INIT_LIST_HEAD (&cntr->qp_list); +return NULL ; -return rdma_alloc_hw_stats_struct (dev->counter_stats->stats_hdrs, - dev->counter_stats->queue_stats_count, -RDMA_HW_STATS_DEFAULT_LIFESPAN ); -err_xa: + id =ida_alloc_max (&dev->counter_stats->counter_ida, +IONIC_MAX_QPID ,GFP_KERNEL ); +if (id <0 ) + goto err_ida; + + counter->id = id; + + stats =rdma_alloc_hw_stats_struct (dev->counter_stats->stats_hdrs, + dev->counter_stats->queue_stats_count, +RDMA_HW_STATS_DEFAULT_LIFESPAN ); +if (!stats) + goto err_hw_stats; + +return stats; + +err_hw_stats: +ida_free (&dev->counter_stats->counter_ida, id); +err_ida:kfree (cntr->vals); -err_vals: -kfree (cntr);return NULL ; }
@@ -271,14 +270,10 @@ err_vals:static int ionic_counter_dealloc (struct rdma_counter *counter) {struct ionic_ibdev *dev =to_ionic_ibdev (counter->device); -struct ionic_counter *cntr; - - cntr =xa_erase (&dev->counter_stats->xa_counters, counter->id); -if (!cntr) -return -EINVAL ; +struct ionic_rdma_counter *cntr =to_ionic_rdma_counter (counter); +ida_free (&dev->counter_stats->counter_ida, counter->id);kfree (cntr->vals); -kfree (cntr);return 0 ; }
@@ -287,13 +282,8 @@ static int ionic_counter_bind_qp(struct struct ib_qp *ibqp, u32 port) { -struct ionic_ibdev *dev =to_ionic_ibdev (counter->device); +struct ionic_rdma_counter *cntr =to_ionic_rdma_counter (counter);struct ionic_qp *qp =to_ionic_qp (ibqp); -struct ionic_counter *cntr; - - cntr =xa_load (&dev->counter_stats->xa_counters, counter->id); -if (!cntr) -return -EINVAL ;list_add_tail (&qp->qp_list_counter, &cntr->qp_list); ibqp->counter = counter;
@@ -313,29 +303,23 @@ static int ionic_counter_unbind_qp(strucreturn 0 ; } -static int ionic_get_qp_stats (struct ib_device *ibdev, -struct rdma_hw_stats *hw_stats, -u32 counter_id) -{ -struct ionic_ibdev *dev =to_ionic_ibdev (ibdev); -struct ionic_counter_stats *cs; -struct ionic_counter *cntr; +static int ionic_counter_update_stats (struct rdma_counter *counter) +{ +struct ionic_rdma_counter *cntr =to_ionic_rdma_counter (counter); +struct ionic_ibdev *dev =to_ionic_ibdev (counter->device); +struct ionic_counter_stats *cs = dev->counter_stats;dma_addr_t hw_stats_dma;struct ionic_qp *qp;int rc, stat_i =0 ; - cs = dev->counter_stats; - cntr =xa_load (&cs->xa_counters, counter_id); -if (!cntr) -return -EINVAL ; - hw_stats_dma =dma_map_single (dev->lif_cfg.hwdev, cntr->vals,PAGE_SIZE ,DMA_FROM_DEVICE ); rc =dma_mapping_error (dev->lif_cfg.hwdev, hw_stats_dma);if (rc)return rc; -memset (hw_stats->value,0 ,sizeof (u64) * hw_stats->num_counters); +memset (counter->stats->value,0 , +sizeof (u64) * counter->stats->num_counters);list_for_each_entry (qp, &cntr->qp_list, qp_list_counter) { rc = ionic_hw_stats_cmd(dev, hw_stats_dma, PAGE_SIZE,
@@ -345,7 +329,7 @@ static int ionic_get_qp_stats(struct ib_ goto err_cmd;for (stat_i =0 ; stat_i < cs->queue_stats_count; ++stat_i) - hw_stats->value[stat_i] += + counter->stats->value[stat_i] +=ionic_v1_stat_val (&cs->hdr[stat_i], cntr->vals,PAGE_SIZE );
@@ -360,11 +344,6 @@ err_cmd:return rc; } -static int ionic_counter_update_stats (struct rdma_counter *counter) -{ -return ionic_get_qp_stats (counter->device, counter->stats, counter->id); -} -static int ionic_alloc_counters (struct ionic_ibdev *dev) {struct ionic_counter_stats *cs = dev->counter_stats;
@@ -424,12 +403,22 @@ static const struct ib_device_ops ionic_ .get_hw_stats = ionic_get_hw_stats, }; +static void ionic_counter_init (struct rdma_counter *counter) +{ +struct ionic_rdma_counter *cntr =to_ionic_rdma_counter (counter); + +INIT_LIST_HEAD (&cntr->qp_list); +} +static const struct ib_device_ops ionic_counter_stats_ops = { .counter_alloc_stats = ionic_counter_alloc_stats, .counter_dealloc = ionic_counter_dealloc, .counter_bind_qp = ionic_counter_bind_qp, .counter_unbind_qp = ionic_counter_unbind_qp, .counter_update_stats = ionic_counter_update_stats, + .counter_init = ionic_counter_init, + +INIT_RDMA_OBJ_SIZE (rdma_counter, ionic_rdma_counter, rdma_counter), };void ionic_stats_init (struct ionic_ibdev *dev)
@@ -458,7 +447,7 @@ void ionic_stats_init(struct ionic_ibdevreturn ; } -xa_init_flags (&dev->counter_stats->xa_counters,XA_FLAGS_ALLOC ); +ida_init (&dev->counter_stats->counter_ida);ib_set_device_ops (&dev->ibdev, &ionic_counter_stats_ops); }
@@ -467,7 +456,7 @@ void ionic_stats_init(struct ionic_ibdevvoid ionic_stats_cleanup (struct ionic_ibdev *dev) {if (dev->counter_stats) { -xa_destroy (&dev->counter_stats->xa_counters); +ida_destroy (&dev->counter_stats->counter_ida);kfree (dev->counter_stats->hdr);kfree (dev->counter_stats->stats_hdrs);kfree (dev->counter_stats);
drivers/infiniband/hw/ionic/ionic_ibdev.h +9 -2
@@ -331,14 +331,21 @@ struct ionic_counter_stats {int queue_stats_count;struct ionic_v1_stat *hdr;struct rdma_stat_desc *stats_hdrs; -struct xarray xa_counters; +struct ida counter_ida; }; -struct ionic_counter { +struct ionic_rdma_counter { +struct rdma_counter rdma_counter;void *vals;struct list_head qp_list; }; +static inline struct ionic_rdma_counter * +to_ionic_rdma_counter (struct rdma_counter *counter) +{ + return container_of(counter, struct ionic_rdma_counter, rdma_counter); +} +static inline struct ionic_ibdev *to_ionic_ibdev (struct ib_device *ibdev) { return container_of(ibdev, struct ionic_ibdev, ibdev);
Greg Kroah-Hartman
maintainerPATCH 445/713 RDMA/ucma: Lock the handler in ucma_set_ib_path()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
commit ecbe7d36dc2de07e5dfbb4a8ff5b315ab43de820 upstream.
ucma_set_ib_path() calls ucma_event_handler() straight from the write() path, without the handler lock that keeps ctx->file stable while a uevent is queued. The handler re-reads ctx->file for every dereference:
mutex_lock(&ctx->file->mut); /* file A */ list_add_tail(&uevent->list, &ctx->file->event_list); /* file B */ mutex_unlock(&ctx->file->mut); /* file B */ wake_up_interruptible(&ctx->file->poll_wait); /* file B */
A concurrent ucma_migrate_id() reassigns ctx->file while the SET_OPTION caller sleeps in mutex_lock(), so the list_add_tail() lands on file B's event_list while only file A's mutex is held, racing every other user of that list:
BUG: KASAN: slab-use-after-free in __list_add_valid_or_report+0x1aa/0x1c0 Read of size 8 at addr ffff888153c6a418 by task poc_corr/486 Call Trace: __list_add_valid_or_report+0x1aa/0x1c0 ucma_event_handler+0x1be/0xc00 ucma_set_ib_path+0x45e/0x710 ucma_set_option+0x32e/0x590 ucma_write+0x1f9/0x330 Allocated by task 505: ucma_write_cm_event+0x1a1/0x660 Freed by task 505: kfree+0x1da/0x4c0 ucma_get_event+0x5d5/0x7e0
The freed object is a ucma_event that another thread dequeued from file B's list under file B's mutex. File A's mut is left held on top of that, wedging its next writer in uninterruptible sleep.
This path needs a bound and address-resolved cm_id, so it requires an RDMA device to be present.
Take the handler lock around the call.
--- drivers/infiniband/core/ucma.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/infiniband/core/ucma.c +4 -1
@@ -1404,7 +1404,10 @@ static int ucma_set_ib_path(struct ucma_memset (&event,0 ,sizeof event); event.event =RDMA_CM_EVENT_ROUTE_RESOLVED ; -return ucma_event_handler (ctx->cm_id, &event); +rdma_lock_handler (ctx->cm_id); + ret =ucma_event_handler (ctx->cm_id, &event); +rdma_unlock_handler (ctx->cm_id); +return ret; } static int ucma_set_option_ib(struct ucma_context *ctx, int optname,
Greg Kroah-Hartman
maintainerPATCH 446/713 RDMA/ucma: Lock the handler in ucma_write_cm_event()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
commit f4cc21c6a8e9d392871477f9fd98d68e5ad80272 upstream.
ctx->file may only be changed under the handler lock and the xa_lock, which is what stops uevents being queued for a ctx while ucma_migrate_id() moves it to another file. The CM core takes that lock before invoking ucma_event_handler(), but the write() paths that queue uevents themselves do not.
ucma_write_cm_event() re-reads ctx->file for each of its four dereferences, so ucma_migrate_id() can swap it mid-sequence:
mutex_lock(&ctx->file->mut); /* file A */ list_add_tail(&uevent->list, &ctx->file->event_list); /* file B */ mutex_unlock(&ctx->file->mut); /* file B */ wake_up_interruptible(&ctx->file->poll_wait); /* file B */
The window is the mutex_lock() itself: the writer sleeps in it while the migration reassigns ctx->file. The list_add_tail() then runs on file B's event_list holding only file A's mutex:
list_add corruption. prev->next should be next (ffff888101320f30),
but was ffff88814a08c418. (prev=ffff88814a075c18). kernel BUG at lib/list_debug.c:32! Call Trace: ucma_write_cm_event+0x36e/0x5e0
and file A's mut is left held forever, wedging its next writer in D state. The uevent is also stranded on a list ucma_cleanup_ctx_events() will not walk, so it outlives its context. /dev/infiniband/rdma_cm is 0666 and no RDMA device is involved, so an unprivileged user reaches all of this.
Take the handler lock, as ucma_cleanup_mc_events() does; ctx->cm_id is pinned by the ucma_get_ctx() reference.
--- drivers/infiniband/core/ucma.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/infiniband/core/ucma.c +9 -0
@@ -1779,6 +1779,13 @@ static ssize_t ucma_write_cm_event(struc goto out; } +rdma_lock_handler (ctx->cm_id); +if (!ctx->uid) { +kfree (uevent); + ret = -EINVAL ; + goto err_unlock; + } + uevent->ctx = ctx; uevent->resp.uid = ctx->uid; uevent->resp.id = ctx->id;
@@ -1792,6 +1799,8 @@ static ssize_t ucma_write_cm_event(strucmutex_unlock (&ctx->file->mut);wake_up_interruptible (&ctx->file->poll_wait); +err_unlock: +rdma_unlock_handler (ctx->cm_id); out:ucma_put_ctx (ctx);return ret;
Greg Kroah-Hartman
maintainerPATCH 447/713 RDMA/uverbs: Add UVERBS_ATTR_UHW to UVERBS_METHOD_REG_MR
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jason Gunthorpe <[email protected]>
commit 011199f46f44a9fd93a9e5ab5d7fd1328d80e9bf upstream.
The original commit missed that three drivers (mthca, irdma, siw) have UHW data associated with reg_mr that cannot be passed through the ioctl. They also assume that the udata cannot be NULL, so failing to pass a valid udata can trigger a NULL udata crash in those drivers.
This never happens in real systems since in rdma-core ibv_cmd_reg_mr_ex() does not accept a udata and those three drivers don't use it, however a malicious userspace could trigger it.
--- drivers/infiniband/core/uverbs_std_types_mr.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/infiniband/core/uverbs_std_types_mr.c +4 -2
@@ -364,7 +364,8 @@ static int UVERBS_HANDLER(UVERBS_METHOD_ dmah, attrs); else mr = pd->device->ops.reg_user_mr (pd, addr, length, iova, - access_flags, dmah,NULL ); + access_flags, dmah, + &attrs->driver_udata);if (IS_ERR (mr))return PTR_ERR (mr);
@@ -527,7 +528,8 @@ DECLARE_UVERBS_NAMED_METHOD( UA_MANDATORY),UVERBS_ATTR_PTR_OUT (UVERBS_ATTR_REG_MR_RESP_RKEY ,UVERBS_ATTR_TYPE (u32), - UA_MANDATORY)); +UA_MANDATORY ), + UVERBS_ATTR_UHW()); DECLARE_UVERBS_NAMED_METHOD_DESTROY( UVERBS_METHOD_MR_DESTROY,
Greg Kroah-Hartman
maintainerPATCH 448/713 RDMA/uverbs: Guard legacy bundles without method_elm
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuhang Pan <[email protected]>
commit 60a42d510113f46de47e86a84bf5758597644487 upstream.
The legacy write() path dispatches through a uverbs_api_write_method, but the uverbs_attr_bundle passed to provider code does not have an ioctl method element. If malformed provider input causes the common uverbs validation code to emit an error message, uverbs_get_handler_fn() dereferences the uninitialized method_elm pointer.
Initialize method_elm explicitly for legacy bundles and make uverbs_get_handler_fn() return NULL when no ioctl method is present. The legacy dispatcher continues to use its local write method, while the ioctl path continues to use the registered ioctl handler.
--- drivers/infiniband/core/ib_core_uverbs.c | 3 +++ drivers/infiniband/core/uverbs_main.c | 1 + 2 files changed, 4 insertions(+)
drivers/infiniband/core/ib_core_uverbs.c +3 -0
@@ -424,6 +424,9 @@ static uverbs_api_ioctl_handler_fn uverbs_get_handler_fn(struct ib_udata *udata)lockdep_assert_held (&bundle->ufile->device->disassociate_srcu); +if (!bundle->method_elm) +return NULL ; +return srcu_dereference (bundle->method_elm->handler, &bundle->ufile->device->disassociate_srcu); }
drivers/infiniband/core/uverbs_main.c +1 -0
@@ -556,6 +556,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, bundle.ufile = file; bundle.context =NULL ;/* only valid if bundle has uobject */ bundle.uobject =NULL ; + bundle.method_elm =NULL ;if (!method_elm->is_ex) {size_t in_len = hdr.in_words *4 -sizeof (hdr);size_t out_len = hdr.out_words *4 ;
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 449/713 regulator: as3722_get_regulator_dt_data: fix premature of_node_put leaving dangling of_node pointer
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: WenTao Liang <[email protected]>
commit f9324d670ae0b88cbfb0aa48fcaefa5baeb8da4c upstream.
In as3722_get_regulator_dt_data(), of_get_child_by_name() acquires a reference on np, which is then assigned to pdev->dev.of_node. The function immediately calls of_node_put(np), releasing the reference and leaving pdev->dev.of_node as a dangling pointer.
Remove the of_node_put(np) call to let the device hold the reference.
--- drivers/regulator/as3722-regulator.c | 1 - 1 file changed, 1 deletion(-)
drivers/regulator/as3722-regulator.c +0 -1
@@ -600,7 +600,6 @@ static int as3722_get_regulator_dt_data( ret =of_regulator_match (&pdev->dev, np, as3722_regulator_matches,ARRAY_SIZE (as3722_regulator_matches)); -of_node_put (np);if (ret <0 ) {dev_err (&pdev->dev,"Parsing of regulator node failed: %d\n" , ret);
Greg Kroah-Hartman
maintainerPATCH 450/713 regulator: max8998_pmic_dt_parse_pdata: of_node_put on reg_np after ownership transferred to rdata
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: WenTao Liang <[email protected]>
commit 7c8cc25d8d86f9eb3979255935cfdc7d062ad746 upstream.
In max8998_pmic_dt_parse_pdata(), of_get_child_by_name() acquires a reference on reg_np which is then stored in rdata->reg_node, transferring ownership to the regulator data array. The subsequent of_node_put(reg_np) at the end of the function releases the last matched regulator node's reference, leaving rdata->reg_node as a dangling pointer for the last entry.
Remove the spurious of_node_put(reg_np) call.
--- drivers/regulator/max8998.c | 1 - 1 file changed, 1 deletion(-)
drivers/regulator/max8998.c +0 -1
@@ -582,7 +582,6 @@ static int max8998_pmic_dt_parse_pdata(s } pdata->num_regulators = rdata - pdata->regulators; -of_node_put (reg_np);of_node_put (regulators_np); pdata->buck_voltage_lock =of_property_read_bool (pmic_np,"max8998,pmic-buck-voltage-lock" );
Greg Kroah-Hartman
maintainerPATCH 451/713 regulator: qcom-refgen: correct the regulator type to CURRENT
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kathiravan Thirumoorthy <[email protected]>
commit 05dfeb2d0ccf87a7b92cd149a393b8423a26a04e upstream.
As per the REFGEN IP team, this block supplies the reference current to the PHYs in the SoC. So, correct the regulator type to REGULATOR_CURRENT to match with the HW behavior.
--- drivers/regulator/qcom-refgen-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/regulator/qcom-refgen-regulator.c +2 -2
@@ -66,7 +66,7 @@ static const struct regulator_desc sdm84 .enable_time = 5, .name = "refgen", .owner = THIS_MODULE, - .type = REGULATOR_VOLTAGE, + .type = REGULATOR_CURRENT, .ops = &(const struct regulator_ops ) { .enable = qcom_sdm845_refgen_enable, .disable = qcom_sdm845_refgen_disable,
@@ -82,7 +82,7 @@ static const struct regulator_desc sm825 .enable_time = 5, .name = "refgen", .owner = THIS_MODULE, - .type = REGULATOR_VOLTAGE, + .type = REGULATOR_CURRENT, .ops = &(const struct regulator_ops ) { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap,
Greg Kroah-Hartman
maintainerPATCH 452/713 ring-buffer: Fix subbuf resize race with ring_buffer_alloc_read_page()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
commit e743527c5bfdceda1095bc0a9e596e2aebb6a9c3 upstream.
ring_buffer_alloc_read_page() is racy with ring_buffer_subbuf_order_set, it can allocate a reader page with an outdated order. This isn't a big issue, the user can still re-allocate a new reader page and try again.
However, what is more problematic is if the value of subbuf_order changes in the middle of ring_buffer_alloc_read_page(). In that case, bpage->order might not match the actual allocated memory.
Use bpage->order for the allocation to prevent this race.
--- kernel/trace/ring_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/ring_buffer.c +1 -1
@@ -7018,7 +7018,7 @@ ring_buffer_alloc_read_page(struct traceif (bpage->data) {rb_init_data_page (bpage->data); }else { - bpage->data =alloc_cpu_data (cpu, cpu_buffer->buffer->subbuf_order); + bpage->data =alloc_cpu_data (cpu, bpage->order);if (!bpage->data) {kfree (bpage);return ERR_PTR (-ENOMEM );
Greg Kroah-Hartman
maintainerPATCH 453/713 ring-buffer: Free cpu_buffer::free_page with subbuf_order
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
commit 234b1a72e9706fe20c08c96f4374ec8e83b934cb upstream.
When sub-buffers use an order greater than 0, cpu_buffer->free_page is allocated with subbuf_order. Use the correct order for cpu_buffer->free_page.
--- kernel/trace/ring_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/ring_buffer.c +1 -1
@@ -2672,7 +2672,7 @@ static void rb_free_cpu_buffer(struct rifree_buffer_page (bpage); } -free_page ((unsigned long )cpu_buffer->free_page); +free_pages ((unsigned long )cpu_buffer->free_page, cpu_buffer->buffer->subbuf_order);kfree (cpu_buffer); }
Greg Kroah-Hartman
maintainerPATCH 454/713 ring-buffer: Hold cpu_buffer::lock when resizing a subbuf
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
commit 24974bd0da1b47fd56c975533ead50abf754e74d upstream.
Because, ring_buffer_subbuf_order_set() can clear cpu_buffer->free_page, hold cpu_buffer->lock to prevent races with ring_buffer_alloc_read_page() and ring_buffer_free_read_page().
--- kernel/trace/ring_buffer.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/trace/ring_buffer.c +2 -0
@@ -7477,8 +7477,10 @@ int ring_buffer_subbuf_order_set(struct cpu_buffer->nr_pages = cpu_buffer->nr_pages_to_update; cpu_buffer->nr_pages_to_update =0 ; +arch_spin_lock (&cpu_buffer->lock); old_free_data_page = cpu_buffer->free_page; cpu_buffer->free_page =NULL ; +arch_spin_unlock (&cpu_buffer->lock);rb_head_page_activate (cpu_buffer);
Greg Kroah-Hartman
maintainerPATCH 455/713 ring-buffer: Make cpu_buffer::free_page a buffer_data_read_page
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
commit 7a1fb95de5404134f8758c1295ce88986bdf117c upstream.
Discarding a cached reader page after a concurrent ring buffer resize uses the new global subbuf_order for the free_pages() call. This mismatched order may crashes the kernel or leaks memory because the cached page was allocated under the old size.
Save the actual free_page order alongside the page address to ensure we always refer to the correct value and do not rely on the potentially stalled cpu_buffer->subbuf_order value. The simplest is to make free_page a buffer_data_read_page which already covers exactly what we
need: a page address and a page order.
--- kernel/trace/ring_buffer.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
kernel/trace/ring_buffer.c +10 -10
@@ -518,7 +518,7 @@ struct ring_buffer_per_cpu {raw_spinlock_t reader_lock;/* serialize readers */ arch_spinlock_t lock;struct lock_class_key lock_key; -struct buffer_data_page *free_page; +struct buffer_data_read_page free_page;unsigned long nr_pages;unsigned int current_context;struct list_head *pages;
@@ -2672,7 +2672,7 @@ static void rb_free_cpu_buffer(struct rifree_buffer_page (bpage); } -free_pages ((unsigned long )cpu_buffer->free_page, cpu_buffer->buffer->subbuf_order); +free_pages ((unsigned long )cpu_buffer->free_page.data, cpu_buffer->free_page.order);kfree (cpu_buffer); }
@@ -7007,9 +7007,9 @@ ring_buffer_alloc_read_page(struct tracelocal_irq_save (flags);arch_spin_lock (&cpu_buffer->lock); -if (cpu_buffer->free_page) { - bpage->data = cpu_buffer->free_page; - cpu_buffer->free_page =NULL ; +if (cpu_buffer->free_page.data) { + *bpage = cpu_buffer->free_page; + cpu_buffer->free_page.data =NULL ; }arch_spin_unlock (&cpu_buffer->lock);
@@ -7061,8 +7061,8 @@ void ring_buffer_free_read_page(struct tlocal_irq_save (flags);arch_spin_lock (&cpu_buffer->lock); -if (!cpu_buffer->free_page) { - cpu_buffer->free_page = dpage; +if (!cpu_buffer->free_page.data) { + cpu_buffer->free_page = *data_page; dpage =NULL ; }
@@ -7436,7 +7436,7 @@ int ring_buffer_subbuf_order_set(struct }for_each_buffer_cpu (buffer, cpu) { -struct buffer_data_page *old_free_data_page; +struct buffer_data_read_page old_free_data_page;struct list_head old_pages;unsigned long flags;
@@ -7479,7 +7479,7 @@ int ring_buffer_subbuf_order_set(structarch_spin_lock (&cpu_buffer->lock); old_free_data_page = cpu_buffer->free_page; - cpu_buffer->free_page =NULL ; + cpu_buffer->free_page.data =NULL ;arch_spin_unlock (&cpu_buffer->lock);rb_head_page_activate (cpu_buffer);
@@ -7491,7 +7491,7 @@ int ring_buffer_subbuf_order_set(structlist_del_init (&bpage->list);free_buffer_page (bpage); } -free_pages ((unsigned long )old_free_data_page, old_order); +free_pages ((unsigned long )old_free_data_page.data, old_free_data_page.order);rb_check_pages (cpu_buffer); }
Greg Kroah-Hartman
maintainerPATCH 456/713 ring-buffer: Stop remote reader update when page swap fails
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivan Immanuel Shaji <[email protected]>
commit 5eab74874d11160725c42ab676ba97a797a362eb upstream.
The remote swap_reader_page callback can return -EBUSY when the writer moves the head before the remote catches it, particularly during an event storm on a small buffer. __rb_get_reader_page_from_remote() currently warns about that failure but continues with the unchanged reader ID and rearranges the local page list as though the swap succeeded.
Handle the callback failure as a recoverable error. Report it with pr_warn_ratelimited() and return NULL. Callers already handle a NULL reader page as a failed attempt. This avoids splicing the same page as both the previous and new reader without flooding the log under contention.
--- kernel/trace/ring_buffer.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
kernel/trace/ring_buffer.c +5 -2
@@ -5791,8 +5791,11 @@ __rb_get_reader_page_from_remote(struct prev_reader = cpu_buffer->subbuf_ids[cpu_buffer->meta_page->reader.id]; -WARN_ON_ONCE (cpu_buffer->remote->swap_reader_page (cpu_buffer->cpu, - cpu_buffer->remote->priv)); +if (cpu_buffer->remote->swap_reader_page (cpu_buffer->cpu, + cpu_buffer->remote->priv)) { +pr_warn_ratelimited ("Remote reader page swap failed\n" ); +return NULL ; + }/* nr_pages doesn't include the reader page */ if (WARN_ON_ONCE (cpu_buffer->meta_page->reader.id > cpu_buffer->nr_pages))return NULL ;
Greg Kroah-Hartman
maintainerPATCH 457/713 PM: sleep: Unblock runtime PM when device prepare fails
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shibo Zhu <[email protected]>
commit cb258d651d747a7f7063d40f145418bee562ceaf upstream.
device_prepare() blocks runtime PM for a device with runtime PM disabled before invoking its system-sleep ->prepare() callback. For a device that has never enabled runtime PM, this changes dev->power.last_status from RPM_INVALID to RPM_BLOCKED.
If the callback returns an error, dpm_prepare() does not move the device to dpm_prepared_list. Consequently, the recovery path through dpm_complete() never calls device_complete() for the failing device.
The error path drops the runtime PM usage reference, but does not clear RPM_BLOCKED. A later legitimate pm_runtime_enable() then reports:
Attempt to enable runtime PM when it is blocked
before clearing the stale state.
Call pm_runtime_unblock() on the prepare error path before dropping the runtime PM reference, matching the cleanup performed by device_complete().
The issue was reproduced with a platform test device whose ->prepare() callback returns -EIO while runtime PM has never been enabled. Before the fix, last_status remained RPM_BLOCKED after the failed suspend and the first pm_runtime_enable() produced the warning above. With the fix, last_status is restored to RPM_INVALID and the warning is absent.
--- drivers/base/power/main.c | 1 + 1 file changed, 1 insertion(+)
drivers/base/power/main.c +1 -0
@@ -2234,6 +2234,7 @@ unlock:if (ret <0 ) {suspend_report_result (dev, callback, ret); +pm_runtime_unblock (dev);pm_runtime_put (dev);return ret; }
Greg Kroah-Hartman
maintainerPATCH 458/713 orangefs: fix double-free of trailer_buf on readdir copy failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yifei Gao <[email protected]>
commit f574296be7f46eb60beca851240b526df232f480 upstream.
On a readdir downcall, orangefs_devreq_write_iter() frees op->downcall.trailer_buf with vfree() when copy_from_iter_full() fails, but does not clear the pointer before goto Efault. The waiter in do_readdir() is then woken with a negative status and frees the same pointer again on its r < 0 path, causing a deterministic double-free. A client holding /dev/pvfs2-req triggers it by sending a readdir downcall whose declared trailer_size exceeds the bytes it supplies.
Clear the pointer after freeing so the readdir-side vfree() becomes a no-op.
--- fs/orangefs/devorangefs-req.c | 1 + 1 file changed, 1 insertion(+)
fs/orangefs/devorangefs-req.c +1 -0
@@ -474,6 +474,7 @@ static ssize_t orangefs_devreq_write_ite op->downcall.trailer_size, iter)) {gossip_err ("%s: failed to copy trailer.\n" , __func__);vfree (op->downcall.trailer_buf); + op->downcall.trailer_buf =NULL ; goto Efault; }
Greg Kroah-Hartman
maintainerPATCH 459/713 orangefs: skip leading spaces before parsing client debug masks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit d410cd5303ec59c7cf23dd61423752ce8e9ecb59 upstream.
orangefs_prepare_cdm_array() sizes each client debug keyword buffer with strcspn(cds_head, " "), but then parses the keyword with %s. The %s conversion skips leading whitespace, while strcspn() does not.
If a client debug entry starts with a space, the allocation can be sized for an empty keyword while sscanf() copies the following non-empty token. This can write past the end of the allocated keyword buffer.
Skip leading spaces before computing the keyword length so the allocation matches the string parsed by sscanf().
--- fs/orangefs/orangefs-debugfs.c | 1 + 1 file changed, 1 insertion(+)
fs/orangefs/orangefs-debugfs.c +1 -0
@@ -569,6 +569,7 @@ static int orangefs_prepare_cdm_array(ch cds_delimiter =strchr (cds_head,'\n' ); *cds_delimiter ='\0' ; + cds_head =skip_spaces (cds_head); keyword_len =strcspn (cds_head," " ); cdm_array[i].keyword =kzalloc (keyword_len +1 ,GFP_KERNEL );
Greg Kroah-Hartman
maintainerPATCH 460/713 ocfs2: always run deallocs on copy-on-write completion
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Antipov <[email protected]>
commit 82ea9d4fc05fb7a387db547c6a7c0aa6a3719616 upstream.
Local fuzzing of 6.12.94 has found the following memory leak caused by doing 'copy_file_range()' within the same filesystem:
unreferenced object 0xffff88812192c980 (size 32):
comm "syz.0.49", pid 12095, jiffies 4294964143 hex dump (first 32 bytes):
00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
c0 c5 92 21 81 88 ff ff 00 02 00 00 00 06 00 00 ...!............
backtrace (crc 7068d63f):
kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline]
slab_post_alloc_hook mm/slub.c:4152 [inline]
slab_alloc_node mm/slub.c:4197 [inline]
__kmalloc_cache_noprof+0x168/0x2c0 mm/slub.c:4358
kmalloc_noprof include/linux/slab.h:878 [inline]
ocfs2_find_per_slot_free_list fs/ocfs2/alloc.c:6618 [inline]
ocfs2_cache_block_dealloc+0x155/0x4b0 fs/ocfs2/alloc.c:6786
ocfs2_cache_extent_block_free fs/ocfs2/alloc.c:6819 [inline]
ocfs2_unlink_path+0x286/0x450 fs/ocfs2/alloc.c:2613
ocfs2_rotate_subtree_left fs/ocfs2/alloc.c:2779 [inline]
__ocfs2_rotate_tree_left+0x1f6f/0x2da0 fs/ocfs2/alloc.c:2985
ocfs2_rotate_tree_left+0x283/0xe00 fs/ocfs2/alloc.c:3237
ocfs2_try_to_merge_extent+0xf56/0x1a20 fs/ocfs2/alloc.c:3825
ocfs2_split_extent+0x15f4/0x2940 fs/ocfs2/alloc.c:5138
ocfs2_clear_ext_refcount+0x2f6/0x550 fs/ocfs2/refcounttree.c:3098
ocfs2_replace_clusters fs/ocfs2/refcounttree.c:3131 [inline]
ocfs2_make_clusters_writable fs/ocfs2/refcounttree.c:3255 [inline]
ocfs2_replace_cow+0x991/0x1660 fs/ocfs2/refcounttree.c:3349
ocfs2_refcount_cow_hunk fs/ocfs2/refcounttree.c:3427 [inline]
ocfs2_refcount_cow+0x5e1/0x9f0 fs/ocfs2/refcounttree.c:3470
ocfs2_prepare_inode_for_write fs/ocfs2/file.c:2340 [inline]
ocfs2_file_write_iter+0xbda/0x1880 fs/ocfs2/file.c:2451
iter_file_splice_write+0x890/0xf60 fs/splice.c:743
do_splice_from fs/splice.c:944 [inline]
direct_splice_actor+0x232/0x480 fs/splice.c:1167
splice_direct_to_actor+0x4b4/0xb60 fs/splice.c:1111
do_splice_direct_actor fs/splice.c:1210 [inline]
do_splice_direct+0x10f/0x1c0 fs/splice.c:1236
do_sendfile+0x430/0xbf0 fs/read_write.c:1388unreferenced object 0xffff88812192c5c0 (size 32):
comm "syz.0.49", pid 12095, jiffies 4294964143 hex dump (first 32 bytes):
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
29 70 00 00 00 00 00 00 19 00 00 00 00 00 00 00 )p..............
backtrace (crc afec850f):
kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline]
slab_post_alloc_hook mm/slub.c:4152 [inline]
slab_alloc_node mm/slub.c:4197 [inline]
__kmalloc_cache_noprof+0x168/0x2c0 mm/slub.c:4358
kmalloc_noprof include/linux/slab.h:878 [inline]
kzalloc_noprof include/linux/slab.h:1014 [inline]
ocfs2_cache_block_dealloc+0x25c/0x4b0 fs/ocfs2/alloc.c:6793
ocfs2_cache_extent_block_free fs/ocfs2/alloc.c:6819 [inline]
ocfs2_unlink_path+0x286/0x450 fs/ocfs2/alloc.c:2613
ocfs2_rotate_subtree_left fs/ocfs2/alloc.c:2779 [inline]
__ocfs2_rotate_tree_left+0x1f6f/0x2da0 fs/ocfs2/alloc.c:2985
ocfs2_rotate_tree_left+0x283/0xe00 fs/ocfs2/alloc.c:3237
ocfs2_try_to_merge_extent+0xf56/0x1a20 fs/ocfs2/alloc.c:3825
ocfs2_split_extent+0x15f4/0x2940 fs/ocfs2/alloc.c:5138
ocfs2_clear_ext_refcount+0x2f6/0x550 fs/ocfs2/refcounttree.c:3098
ocfs2_replace_clusters fs/ocfs2/refcounttree.c:3131 [inline]
ocfs2_make_clusters_writable fs/ocfs2/refcounttree.c:3255 [inline]
ocfs2_replace_cow+0x991/0x1660 fs/ocfs2/refcounttree.c:3349
ocfs2_refcount_cow_hunk fs/ocfs2/refcounttree.c:3427 [inline]
ocfs2_refcount_cow+0x5e1/0x9f0 fs/ocfs2/refcounttree.c:3470
ocfs2_prepare_inode_for_write fs/ocfs2/file.c:2340 [inline]
ocfs2_file_write_iter+0xbda/0x1880 fs/ocfs2/file.c:2451
iter_file_splice_write+0x890/0xf60 fs/splice.c:743
do_splice_from fs/splice.c:944 [inline]
direct_splice_actor+0x232/0x480 fs/splice.c:1167
splice_direct_to_actor+0x4b4/0xb60 fs/splice.c:1111
do_splice_direct_actor fs/splice.c:1210 [inline]
do_splice_direct+0x10f/0x1c0 fs/splice.c:1236
do_sendfile+0x430/0xbf0 fs/read_write.c:1388This happens when 'ocfs2_cache_block_dealloc()' called from 'ocfs2_cache_extent_block_free()' uses the suballocator to schedule extent removal, so 'ocfs2_run_deallocs()' should be run unconditionally to complete the removal with 'ocfs2_free_cached_blocks()'. An extra semi-automated static analysis [1] suspects that the same scenario looks possible in 'ocfs2_attach_refcount_tree()' and 'ocfs2_reflink_remap_blocks()' as well, but, since 'ocfs2_run_deallocs()' is a safe no-op for an empty dealloc context, 'ocfs2_create_reflink_node()' and 'ocfs2_reflink_xattrs()' may be adjusted in the same way too, thus keeping the code pattern consistent.
--- fs/ocfs2/refcounttree.c | 20 ++++++++------------ fs/ocfs2/xattr.c | 5 ++--- 2 files changed, 10 insertions(+), 15 deletions(-)
fs/ocfs2/refcounttree.c +8 -12
@@ -3360,10 +3360,9 @@ static int ocfs2_replace_cow(struct ocfs cow_start += num_clusters; } -if (ocfs2_dealloc_has_cluster (&context->dealloc)) { +if (ocfs2_dealloc_has_cluster (&context->dealloc))ocfs2_schedule_truncate_log_flush (osb,1 ); -ocfs2_run_deallocs (osb, &context->dealloc); - } +ocfs2_run_deallocs (osb, &context->dealloc);return ret; }
@@ -3846,10 +3845,9 @@ unlock:ocfs2_unlock_refcount_tree (osb, ref_tree,1 );brelse (ref_root_bh); -if (!ret &&ocfs2_dealloc_has_cluster (&dealloc)) { +if (!ret &&ocfs2_dealloc_has_cluster (&dealloc))ocfs2_schedule_truncate_log_flush (osb,1 ); -ocfs2_run_deallocs (osb, &dealloc); - } +ocfs2_run_deallocs (osb, &dealloc); out:/* * Empty the extent map so that we may get the right extent
@@ -4135,10 +4133,9 @@ out_unlock_refcount:ocfs2_unlock_refcount_tree (osb, ref_tree,1 );brelse (ref_root_bh); out: -if (ocfs2_dealloc_has_cluster (&dealloc)) { +if (ocfs2_dealloc_has_cluster (&dealloc))ocfs2_schedule_truncate_log_flush (osb,1 ); -ocfs2_run_deallocs (osb, &dealloc); - } +ocfs2_run_deallocs (osb, &dealloc);return ret; }
@@ -4691,10 +4688,9 @@ loff_t ocfs2_reflink_remap_blocks(struct } out: -if (ocfs2_dealloc_has_cluster (&dealloc)) { +if (ocfs2_dealloc_has_cluster (&dealloc))ocfs2_schedule_truncate_log_flush (osb,1 ); -ocfs2_run_deallocs (osb, &dealloc); - } +ocfs2_run_deallocs (osb, &dealloc);return ret; }
fs/ocfs2/xattr.c +2 -3
@@ -7241,10 +7241,9 @@ out_unlock: ref_tree, 1);brelse (ref_root_bh); -if (ocfs2_dealloc_has_cluster (&dealloc)) { +if (ocfs2_dealloc_has_cluster (&dealloc))ocfs2_schedule_truncate_log_flush (OCFS2_SB (old_inode->i_sb),1 ); -ocfs2_run_deallocs (OCFS2_SB (old_inode->i_sb), &dealloc); - } +ocfs2_run_deallocs (OCFS2_SB (old_inode->i_sb), &dealloc); out:return ret;
Greg Kroah-Hartman
maintainerPATCH 461/713 ocfs2: bound namelen in dlm_migrate_request_handler
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit ea5b5609305a8437bc955a0834a530c12246d78f upstream.
Patch series "ocfs2/dlm: bound peer-controlled lengths in the o2dlm".
The o2dlm receive handlers trust u8 length and count fields from the wire without bounding them, so a node in a DLM domain can corrupt or panic any other node with a malformed message. Three defects:
- dlm_migrate_request_handler() passes migrate->namelen unchecked to dlm_init_mle(), which memcpy()s it into the 32-byte mname[] of an o2dlm_mle slab object: a heap out-of-bounds write of up to ~215 attacker-controlled bytes.
- dlm_mig_lockres_handler() passes mres->lockname_len unchecked to dlm_init_lockres(), which memcpy()s it into the 32-byte o2dlm_lockname slab object: a heap out-of-bounds write of up to ~223 bytes.
- the same handler trusts mres->num_locks without checking that the message is large enough to hold that many entries, so dlm_process_recovery_data() walks mres->ml[] past the kmalloc(data_len) copy and trips a BUG_ON (an out-of-bounds read ending in a panic).
The other o2dlm receive handlers already reject an oversized name; the migration and recovery handlers have omitted it since the DLM was added (see the Fixes tags). Patch 1 bounds namelen; patch 2 validates lockname_len, num_locks, and the payload size. Conforming recovery and migration traffic is unaffected.
o2net authenticates peers only by the DLM domain key, so any node that has joined the domain -- including a compromised or malicious member -- can send these messages. There is no local trigger; the attacker must already be a member of the cluster.
Each sink was confirmed under KASAN with an out-of-tree module mirroring it exactly -- a kmem_cache/kmalloc of the real destination size, then the same unclamped memcpy/loop: slab-out-of-bounds Write for the two writes, Read for the recovery walk, and a panic. A userspace AddressSanitizer build faults identically under -m32 and -m64. Scrubbed logs are available on request.
I reported this privately to [email protected] and the ocfs2 maintainers on 2026-06-20; with no response after the standard embargo period I am posting the fix publicly. I have no embargo requirement.
This patch (of 2):
A node receiving a DLM_MIGRATE_REQUEST message trusts the peer-supplied name length (migrate->namelen) without bounding it. dlm_init_mle() then copies that many bytes into the fixed DLM_LOCKID_NAME_MAX-byte mname[] array of an o2dlm_mle slab object, so a malformed message from a cluster peer overflows the slab object by up to ~215 bytes: a heap out-of-bounds write of attacker-controlled data, reachable by any node in the domain.
Reject an oversized name, the way dlm_master_request_handler() and the other o2dlm receive handlers already do; the migration handler omits the check entirely. Conforming messages are unaffected.
--- fs/ocfs2/dlm/dlmmaster.c | 6 ++++++ 1 file changed, 6 insertions(+)
fs/ocfs2/dlm/dlmmaster.c +6 -0
@@ -3099,6 +3099,12 @@ int dlm_migrate_request_handler(struct o name = migrate->name; namelen = migrate->namelen; +if (namelen >DLM_LOCKID_NAME_MAX ) { +mlog (ML_ERROR ,"%s: invalid name length %u in migrate request\n" , + dlm->name, namelen); + ret = -EINVAL ; + goto leave; + } hash =dlm_lockid_hash (name, namelen);/* preallocate.. if this fails, abort */
Greg Kroah-Hartman
maintainerPATCH 462/713 ocfs2: validate lengths in dlm_mig_lockres_handler
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit b54e03d9b3697d25f4a0063cf717d459c5e3ad94 upstream.
A node receiving a DLM_MIG_LOCKRES message trusts several fields of the peer-supplied dlm_migratable_lockres without validation. num_locks and lockname_len are bounded only on the sending side, and the message is never checked to actually carry num_locks migratable_lock entries. As a result dlm_process_recovery_data() walks mres->ml[0..num_locks) past the kmalloc(data_len) copy of the message (an out-of-bounds read that ends in a BUG_ON panic), and dlm_init_lockres() copies lockname_len bytes into the fixed 32-byte o2dlm_lockname slab object (a heap out-of-bounds write). Both are reachable by any node in the domain.
Validate these fields right after dlm_grab(), before anything uses them -- including the not-joined error path, which already prints mres->lockname with the unbounded lockname_len as a %.*s precision. Reject the message unless lockname_len <= DLM_LOCKID_NAME_MAX, num_locks <= DLM_MAX_MIGRATABLE_LOCKS (the bound the sender already asserts), and the payload is large enough to hold the claimed locks. Conforming recovery and migration messages are unaffected.
--- fs/ocfs2/dlm/dlmrecovery.c | 9 +++++++++ 1 file changed, 9 insertions(+)
fs/ocfs2/dlm/dlmrecovery.c +9 -0
@@ -1357,6 +1357,15 @@ int dlm_mig_lockres_handler(struct o2netif (!dlm_grab (dlm))return -EINVAL ; +if (mres->lockname_len >DLM_LOCKID_NAME_MAX || + mres->num_locks >DLM_MAX_MIGRATABLE_LOCKS || +be16_to_cpu (msg->data_len) <struct_size (mres, ml, mres->num_locks)) { +mlog (ML_ERROR ,"%s: invalid lockres migration message from %u\n" , + dlm->name, mres->master); +dlm_put (dlm); +return -EINVAL ; + } +if (!dlm_joined (dlm)) { mlog(ML_ERROR, "Domain %s not joined! " "lockres %.*s, master %u\n",
Greg Kroah-Hartman
maintainerPATCH 463/713 ocfs2: validate rl_used against rl_count in refcount block validator
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ibrahim Hashimov <[email protected]>
commit 4ca62df6bc0708947b48da3f6a712ecb8e73929c upstream.
ocfs2_find_refcount_rec_in_rl() walks the on-disk refcount record array with:
for (; i < le16_to_cpu(rb->rf_records.rl_used); i++) {
rec = &rb->rf_records.rl_recs[i];
...rl_recs[] lives in a single metadata block (4096 bytes on the common configuration), so its real capacity is fixed by ocfs2_refcount_recs_per_rb(sb) (247 records for a 4K block with the 16-byte ocfs2_refcount_rec). rl_used and rl_count are both read directly off disk by ocfs2_validate_refcount_block() and are never checked against that capacity, nor against each other, before any refcount/reflink/CoW operation walks the array.
A crafted (or corrupted) refcount block with rl_used == 0xffff makes the loop above walk far past the end of the block, dereferencing rl_recs[i] for i up to 65534. The resulting index is then handed to the sibling ocfs2_insert_refcount_rec(), whose insert-shift does:
if (index < le16_to_cpu(rf_list->rl_used)) memmove(&rf_list->rl_recs[index + 1], &rf_list->rl_recs[index], (le16_to_cpu(rf_list->rl_used) - index) * sizeof(struct ocfs2_refcount_rec));
i.e. a memmove() of up to (0xffff - index) * 16 bytes (~1 MiB) from an offset already past the block. This is reachable from an ordinary reflink (FICLONE) against a crafted/corrupted ocfs2 image: attaching an extent whose cpos sorts past every real record in the leaf forces the lookup to run off the end instead of returning early on a match. The attacker model is local: CAP_SYS_ADMIN mounting a crafted or corrupted ocfs2 image, or a raw write to the block device backing an already-mounted ocfs2 filesystem.
ocfs2_validate_refcount_block() already validates the block's ECC, signature, rf_blkno and rf_fs_generation, but never rl_count/rl_used against the block's actual on-disk capacity. This is the same class of gap that ocfs2_validate_extent_block() (fs/ocfs2/alloc.c) already closes for the sibling extent-list header, which checks both the record capacity and the "used" bound before any code walks h_list.l_recs[]:
if (le16_to_cpu(eb->h_list.l_count) != ocfs2_extent_recs_per_eb(sb)) {
rc = ocfs2_error(...);
goto bail;
}
if (le16_to_cpu(eb->h_list.l_next_free_rec) >
le16_to_cpu(eb->h_list.l_count)) {
rc = ocfs2_error(...);
goto bail;
}Add the equivalent pair of checks to ocfs2_validate_refcount_block():
reject a refcount block whose rl_count does not match the fixed per-block capacity returned by ocfs2_refcount_recs_per_rb(), and reject rl_used > rl_count. Both checks are skipped when OCFS2_REFCOUNT_TREE_FL is set, because in that case the same union bytes hold an ocfs2_extent_list (rf_list), not the refcount record list (rf_records) -- that layout is already validated separately by ocfs2_validate_extent_block() when the referenced extent block is read. This mirrors the existing "!(rb->rf_flags & OCFS2_REFCOUNT_TREE_FL)" guard used elsewhere in this file (e.g. ocfs2_get_refcount_rec()) to decide whether rf_records or rf_list is the live member of the union.
With this in place, a forged rl_used/rl_count is caught at block validation time (ocfs2_error()), consistent with every other corruption check in this function, instead of driving an out-of-bounds read in ocfs2_find_refcount_rec_in_rl() and a subsequent out-of-bounds memmove() in ocfs2_insert_refcount_rec().
Verified against a crafted image on a v6.19 KASAN (KASAN_GENERIC) build:
replaying the same reflink (FICLONE) reliably hit a KASAN report in __ocfs2_increase_refcount()/ocfs2_insert_refcount_rec() before this patch, and triggers no report once ocfs2_validate_refcount_block() rejects the forged rl_used/rl_count.
--- fs/ocfs2/refcounttree.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
fs/ocfs2/refcounttree.c +27 -0
@@ -116,6 +116,33 @@ static int ocfs2_validate_refcount_block le32_to_cpu(rb->rf_fs_generation)); goto out; } + +/* +* rf_records (rl_count/rl_used/rl_recs[]) is only meaningful when +* this block is not an interior tree block (OCFS2_REFCOUNT_TREE_FL); +* in that case the same union bytes hold an extent list (rf_list) +* instead, which is validated by ocfs2_validate_extent_block(). +*/ +if (!(le32_to_cpu (rb->rf_flags) &OCFS2_REFCOUNT_TREE_FL )) { +if (le16_to_cpu (rb->rf_records.rl_count) != +ocfs2_refcount_recs_per_rb (sb)) { + rc =ocfs2_error (sb, +"Refcount block #%llu has an invalid rl_count of %u\n" , + (unsigned long long )bh->b_blocknr, +le16_to_cpu (rb->rf_records.rl_count)); + goto out; + } + +if (le16_to_cpu (rb->rf_records.rl_used) > +le16_to_cpu (rb->rf_records.rl_count)) { + rc =ocfs2_error (sb, +"Refcount block #%llu has an invalid rl_used of %u (rl_count %u)\n" , + (unsigned long long )bh->b_blocknr, +le16_to_cpu (rb->rf_records.rl_used), +le16_to_cpu (rb->rf_records.rl_count)); + goto out; + } + } out:return rc; }
Greg Kroah-Hartman
maintainerPATCH 464/713 ocfs2: validate directory-index entry counts when reading metadata
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit bc70726ddad53c7e9a9a85915bf2415b0d4f42f9 upstream.
ocfs2_validate_dx_leaf() and ocfs2_validate_dx_root() check the ECC and signature of an indexed-directory block before it reaches higher-level callers, but neither validator bounds the ocfs2_dx_entry_list counts against the capacity of the block that holds them.
ocfs2_dx_dir_search() then walks
for (i = 0; i < le16_to_cpu(entry_list->de_num_used); i++) dx_entry = &entry_list->de_entries[i];
over de_num_used entries with no bounds check. entry_list is either dx_leaf->dl_list (from ocfs2_read_dx_leaf) or, for an inline root, dx_root->dr_entries. A crafted on-disk image can set de_num_used (and de_count, which is the __counted_by_le() bound of de_entries) to 0xffff and make the walk read far past the end of the 4KB metadata block, giving a slab out-of-bounds read reachable from any path lookup, stat() or open() on an indexed directory once the image is mounted.
Commit 775c17386a6f ("ocfs2: validate dx_root extent list fields during block read") already bounds dr_list for the non-inline dx_root, but left the inline dr_entries path and the dx_leaf dl_list unchecked. Add the same read-time validation for both entry lists: de_count must equal the capacity of the block (ocfs2_dx_entries_per_leaf()/per_root()) and de_num_used must not exceed de_count, rejecting corrupted metadata with -EFSCORRUPTED before ocfs2_dx_dir_search() can walk an out-of-range entry array.
de_count is always written as exactly the block capacity when a leaf or inline root is formatted, so the equality check does not reject any valid image.
Found by 0sec automated security-research tooling (https://0sec.ai).
--- fs/ocfs2/dir.c | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-)
fs/ocfs2/dir.c +41 -4
@@ -625,6 +625,28 @@ static int ocfs2_validate_dx_root(struct le16_to_cpu(el->l_count)); goto bail; } + }else { +struct ocfs2_dx_entry_list *dl_list = &dx_root->dr_entries; + +if (le16_to_cpu (dl_list->de_count) != +ocfs2_dx_entries_per_root (sb)) { + ret =ocfs2_error (sb, +"Dir Index Root # %llu has invalid de_count %u (expected %u)\n" , + (unsigned long long )le64_to_cpu (dx_root->dr_blkno), +le16_to_cpu (dl_list->de_count), +ocfs2_dx_entries_per_root (sb)); + goto bail; + } + +if (le16_to_cpu (dl_list->de_num_used) > +le16_to_cpu (dl_list->de_count)) { + ret =ocfs2_error (sb, +"Dir Index Root # %llu has invalid de_num_used %u (de_count %u)\n" , + (unsigned long long )le64_to_cpu (dx_root->dr_blkno), +le16_to_cpu (dl_list->de_num_used), +le16_to_cpu (dl_list->de_count)); + goto bail; + } } bail:
@@ -664,10 +686,25 @@ static int ocfs2_validate_dx_leaf(structreturn ret; } -if (!OCFS2_IS_VALID_DX_LEAF (dx_leaf)) { - ret =ocfs2_error (sb,"Dir Index Leaf has bad signature %.*s\n" , -7 , dx_leaf->dl_signature); - } +if (!OCFS2_IS_VALID_DX_LEAF (dx_leaf)) +return ocfs2_error (sb,"Dir Index Leaf has bad signature %.*s\n" , +7 , dx_leaf->dl_signature); + +if (le16_to_cpu (dx_leaf->dl_list.de_count) != +ocfs2_dx_entries_per_leaf (sb)) +return ocfs2_error (sb, +"Dir Index Leaf # %llu has invalid de_count %u (expected %u)\n" , + (unsigned long long )le64_to_cpu (dx_leaf->dl_blkno), +le16_to_cpu (dx_leaf->dl_list.de_count), +ocfs2_dx_entries_per_leaf (sb)); + +if (le16_to_cpu (dx_leaf->dl_list.de_num_used) > +le16_to_cpu (dx_leaf->dl_list.de_count)) +return ocfs2_error (sb, +"Dir Index Leaf # %llu has invalid de_num_used %u (de_count %u)\n" , + (unsigned long long )le64_to_cpu (dx_leaf->dl_blkno), +le16_to_cpu (dx_leaf->dl_list.de_num_used), +le16_to_cpu (dx_leaf->dl_list.de_count));return ret; }
Greg Kroah-Hartman
maintainerPATCH 465/713 ocfs2: cluster: dont sleep while holding o2hb_live_lock in o2hb_region_pin()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joseph Qi <[email protected]>
commit af09df89db9a68a1d76df0f75667998135bc8d65 upstream.
Patch series "ocfs2: cluster: o2hb_region_pin() fixes", v2.
This series fixes three related issues in o2hb_region_pin(), all are from the original implementation in commit: 58a3158a5d17 ("ocfs2/cluster:
Pin/unpin o2hb regions"):
1) It is called with o2hb_live_lock (a spinlock) held, but the underlying configfs_depend_item() sleeps (takes inode rwsem and pins the filesystem). This triggers BUG under CONFIG_DEBUG_ATOMIC_SLEEP.
2) When called from the configfs drop_item callback, it creates a lock order inversion: parent inode_lock -> configfs root inode_lock, which can deadlock against subsystem unregistration paths taking root -> parent.
3) If pinning fails partway through o2hb_region_inc_user(), the o2hb_dependent_users counter is leaked and partially-pinned regions are never released, leaving heartbeat regions unprotected on subsequent mounts.
Patch 1 reworks o2hb_region_pin() to drop o2hb_live_lock across each sleeping configfs_depend_item() call, using a config_item reference to keep the region alive while unlocked.
Patch 2 adds a from_callback parameter to select configfs_depend_item_unlocked() when called from configfs context, avoiding the inode_lock nesting.
Patch 3 fixes the error path in o2hb_region_inc_user() to unpin and decrement the counter on failure.
This patch (of 3):
o2hb_region_pin() is always called with the o2hb_live_lock spinlock held (from o2hb_region_inc_user() and o2hb_heartbeat_group_drop_item()), but it calls o2nm_depend_item() -> configfs_depend_item(), which sleeps: it pins the configfs filesystem and takes the configfs root inode rwsem. Under CONFIG_DEBUG_ATOMIC_SLEEP this triggers:
BUG: sleeping function called from invalid context at kernel/locking/rwsem.c
in_atomic(): 1, ... name: mount.ocfs2
down_write
configfs_depend_item
o2hb_region_pin
o2hb_region_inc_user
o2hb_register_callback
dlm_register_domain_handlers
...
ocfs2_dlm_init
ocfs2_mount_volume
ocfs2_fill_superRework o2hb_region_pin() to pin one region at a time with the lock dropped across the sleeping call: under o2hb_live_lock find the next eligible region and take a config_item reference to keep it alive, drop the lock, call o2nm_depend_item(), then retake the lock and record the pin. The config_item_put() is done with the lock released as well, since o2hb_region_release() also acquires o2hb_live_lock and can sleep. The region list may change while unlocked, so the scan restarts from the top after each pin. Local heartbeat still pins only the matching region; global heartbeat pins all eligible regions.
The unpin path is unaffected: configfs_undepend_item() only takes a spinlock and does not sleep.
--- fs/ocfs2/cluster/heartbeat.c | 126 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 25 deletions(-)
fs/ocfs2/cluster/heartbeat.c +101 -25
@@ -43,6 +43,14 @@ static DECLARE_RWSEM(o2hb_callback_sem);* whenever any of the threads sees activity from the node in its region. */ static DEFINE_SPINLOCK (o2hb_live_lock); +/* +* Serializes region pin/unpin dependency management (o2hb_dependent_users +* and the o2nm_depend_item()/o2nm_undepend_item() calls). o2hb_region_pin() +* has to drop o2hb_live_lock across the sleeping o2nm_depend_item(), so the +* spinlock alone can no longer keep pin and unpin mutually exclusive; this +* mutex, taken outside o2hb_live_lock, does. +*/ +static DEFINE_MUTEX (o2hb_dependency_mutex);static struct list_head o2hb_live_slots[O2NM_MAX_NODES ];static unsigned long o2hb_live_node_bitmap[BITS_TO_LONGS (O2NM_MAX_NODES )];static LIST_HEAD (o2hb_node_events);
@@ -2142,6 +2150,7 @@ static void o2hb_heartbeat_group_drop_it* If global heartbeat active and there are dependent users, * pin all regions if quorum region count <= CUT_OFF */ +mutex_lock (&o2hb_dependency_mutex);spin_lock (&o2hb_live_lock);if (!o2hb_dependent_users)
@@ -2153,6 +2162,7 @@ static void o2hb_heartbeat_group_drop_it unlock:spin_unlock (&o2hb_live_lock); +mutex_unlock (&o2hb_dependency_mutex); } static ssize_t o2hb_heartbeat_group_dead_threshold_show(struct config_item *item,
@@ -2292,46 +2302,108 @@ EXPORT_SYMBOL_GPL(o2hb_setup_callback);*/ static int o2hb_region_pin (const char *region_uuid) { -int ret =0 , found =0 ; -struct o2hb_region *reg; +int ret =0 , found; +struct o2hb_region *reg, *pinned;char *uuid;assert_spin_locked (&o2hb_live_lock); -list_for_each_entry (reg, &o2hb_all_regions, hr_all_item) { -if (reg->hr_item_dropped) -continue ; +do { + found =0 ; + pinned =NULL ; - uuid =config_item_name (®->hr_item); +list_for_each_entry (reg, &o2hb_all_regions, hr_all_item) { +if (reg->hr_item_dropped) +continue ; -/* local heartbeat */ -if (region_uuid) { -if (strcmp (region_uuid, uuid)) + uuid =config_item_name (®->hr_item); + +/* local heartbeat */ +if (region_uuid) { +if (strcmp (region_uuid, uuid)) +continue ; + found =1 ; + } + +if (reg->hr_item_pinned || reg->hr_item_dropped) { +if (found) +break ;continue ; - found =1 ; + } + +/* +* Found a region that needs pinning. Take a reference +* so it stays alive while we drop the lock below. +*/ + pinned = reg; +config_item_get (®->hr_item); +break ; } -if (reg->hr_item_pinned || reg->hr_item_dropped) - goto skip_pin; +if (!pinned) +break ; + + uuid =config_item_name (&pinned->hr_item); + +/* +* o2nm_depend_item() -> configfs_depend_item() can sleep (it +* takes the configfs root inode rwsem), so it must not run +* under o2hb_live_lock. Drop the lock across it; @pinned is +* kept alive by the reference taken above. The region list may +* change while unlocked, so we rescan from the top afterwards. +*/ +spin_unlock (&o2hb_live_lock);/* Ignore ENOENT only for local hb (userdlm domain) */ - ret =o2nm_depend_item (®->hr_item); + ret =o2nm_depend_item (&pinned->hr_item); + +spin_lock (&o2hb_live_lock);if (!ret) { -mlog (ML_CLUSTER ,"Pin region %s\n" , uuid); - reg->hr_item_pinned =1 ; - }else { -if (ret == -ENOENT && found) - ret =0 ; -else { -mlog (ML_ERROR ,"Pin region %s fails with %d\n" , - uuid, ret); +/* +* o2hb_live_lock was dropped across o2nm_depend_item(). +* o2hb_set_quorum_device() runs in the heartbeat thread +* without o2hb_dependency_mutex, so for global heartbeat +* it may have crossed O2HB_PIN_CUT_OFF and unpinned the +* regions while we slept. If that happened this pin is +* no longer wanted; undo it and stop rather than +* resurrecting it on the rescan below. +*/ +if (!region_uuid && +bitmap_weight (o2hb_quorum_region_bitmap, +O2NM_MAX_REGIONS ) >O2HB_PIN_CUT_OFF ) { +o2nm_undepend_item (&pinned->hr_item); +spin_unlock (&o2hb_live_lock); +config_item_put (&pinned->hr_item); +spin_lock (&o2hb_live_lock);break ; } +mlog (ML_CLUSTER ,"Pin region %s\n" , uuid); + pinned->hr_item_pinned =1 ; + }else if (ret == -ENOENT && (found || !region_uuid)) { +/* +* For local hb (found): ignore ENOENT from userdlm +* domains as before. For global hb (!region_uuid): +* the region may have been detached from configfs +* while the lock was dropped — skip it and continue +* pinning the remaining regions. +*/ + ret =0 ; + }else { +mlog (ML_ERROR ,"Pin region %s fails with %d\n" , + uuid, ret); } -skip_pin: -if (found) -break ; - } + +/* +* config_item_put() may drop the last reference and run +* o2hb_region_release(), which also grabs o2hb_live_lock and +* can sleep, so it must happen with the lock released. +*/ +spin_unlock (&o2hb_live_lock); +config_item_put (&pinned->hr_item); +spin_lock (&o2hb_live_lock); + +/* local hb pins a single matching region */ + }while (!ret && !region_uuid);return ret; }
@@ -2376,6 +2448,7 @@ static int o2hb_region_inc_user(const ch {int ret =0 ; +mutex_lock (&o2hb_dependency_mutex);spin_lock (&o2hb_live_lock);/* local heartbeat */
@@ -2398,11 +2471,13 @@ static int o2hb_region_inc_user(const ch unlock:spin_unlock (&o2hb_live_lock); +mutex_unlock (&o2hb_dependency_mutex);return ret; }static void o2hb_region_dec_user (const char *region_uuid) { +mutex_lock (&o2hb_dependency_mutex);spin_lock (&o2hb_live_lock);/* local heartbeat */
@@ -2421,6 +2496,7 @@ static void o2hb_region_dec_user(const c unlock:spin_unlock (&o2hb_live_lock); +mutex_unlock (&o2hb_dependency_mutex); } int o2hb_register_callback(const char *region_uuid,
Greg Kroah-Hartman
maintainerPATCH 466/713 ocfs2: cluster: avoid lock order inversion in o2hb_region_pin() from drop_item
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joseph Qi <[email protected]>
commit cd789996db3c87427343f54f509d17810bd7ba7c upstream.
o2hb_heartbeat_group_drop_item() is called from configfs rmdir with the parent directory's inode_lock held. It calls o2hb_region_pin() -> o2nm_depend_item() -> configfs_depend_item(), which acquires the configfs root inode_lock. This creates a parent -> root inode_lock nesting that could deadlock against paths taking root -> parent (e.g. subsystem unregistration).
Fix this by using configfs_depend_item_unlocked() when o2hb_region_pin() is called from a configfs callback context. This variant skips the root inode_lock when caller and target are in the same subsystem, which is safe because VFS already holds a lock preventing unregistration.
Add o2nm_depend_item_unlocked() wrapper and a from_callback parameter to o2hb_region_pin() to select the appropriate variant.
--- fs/ocfs2/cluster/heartbeat.c | 17 ++++++++++------- fs/ocfs2/cluster/nodemanager.c | 6 ++++++ fs/ocfs2/cluster/nodemanager.h | 1 + 3 files changed, 17 insertions(+), 7 deletions(-)
fs/ocfs2/cluster/heartbeat.c +10 -7
@@ -146,7 +146,7 @@ static unsigned int o2hb_dependent_users* In global heartbeat mode, we pin/unpin all o2hb regions. This solution * works for both file system and userdlm domains. */ -static int o2hb_region_pin (const char *region_uuid); +static int o2hb_region_pin (const char *region_uuid,bool from_callback);static void o2hb_region_unpin (const char *region_uuid);/* Only sets a new threshold if there are no active regions.
@@ -2158,7 +2158,7 @@ static void o2hb_heartbeat_group_drop_itif (bitmap_weight (o2hb_quorum_region_bitmap,O2NM_MAX_REGIONS ) <=O2HB_PIN_CUT_OFF ) -o2hb_region_pin (NULL ); +o2hb_region_pin (NULL , true); unlock:spin_unlock (&o2hb_live_lock);
@@ -2300,7 +2300,7 @@ EXPORT_SYMBOL_GPL(o2hb_setup_callback);* In local, we only pin the matching region. In global we pin all the active * regions. */ -static int o2hb_region_pin (const char *region_uuid) +static int o2hb_region_pin (const char *region_uuid,bool from_callback) {int ret =0 , found;struct o2hb_region *reg, *pinned;
@@ -2355,7 +2355,10 @@ static int o2hb_region_pin(const char *rspin_unlock (&o2hb_live_lock);/* Ignore ENOENT only for local hb (userdlm domain) */ - ret =o2nm_depend_item (&pinned->hr_item); +if (from_callback) + ret =o2nm_depend_item_unlocked (&pinned->hr_item); +else + ret =o2nm_depend_item (&pinned->hr_item);spin_lock (&o2hb_live_lock);if (!ret) {
@@ -2453,8 +2456,8 @@ static int o2hb_region_inc_user(const ch/* local heartbeat */ if (!o2hb_global_heartbeat_active ()) { - ret =o2hb_region_pin (region_uuid); - goto unlock; + ret =o2hb_region_pin (region_uuid, false); + goto unlock; }/*
@@ -2467,7 +2470,7 @@ static int o2hb_region_inc_user(const chif (bitmap_weight (o2hb_quorum_region_bitmap,O2NM_MAX_REGIONS ) <=O2HB_PIN_CUT_OFF ) - ret =o2hb_region_pin (NULL ); + ret =o2hb_region_pin (NULL , false); unlock:spin_unlock (&o2hb_live_lock);
fs/ocfs2/cluster/nodemanager.c +6 -0
@@ -777,6 +777,12 @@ int o2nm_depend_item(struct config_itemreturn configfs_depend_item (&o2nm_cluster_group.cs_subsys, item); } +int o2nm_depend_item_unlocked (struct config_item *item) +{ +return configfs_depend_item_unlocked (&o2nm_cluster_group.cs_subsys, + item); +} +void o2nm_undepend_item (struct config_item *item) {configfs_undepend_item (item);
fs/ocfs2/cluster/nodemanager.h +1 -0
@@ -64,6 +64,7 @@ void o2nm_node_get(struct o2nm_node *nodvoid o2nm_node_put (struct o2nm_node *node);int o2nm_depend_item (struct config_item *item); +int o2nm_depend_item_unlocked (struct config_item *item);void o2nm_undepend_item (struct config_item *item);int o2nm_depend_this_node (void );void o2nm_undepend_this_node (void );
Greg Kroah-Hartman
maintainerPATCH 467/713 ocfs2: cluster: fix o2hb_dependent_users leak on pin failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joseph Qi <[email protected]>
commit 12c2ab42dbe227956c765e2674364bfca5de0533 upstream.
In o2hb_region_inc_user(), o2hb_dependent_users is incremented unconditionally before calling o2hb_region_pin(). If the pin fails, the counter is never decremented and any partially-pinned regions are never unpinned, since the caller does not call o2hb_region_dec_user() on error.
The leaked counter causes subsequent o2hb_region_inc_user() calls to skip pinning entirely (the > 1 check), leaving heartbeat regions unprotected.
Fix by rolling back on failure: call o2hb_region_unpin(NULL) to release any partially-pinned regions and decrement o2hb_dependent_users to restore the pre-increment state.
--- fs/ocfs2/cluster/heartbeat.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
fs/ocfs2/cluster/heartbeat.c +6 -1
@@ -2469,8 +2469,13 @@ static int o2hb_region_inc_user(const ch goto unlock;if (bitmap_weight (o2hb_quorum_region_bitmap, -O2NM_MAX_REGIONS ) <=O2HB_PIN_CUT_OFF ) +O2NM_MAX_REGIONS ) <=O2HB_PIN_CUT_OFF ) { ret =o2hb_region_pin (NULL , false); +if (ret) { +o2hb_region_unpin (NULL ); + o2hb_dependent_users--; + } + } unlock:spin_unlock (&o2hb_live_lock);
Greg Kroah-Hartman
maintainerPATCH 468/713 ocfs2: fix cached cluster count after suballocator reclaim
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matthias Goergens <[email protected]>
commit 621c2bcb87548ff6fe7ec1116f9b27ed87fbeb48 upstream.
When reclaiming a suballocator block group, first reduce the on-disk cluster count by cl_cpg. The current code then subtracts that new count (fe->i_clusters) from the old cached count (OCFS2_I(alloc_inode)->ip_clusters).
For an allocator with N block groups, that leaves the cache at
N * cl_cpg - (N * cl_cpg - cl_cpg) = cl_cpg
i.e. ip_clusters -= (fe->i_clusters - cl_cpg) leaves ip_clusters equal to cl_cpg regardless of N. This happens to be correct when reclaiming from two block groups, but undercounts the clusters from three block groups onwards. The incorrect cache value is also used immediately to update i_blocks.
Assign the updated on-disk count to the cache, matching the allocation and inode refresh paths.
In a QEMU test using a clean 256 MiB OCFS2 image and a 10,000-file create/delete workload, the first buggy reclaim left the on-disk (fe->i_clusters) and cached (ip_clusters) counts at 2048 and 512 clusters respectively; later reclaims underflowed the cache. With this change, the cache matched the on-disk count across all four reclaims: 2048, 1536, 1024, and 512 clusters.
--- fs/ocfs2/suballoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/ocfs2/suballoc.c +1 -1
@@ -2759,7 +2759,7 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, fe->i_clusters =cpu_to_le32 (tmp_used -le16_to_cpu (cl->cl_cpg));spin_lock (&OCFS2_I (alloc_inode)->ip_lock); -OCFS2_I (alloc_inode)->ip_clusters -=le32_to_cpu (fe->i_clusters); +OCFS2_I (alloc_inode)->ip_clusters =le32_to_cpu (fe->i_clusters); fe->i_size =cpu_to_le64 (ocfs2_clusters_to_bytes (alloc_inode->i_sb,le32_to_cpu (fe->i_clusters)));spin_unlock (&OCFS2_I (alloc_inode)->ip_lock);
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 469/713 ocfs2: fix readdir position truncation on 32-bit kernels
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhan Xusheng <[email protected]>
commit a63308ab426f3a3c7e33b02c150ea59054620261 upstream.
In ocfs2_dir_foreach_blk_el(), the directory cookie position is rebuilt with
ctx->pos = (ctx->pos & ~(sb->s_blocksize - 1)) | offset;
ctx->pos is loff_t (signed 64-bit), while sb->s_blocksize is unsigned long. On 32-bit kernels unsigned long is 32-bit, so the mask
~(sb->s_blocksize - 1)
is computed as a 32-bit unsigned value (e.g. 0xfffff000 for a 4 KiB block size). In the AND expression with the 64-bit ctx->pos, that unsigned operand is zero-extended to 64 bits per the usual arithmetic conversions, yielding 0x00000000fffff000. The high 32 bits of ctx->pos are silently cleared, even though directory size is allowed to exceed 4 GiB.
When readdir() crosses the 4 GiB boundary on a 32-bit kernel the position is reset back into the first 4 GiB block, making the re-validation path re-enumerate already-returned dirents indefinitely.
This is ocfs2_dir_foreach_blk_el(), the extent-list readdir path taken for all non-inline directories, so a directory large enough to cross 4 GiB reaches it.
This is the same class of bug that commit 3dce5bb82c97 ("exfat: Fix bitwise operation having different size") fixed in exfat, and the fix mirrors the equivalent ext4 fix in this series. Cast the operand to loff_t so the mask is 64-bit before the AND:
ctx->pos = (ctx->pos & ~((loff_t)sb->s_blocksize - 1)) | offset;
64-bit kernels are unaffected.
--- fs/ocfs2/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/ocfs2/dir.c +1 -1
@@ -1954,7 +1954,7 @@ static int ocfs2_dir_foreach_blk_el(stru i +=le16_to_cpu (de->rec_len); } offset = i; - ctx->pos = (ctx->pos & ~(sb->s_blocksize -1 )) + ctx->pos = (ctx->pos & ~((loff_t )sb->s_blocksize -1 )) | offset; *f_version =inode_query_iversion (inode); }
Greg Kroah-Hartman
maintainerPATCH 470/713 openrisc: fix arbitrary kernel memory access via or1k_atomic syscall
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ali Ahmet Memis <[email protected]>
commit 78004e9a87f240df03e2f73120d291763c32e0a7 upstream.
sys_or1k_atomic() (syscall 244 in the "or1k" ABI) takes two user pointers, v1 and v2, and swaps the words they point to in hand-written assembly.
l.lwz r29,0(r4)
l.lwz r27,0(r5)
l.sw 0(r4),r27
l.sw 0(r5),r29The pointers are not checked with access_ok(). The four memory accesses also have no exception table entries.
A caller passes a kernel address as either pointer, and the syscall reads from and writes to it directly.
This gives an unprivileged process a kernel read/write primitive. It overwrites kernel data such as the sys_call_table, gaining code execution in kernel context.
Check both pointers before entering the critical section. Add fixups for the four memory accesses so faults on valid but unmapped user addresses return -EFAULT.
--- arch/openrisc/kernel/entry.S | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-)
arch/openrisc/kernel/entry.S +39 -4
@@ -1223,15 +1223,50 @@ _no_syscall_trace: * */ +/* Keep this literal; hi()/lo() can't use the UL-suffixed TASK_SIZE. */ +#define OR1K_ATOMIC_ADDR_LIMIT 0x7ffffffc + ENTRY(sys_or1k_atomic) /* FIXME: This ignores r3 and always does an XCHG */ + + /* Check both user pointers before accessing them. */ + l.movhi r13,hi(OR1K_ATOMIC_ADDR_LIMIT) + l.ori r13,r13,lo(OR1K_ATOMIC_ADDR_LIMIT) + l.sfgtu r4,r13 + l.bf 9f + l.nop + l.sfgtu r5,r13 + l.bf 9f + l.nop + DISABLE_INTERRUPTS(r17,r19) - l.lwz r29,0(r4) - l.lwz r27,0(r5) - l.sw 0(r4),r27 - l.sw 0(r5),r29 +10: l.lwz r29,0(r4) +11: l.lwz r27,0(r5) +12: l.sw 0(r4),r27 +13: l.sw 0(r5),r29 ENABLE_INTERRUPTS(r17) l.jr r9 l.or r11,r0,r0 + /* + * Either pointer was outside user space, or turned out to be + * unmapped/inaccessible when we actually touched it. + */ +9: l.jr r9 + l.addi r11,r0,-EFAULT + + .section .fixup, "ax" +14: + ENABLE_INTERRUPTS(r17) + l.j 9b + l.nop + .previous + + .section __ex_table, "a" + .long 10b, 14b + .long 11b, 14b + .long 12b, 14b + .long 13b, 14b + .previous + /* ============================================================[ EOF ]=== */
Greg Kroah-Hartman
maintainerPATCH 471/713 openvswitch: Fix CT limit teardown use-after-free
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuqi Xu <[email protected]>
commit 403f96c32c9e24600093d7d0c61c17daeedca957 upstream.
Packet processing uses CT limit state under RCU, while netns teardown frees that state under ovs_mutex. The CT limit pointer was neither removed from readers nor protected by a grace period, allowing packet processing to dereference the freed state.
An unprivileged user can trigger this bug from a user and network namespace, causing a slab-use-after-free in ovs_ct_execute() when the netns is torn down.
Publish the CT limit pointer through RCU, remove it before teardown, and wait for readers before freeing its contents. Keep ovs_mutex around individual CT limit updates, and use the RCU read-side lock while GET traverses the RCU-protected limit lists.
Netns teardown detaches the RCU-protected CT limit state in the pernet .pre_exit callback while holding ovs_mutex. The pernet core guarantees an RCU grace period between the .pre_exit and .exit callbacks, so the .exit callback completes the teardown without adding any extra synchronization.
The netlink command handlers do not need NULL checks because the userspace netlink socket holds an active reference to its network namespace while a request is processed. The per-netns exit path therefore cannot run concurrently with SET, DEL, or GET for that socket's namespace.
--- net/openvswitch/conntrack.c | 120 ++++++++++++++++++++++++++++---------------- net/openvswitch/conntrack.h | 6 +- net/openvswitch/datapath.c | 10 +++ net/openvswitch/datapath.h | 8 ++ 4 files changed, 98 insertions(+), 46 deletions(-)
net/openvswitch/conntrack.c +79 -41
@@ -933,10 +933,14 @@ static int ovs_ct_check_limit(struct net const struct ovs_conntrack_info *info) {struct ovs_net *ovs_net =net_generic (net, ovs_net_id); -const struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info; +const struct ovs_ct_limit_info *ct_limit_info;u32 per_zone_limit, connections;u32 conncount_key; + ct_limit_info =rcu_dereference (ovs_net->ct_limit_info); +if (!ct_limit_info) +return 0 ; + conncount_key = info->zone.id; per_zone_limit =ct_limit_get (ct_limit_info, info->zone.id);
@@ -1585,40 +1589,55 @@ static void __ovs_ct_free_action(struct#if IS_ENABLED (CONFIG_NETFILTER_CONNCOUNT )static int ovs_ct_limit_init (struct net *net,struct ovs_net *ovs_net) { +struct ovs_ct_limit_info *info;int i, err; - ovs_net->ct_limit_info =kmalloc_obj (*ovs_net->ct_limit_info); -if (!ovs_net->ct_limit_info) + info =kmalloc_obj (*info); +if (!info)return -ENOMEM ; - ovs_net->ct_limit_info->default_limit =OVS_CT_LIMIT_DEFAULT ; - ovs_net->ct_limit_info->limits = + info->default_limit =OVS_CT_LIMIT_DEFAULT ; + info->limits = kmalloc_objs(struct hlist_head, CT_LIMIT_HASH_BUCKETS); -if (!ovs_net->ct_limit_info->limits) { -kfree (ovs_net->ct_limit_info); +if (!info->limits) { +kfree (info);return -ENOMEM ; }for (i =0 ; i <CT_LIMIT_HASH_BUCKETS ; i++) -INIT_HLIST_HEAD (&ovs_net->ct_limit_info->limits[i]); +INIT_HLIST_HEAD (&info->limits[i]); - ovs_net->ct_limit_info->data =nf_conncount_init (net,sizeof (u32)); + info->data =nf_conncount_init (net,sizeof (u32)); -if (IS_ERR (ovs_net->ct_limit_info->data)) { - err =PTR_ERR (ovs_net->ct_limit_info->data); -kfree (ovs_net->ct_limit_info->limits); -kfree (ovs_net->ct_limit_info); +if (IS_ERR (info->data)) { + err =PTR_ERR (info->data); +kfree (info->limits); +kfree (info);pr_err ("openvswitch: failed to init nf_conncount %d\n" , err);return err; } +rcu_assign_pointer (ovs_net->ct_limit_info, info);return 0 ; } -static void ovs_ct_limit_exit (struct net *net,struct ovs_net *ovs_net) +static void *ovs_ct_limit_exit_start (struct ovs_net *ovs_net) +{ +return rcu_replace_pointer (ovs_net->ct_limit_info,NULL , +lockdep_ovsl_is_held ()); +} + +/* The CT limit state must be detached by ovs_ct_limit_exit_start() and an +* RCU grace period must elapse before this function runs. The pernet core +* guarantees the grace period between the .pre_exit and .exit callbacks. +*/ +static void ovs_ct_limit_exit_finish (struct net *net,void *data) { -const struct ovs_ct_limit_info *info = ovs_net->ct_limit_info; +const struct ovs_ct_limit_info *info = data;int i; +if (!info) +return ; +nf_conncount_destroy (net, info->data);for (i =0 ; i <CT_LIMIT_HASH_BUCKETS ; ++i) {struct hlist_head *head = &info->limits[i];
@@ -1626,7 +1645,7 @@ static void ovs_ct_limit_exit(struct netstruct hlist_node *next;hlist_for_each_entry_safe (ct_limit, next, head, hlist_node) -kfree_rcu (ct_limit, rcu); +kfree (ct_limit); }kfree (info->limits);kfree (info);
@@ -1665,12 +1684,13 @@ static bool check_zone_id(int zone_id, ureturn false; } -static int ovs_ct_limit_set_zone_limit (struct nlattr *nla_zone_limit, -struct ovs_ct_limit_info *info) +static int ovs_ct_limit_set_zone_limit (struct ovs_net *ovs_net, +struct nlattr *nla_zone_limit) {struct ovs_zone_limit *zone_limit; -int rem; +struct ovs_ct_limit_info *info;u16 zone; +int rem; rem =NLA_ALIGN (nla_len (nla_zone_limit)); zone_limit = (struct ovs_zone_limit *)nla_data (nla_zone_limit);
@@ -1679,6 +1699,7 @@ static int ovs_ct_limit_set_zone_limit(sif (unlikely (zone_limit->zone_id ==OVS_ZONE_LIMIT_DEFAULT_ZONE )) {ovs_lock (); + info =ovsl_dereference (ovs_net->ct_limit_info); info->default_limit = zone_limit->limit;ovs_unlock (); } else if (unlikely(!check_zone_id(
@@ -1695,6 +1716,7 @@ static int ovs_ct_limit_set_zone_limit(s ct_limit->limit = zone_limit->limit;ovs_lock (); + info =ovsl_dereference (ovs_net->ct_limit_info);ct_limit_set (info, ct_limit);ovs_unlock (); }
@@ -1709,12 +1731,13 @@ static int ovs_ct_limit_set_zone_limit(sreturn 0 ; } -static int ovs_ct_limit_del_zone_limit (struct nlattr *nla_zone_limit, -struct ovs_ct_limit_info *info) +static int ovs_ct_limit_del_zone_limit (struct ovs_net *ovs_net, +struct nlattr *nla_zone_limit) {struct ovs_zone_limit *zone_limit; -int rem; +struct ovs_ct_limit_info *info;u16 zone; +int rem; rem =NLA_ALIGN (nla_len (nla_zone_limit)); zone_limit = (struct ovs_zone_limit *)nla_data (nla_zone_limit);
@@ -1723,6 +1746,7 @@ static int ovs_ct_limit_del_zone_limit(sif (unlikely (zone_limit->zone_id ==OVS_ZONE_LIMIT_DEFAULT_ZONE )) {ovs_lock (); + info =ovsl_dereference (ovs_net->ct_limit_info); info->default_limit =OVS_CT_LIMIT_DEFAULT ;ovs_unlock (); } else if (unlikely(!check_zone_id(
@@ -1730,6 +1754,7 @@ static int ovs_ct_limit_del_zone_limit(sOVS_NLERR (true,"zone id is out of range" ); }else {ovs_lock (); + info =ovsl_dereference (ovs_net->ct_limit_info);ct_limit_del (info, zone);ovs_unlock (); }
@@ -1773,6 +1798,7 @@ static int __ovs_ct_limit_get_zone_limitreturn nla_put_nohdr (reply,sizeof (zone_limit), &zone_limit); } +/* Called with RCU read lock held. */ static int ovs_ct_limit_get_zone_limit(struct net *net, struct nlattr *nla_zone_limit, struct ovs_ct_limit_info *info,
@@ -1796,12 +1822,10 @@ static int ovs_ct_limit_get_zone_limit(s &zone))) { OVS_NLERR(true, "zone id is out of range"); } else { -rcu_read_lock (); limit =ct_limit_get (info, zone); err =__ovs_ct_limit_get_zone_limit ( net, info->data, zone, limit, reply); -rcu_read_unlock ();if (err)return err; }
@@ -1816,6 +1840,7 @@ static int ovs_ct_limit_get_zone_limit(sreturn 0 ; } +/* Called with RCU read lock held. */ static int ovs_ct_limit_get_all_zone_limit (struct net *net,struct ovs_ct_limit_info *info,struct sk_buff *reply)
@@ -1828,19 +1853,16 @@ static int ovs_ct_limit_get_all_zone_limif (err)return err; -rcu_read_lock ();for (i =0 ; i <CT_LIMIT_HASH_BUCKETS ; ++i) { head = &info->limits[i];hlist_for_each_entry_rcu (ct_limit, head, hlist_node) { err =__ovs_ct_limit_get_zone_limit (net, info->data, ct_limit->zone, ct_limit->limit, reply);if (err) - goto exit_err; +return err; } } -exit_err: -rcu_read_unlock ();return err; }
@@ -1850,7 +1872,6 @@ static int ovs_ct_limit_cmd_set(struct sstruct sk_buff *reply;struct ovs_header *ovs_reply_header;struct ovs_net *ovs_net =net_generic (sock_net (skb->sk), ovs_net_id); -struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info;int err; reply = ovs_ct_limit_cmd_reply_start(info, OVS_CT_LIMIT_CMD_SET,
@@ -1863,8 +1884,8 @@ static int ovs_ct_limit_cmd_set(struct s goto exit_err; } - err =ovs_ct_limit_set_zone_limit (a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT ], - ct_limit_info); + err =ovs_ct_limit_set_zone_limit (ovs_net, + a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT ]);if (err) goto exit_err;
@@ -1884,7 +1905,6 @@ static int ovs_ct_limit_cmd_del(struct sstruct sk_buff *reply;struct ovs_header *ovs_reply_header;struct ovs_net *ovs_net =net_generic (sock_net (skb->sk), ovs_net_id); -struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info;int err; reply = ovs_ct_limit_cmd_reply_start(info, OVS_CT_LIMIT_CMD_DEL,
@@ -1897,8 +1917,8 @@ static int ovs_ct_limit_cmd_del(struct s goto exit_err; } - err =ovs_ct_limit_del_zone_limit (a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT ], - ct_limit_info); + err =ovs_ct_limit_del_zone_limit (ovs_net, + a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT ]);if (err) goto exit_err;
@@ -1918,7 +1938,7 @@ static int ovs_ct_limit_cmd_get(struct sstruct ovs_header *ovs_reply_header;struct net *net =sock_net (skb->sk);struct ovs_net *ovs_net =net_generic (net, ovs_net_id); -struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info; +struct ovs_ct_limit_info *ct_limit_info;int err; reply = ovs_ct_limit_cmd_reply_start(info, OVS_CT_LIMIT_CMD_GET,
@@ -1932,18 +1952,19 @@ static int ovs_ct_limit_cmd_get(struct s goto exit_err; } +rcu_read_lock (); + ct_limit_info =rcu_dereference (ovs_net->ct_limit_info);if (a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT ]) { err =ovs_ct_limit_get_zone_limit ( net, a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT ], ct_limit_info, reply); -if (err) - goto exit_err; }else { err =ovs_ct_limit_get_all_zone_limit (net, ct_limit_info, reply); -if (err) - goto exit_err; } +rcu_read_unlock (); +if (err) + goto exit_err;nla_nest_end (reply, nla_reply);genlmsg_end (reply, ovs_reply_header);
@@ -2016,12 +2037,29 @@ int ovs_ct_init(struct net *net)#endif } -void ovs_ct_exit (struct net *net) +/* Must be called with ovs_mutex held. Detaches the RCU-protected +* ct_limit_info and stores it in ovs_net->ct_limit_exit_data for +* ovs_ct_exit_finish() to complete the teardown after an RCU grace period. +*/ +void ovs_ct_exit_start (struct net *net__maybe_unused ) +{ +#if IS_ENABLED (CONFIG_NETFILTER_CONNCOUNT ) +struct ovs_net *ovs_net =net_generic (net, ovs_net_id); + + ovs_net->ct_limit_exit_data =ovs_ct_limit_exit_start (ovs_net); +#endif +} + +/* Completes the CT limit teardown. The pernet core guarantees an RCU +* grace period between detaching the state in ovs_ct_exit_start() and +* this call, so no RCU readers remain. +*/ +void ovs_ct_exit_finish (struct net *net) {struct ovs_net *ovs_net =net_generic (net, ovs_net_id);#if IS_ENABLED (CONFIG_NETFILTER_CONNCOUNT ) -ovs_ct_limit_exit (net, ovs_net); +ovs_ct_limit_exit_finish (net, ovs_net->ct_limit_exit_data);#endif if (ovs_net->xt_label)
net/openvswitch/conntrack.h +4 -2
@@ -14,7 +14,8 @@ enum ovs_key_attr;#if IS_ENABLED (CONFIG_NF_CONNTRACK )int ovs_ct_init (struct net *); -void ovs_ct_exit (struct net *); +void ovs_ct_exit_start (struct net *net); +void ovs_ct_exit_finish (struct net *net);bool ovs_ct_verify (struct net *,enum ovs_key_attr attr); int ovs_ct_copy_action(struct net *, const struct nlattr *, const struct sw_flow_key *, struct sw_flow_actions **,
@@ -40,7 +41,8 @@ void ovs_ct_free_action(const struct nlastatic inline int ovs_ct_init (struct net *net) {return 0 ; } -static inline void ovs_ct_exit (struct net *net) { } +static inline void ovs_ct_exit_start (struct net *net) { } +static inline void ovs_ct_exit_finish (struct net *net) { }static inline bool ovs_ct_verify (struct net *net,int attr) {
net/openvswitch/datapath.c +9 -1
@@ -2756,6 +2756,13 @@ static void __net_exit list_vports_from_ } } +static void __net_exit ovs_pre_exit_net(struct net *dnet) +{ +ovs_lock (); +ovs_ct_exit_start (dnet); +ovs_unlock (); +} + static void __net_exit ovs_exit_net(struct net *dnet) {struct datapath *dp, *dp_next;
@@ -2766,7 +2773,7 @@ static void __net_exit ovs_exit_net(struovs_lock (); -ovs_ct_exit (dnet); +ovs_ct_exit_finish (dnet);list_for_each_entry_safe (dp, dp_next, &ovs_net->dps, list_node)__dp_destroy (dp);
@@ -2790,6 +2797,7 @@ static void __net_exit ovs_exit_net(strustatic struct pernet_operations ovs_net_ops = { .init = ovs_init_net, + .pre_exit = ovs_pre_exit_net, .exit = ovs_exit_net, .id = &ovs_net_id, .size =sizeof (struct ovs_net ),
net/openvswitch/datapath.h +6 -2
@@ -164,7 +164,10 @@ struct dp_upcall_info {* Protected by genl_mutex. * @dp_notify_work: A work notifier to handle port unregistering. * @masks_rebalance: A work to periodically optimize flow table caches. -* @ct_limit_info: A hash table of conntrack zone connection limits. +* @ct_limit_info: Hash table of conntrack zone connection limits. Protected +* by RCU; updates and teardown are serialized by ovs_mutex. May be NULL during +* netns teardown. +* @ct_limit_exit_data: CT limit state detached at .pre_exit, freed at .exit. * @xt_label: Whether connlables are configured for the network or not. */ struct ovs_net {
@@ -172,7 +175,8 @@ struct ovs_net {struct work_struct dp_notify_work;struct delayed_work masks_rebalance;#if IS_ENABLED (CONFIG_NETFILTER_CONNCOUNT ) -struct ovs_ct_limit_info *ct_limit_info; +struct ovs_ct_limit_info __rcu *ct_limit_info; +struct ovs_ct_limit_info *ct_limit_exit_data;#endif bool xt_label; };
Greg Kroah-Hartman
maintainerPATCH 472/713 openvswitch: only skb_tx_error() a packet we are about to drop
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
commit 0dbc2398fca3bb33eda963849f865ddb1b3aa05e upstream.
queue_userspace_packet() borrows the packet skb -- it only copies it into a private netlink message (user_skb) and does not own it; on return do_execute_actions() keeps forwarding it through the flow's remaining actions. Its error path nevertheless calls skb_tx_error(skb), which via skb_zcopy_clear() does skb_shinfo(skb)->flags &= ~SKBFL_ALL_ZEROCOPY, stripping SKBFL_SHARED_FRAG from that live skb (skb_tx_error()'s kerneldoc says "skb must be freed afterwards").
For a MSG_ZEROCOPY skb carrying page-cache frags, SKBFL_SHARED_FRAG is what makes esp_input() skb_cow_data() before in-place AEAD; once it is stripped a later local ESP-in-UDP delivery decrypts in place over pages the sender does not own -- an unprivileged page-cache write (the "Fragnesia" primitive).do_execute_actions() ignores output_userspace()'s return value, so any action after a failed USERSPACE upcall inherits the stripped skb.
Move the skb_tx_error() to the flow-miss drop path - the "default" branch of ovs_dp_process_packet()'s switch(error), before kfree_skb().
The call has been here since commit 36d5fe6a0007 ("core, nfqueue,
openvswitch: Orphan frags in skb_zerocopy and handle errors") but was harmless until esp_input() began relying on SKBFL_SHARED_FRAG to gate in-place decrypt; only then did stripping it on a still-forwarded skb become a page-cache write primitive.
--- net/openvswitch/datapath.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
net/openvswitch/datapath.c +1 -2
@@ -285,6 +285,7 @@ void ovs_dp_process_packet(struct sk_bufconsume_skb (skb);break ;default : +skb_tx_error (skb);kfree_skb (skb);break ; }
@@ -601,8 +602,6 @@ static int queue_userspace_packet(struct err =genlmsg_unicast (ovs_dp_get_net (dp), user_skb, upcall_info->portid); user_skb =NULL ; out: -if (err) -skb_tx_error (skb);consume_skb (user_skb);consume_skb (nskb);
Greg Kroah-Hartman
maintainerPATCH 473/713 ALSA: ump: Fix corrupted data bytes at MIDI 1.0 SysEx to UMP conversion
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
commit 8a906c0b4f1ba123a95c166f644d2383bf30a420 upstream.
The cvt_legacy_sysex_to_ump() initialises only the first word of the output packet and ORs the data bytes into it. The second word is left alone, and the conversion context is kept across calls, so it still carries the previous packet's bytes. Those stale bits corrupt the new data. Any SysEx longer than six data bytes is affected.
A SysEx with the twelve data bytes 01..0c comes out as:
30160102 03040506
30260708 0b0e0f0e
The second packet declares six data bytes and four of them are wrong, inside the declared length.
The sibling cvt_legacy_cmd_to_ump() already clears the second word. Do the same here.
--- sound/core/ump_convert.c | 1 + 1 file changed, 1 insertion(+)
sound/core/ump_convert.c +1 -0
@@ -258,6 +258,7 @@ static int cvt_legacy_sysex_to_ump(strucelse status =UMP_SYSEX_STATUS_CONTINUE ; *data =ump_compose (UMP_MSG_TYPE_DATA , group, status, cvt->len); + data[1 ] =0 ; offset =8 ;for (i =0 ; i < cvt->len; i++) { *data |= cvt->buf[i] << offset;
Greg Kroah-Hartman
maintainerPATCH 474/713 arm64: compat: Fix decrementing LDM/STM alignment emulation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
commit f5b8b9037df387394a73aab47c5437bbac975077 upstream.
The compat alignment emulator inherited unsigned long data addresses from the 32-bit ARM implementation.
In do_alignment_ldmstm(), nr_regs is an unsigned int holding the transfer size. The function uses the same address addition for both transfer directions, negating nr_regs first for a decrementing LDM or STM. The 32-bit negation wraps before the addition, so the handler adds nearly 4 GiB instead of subtracting the transfer size.
The resulting address lies outside the compat task's address space, so decrementing LDM/STM emulation fails, while incrementing forms work.
For example, a backwards-moving copy routine using decrementing LDM/STM can take an alignment fault when called with unaligned pointers. The compat handler should emulate the transfer, but this bug instead causes SIGBUS.
The offset negated in do_alignment_finish_ldst() is offset_union.un, which is already unsigned long and does not have this width mismatch.
Make nr_regs unsigned long so its negation and the address arithmetic use the same width.
--- arch/arm64/kernel/compat_alignment.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/kernel/compat_alignment.c +2 -2
@@ -114,8 +114,8 @@ do_alignment_ldrdstrd(unsigned long addrstatic int do_alignment_ldmstm (unsigned long addr,u32 instr,struct pt_regs *regs) { -unsigned int rd, rn, nr_regs, regbits; -unsigned long eaddr, newaddr; +unsigned int rd, rn, regbits; +unsigned long eaddr, newaddr, nr_regs;unsigned int val;/* count the number of registers in the mask to be transferred */
Greg Kroah-Hartman
maintainerPATCH 475/713 arm64: proton-pack: Restore the nospectre_bhb command-line option
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
commit 2f6fc0612607c95489c960aaefc8cb5578cdab8c upstream.
Commit 7f1635737823 ("arm64: proton-pack: Fix hard lockup due to print in scheduler context") moved the "mitigation disabled" printks into spectre_print_disabled_mitigations(). For spectre-v2 and spectre-v4 only the pr_info_once() calls were removed, but for spectre-bhb the whole branch went with the print:
- } else if (cpu_mitigations_off() || __nospectre_bhb) {
- pr_info_once("spectre-bhb mitigation disabled ...\n");
spectre_bhb_enable_mitigation() therefore no longer tests __nospectre_bhb or cpu_mitigations_off() and the mitigation is enabled regardless of the command line. The parameter is still parsed and its flag is still checked by spectre_print_disabled_mitigations(), so the kernel prints "spectre-bhb mitigation disabled by command-line option" while /sys/devices/system/cpu/vulnerabilities/spectre_v2 reports "Mitigation:
CSV2, BHB" and the vectors are switched to EL1_VECTOR_BHB_LOOP.
The only remaining escape is the SPECTRE_VULNERABLE arm at the top of the chain, which a CSV2 core never reaches, so from Cortex-A76 and Neoverse N1 onwards both nospectre_bhb and mitigations=off are ignored. Both are documented in Documentation/admin-guide/kernel-parameters.txt.
The identical mistake was made on the neighbouring compile-time-option branch immediately before this regression and fixed shortly afterwards; this command-line branch was missed.build_bhb_mitigation() in arch/arm64/net/bpf_jit_comp.c still tests both flags, so nospectre_bhb currently keeps the exception-vector loop while dropping the cBPF epilogue mitigation.
Restore the check, folded into a spectre_bhb_mitigations_off() helper alongside its spectre_v2/v4 counterparts, and use it for the boot-time print in spectre_print_disabled_mitigations() as well. The print itself already lives there and does not need restoring.
Tested under QEMU with -cpu neoverse-n1 (CSV2, no ECBHB, no CLRBHB). Before, spectre_v2 read "Mitigation: CSV2, BHB" with and without the option; after, nospectre_bhb and mitigations=off both give "Mitigation:
CSV2, but not BHB" and a boot without either is unchanged.
--- arch/arm64/kernel/proton-pack.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
arch/arm64/kernel/proton-pack.c +8 -1
@@ -1023,6 +1023,11 @@ static int __init parse_spectre_bhb_para }early_param ("nospectre_bhb" , parse_spectre_bhb_param); +static bool spectre_bhb_mitigations_off (void ) +{ +return __nospectre_bhb ||cpu_mitigations_off (); +} +void spectre_bhb_enable_mitigation (const struct arm64_cpu_capabilities *entry) {bp_hardening_cb_t cpu_cb;
@@ -1036,6 +1041,8 @@ void spectre_bhb_enable_mitigation(const/* No point mitigating Spectre-BHB alone. */ }else if (!IS_ENABLED (CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY )) {/* Do nothing */ + }else if (spectre_bhb_mitigations_off ()) { +/* Mitigation disabled on the command line */ }else if (supports_ecbhb (SCOPE_LOCAL_CPU )) { state =SPECTRE_MITIGATED ;set_bit (BHB_HW , &system_bhb_mitigations);
@@ -1201,6 +1208,6 @@ void spectre_print_disabled_mitigations(if (spectre_v4_mitigations_off ())pr_info ("spectre-v4 %s" , spectre_disabled_suffix); -if (__nospectre_bhb ||cpu_mitigations_off ()) +if (spectre_bhb_mitigations_off ())pr_info ("spectre-bhb %s" , spectre_disabled_suffix); }
Greg Kroah-Hartman
maintainerPATCH 476/713 ASoC: amd: yc: Add DMI entry for MSI Thin A15 B7UC
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christopher Tolang <[email protected]>
commit e2aa5ad3be41accfcdcccc62348f21af7baa3a38 upstream.
This model requires an additional detection quirk to enable the internal microphone.
--- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+)
sound/soc/amd/yc/acp6x-mach.c +7 -0
@@ -846,6 +846,13 @@ static const struct dmi_system_id yc_acp { .driver_data = &acp6x_card, .matches = { +DMI_MATCH (DMI_BOARD_VENDOR ,"Micro-Star International Co., Ltd." ), + DMI_MATCH(DMI_PRODUCT_NAME, "Thin A15 B7UC"), + } + }, + { + .driver_data = &acp6x_card, + .matches = {DMI_MATCH (DMI_BOARD_VENDOR ,"ASUSTeK COMPUTER INC." ), DMI_MATCH(DMI_PRODUCT_NAME, "M7601RM"), }
Greg Kroah-Hartman
maintainerPATCH 477/713 ASoC: codecs: aw88261: only check PLL and clock state at power-up
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jorijn van der Graaf <[email protected]>
commit 06b6f1245567a4be862c3e1cc74577922ceb05fb upstream.
The SYSST check performed during device start requires SWS (amplifier switching, bit 8) and BSTS (boost finished, bit 9) on top of PLL lock and clock stability. Those bits cannot be asserted at this point in the
sequence: the check runs after amppd release but before the hmute/ULS-hmute release, and the amplifier neither switches nor finishes ramping its boost converter while it is still muted. With the Fairphone (Gen. 6) firmware profile, aw88261_dev_start() therefore always fails with
check sysst fail, reg_val=0x0011, check:0x311
and playback aborts, even though the amplifier is fine and PLL lock and stable clocks are present.
Check only PLL lock and clock stability, for which a definition already exists; this still re-validates the clocks after amppd release (aw88261_dev_check_syspll() checked them before it). This matches the vendor aw882xx driver, which only validates PLL lock and clock stability at this stage, and the in-tree aw88399 driver, which skips the SWS check whenever the amplifier may legitimately not be switching (AW88399_BIT_SYSST_NOSWS_CHECK).
--- sound/soc/codecs/aw88261.c | 6 +++--- sound/soc/codecs/aw88261.h | 6 ------ 2 files changed, 3 insertions(+), 9 deletions(-)
sound/soc/codecs/aw88261.c +3 -3
@@ -206,10 +206,10 @@ static int aw88261_dev_check_sysst(strucreturn ret; check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK ) - &AW88261_BIT_SYSST_CHECK ; -if (check_val !=AW88261_BIT_SYSST_CHECK ) { + &AW88261_BIT_PLL_CHECK ; +if (check_val !=AW88261_BIT_PLL_CHECK ) {dev_dbg (aw_dev->dev,"check sysst fail, reg_val=0x%04x, check:0x%x" , - reg_val,AW88261_BIT_SYSST_CHECK ); + reg_val,AW88261_BIT_PLL_CHECK );usleep_range (AW88261_2000_US ,AW88261_2000_US +10 ); }else {return 0 ;
sound/soc/codecs/aw88261.h +0 -6
@@ -194,12 +194,6 @@ AW88261_OTHS_OT_VALUE | \ AW88261_PLLS_LOCKED_VALUE)) -#define AW88261_BIT_SYSST_CHECK \ - (AW88261_BSTS_FINISHED_VALUE | \ - AW88261_SWS_SWITCHING_VALUE | \ - AW88261_CLKS_STABLE_VALUE | \ - AW88261_PLLS_LOCKED_VALUE) -#define AW88261_ULS_HMUTE_START_BIT (14)#define AW88261_ULS_HMUTE_BITS_LEN (1)#define AW88261_ULS_HMUTE_MASK \
Greg Kroah-Hartman
maintainerPATCH 478/713 dma-contiguous: fix truncation of numa_cma / cma_pernuma sizes >= 2G
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Graf <[email protected]>
commit 1476cca098f6d3a553fcec6fe9b7d86e15c00b59 upstream.
numa_cma=0:4G reserves nothing at all. dma_numa_cma_reserve() copies the requested size into a local int before handing it to cma_declare_contiguous_nid(), so 0x100000000 truncates to zero and the loop skips the node silently. Both parameters are documented in kernel-parameters.txt as nn[MG], so that is the syntax the documentation invites.
Which bits survive decides what a request turns into: 4G, 8G and 16G reserve nothing, 2G, 3G and 6G sign-extend into a size the allocator rejects with a warning, and 5G quietly reserves 1G.
It reaches further than those parameters. On a CMA_SIZE_PERNUMA kernel with no per-node parameter, dma_numa_cma_reserve() takes the per-node size from the default area, so a plain cma=4G on a multi-node machine feeds that size through the same local and loses every per-node area.
numa_cma_size[] and pernuma_size_bytes are both phys_addr_t, so use it for the local too, and give early_numa_cma() separate variables for the node id and the size while in there.
--- kernel/dma/contiguous.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
kernel/dma/contiguous.c +9 -7
@@ -140,21 +140,22 @@ static bool numa_cma_configured __initdata;static int __init early_numa_cma (char *p) {int nid, count =0 ; -unsigned long tmp; +unsigned long node; +phys_addr_t size;char *s = p;while (*s) { -if (sscanf (s,"%lu%n" , &tmp, &count) !=1 ) +if (sscanf (s,"%lu%n" , &node, &count) !=1 )break ;if (s[count] ==':' ) { -if (tmp >=MAX_NUMNODES ) +if (node >=MAX_NUMNODES )break ; - nid =array_index_nospec (tmp,MAX_NUMNODES ); + nid =array_index_nospec (node,MAX_NUMNODES ); s += count +1 ; - tmp =memparse (s, &s); - numa_cma_size[nid] = tmp; + size =memparse (s, &s); + numa_cma_size[nid] = size;if (*s ==',' ) s++;
@@ -207,9 +208,10 @@ static void __init dma_numa_cma_reserve(void) pernuma_size_bytes =cma_get_size (dma_contiguous_default_area);for_each_node (nid) { -int size, ret; +phys_addr_t size;char name[CMA_MAX_NAME ];struct cma **cma; +int ret;if (!node_online (nid)) {if (pernuma_size_bytes || numa_cma_size[nid])
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 479/713 hwmon: (max6621) fix negative temperature offset and crit readings
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cong Nguyen <[email protected]>
commit acc52bd431e2d8698fae8d82a74ac45d79b62e0a upstream.
max6621_read() reads the CONFIG2 offset and the critical alert threshold registers into a u32 and scales them without sign extension:
/* offset */ *val = (regval >> MAX6621_REG_TEMP_SHIFT) * 1000L; /* crit */ *val = regval * 1000L;
Both attributes are writable and their write paths clamp to a negative minimum and encode negative values, so a value written as negative is read back as a large positive number. For example, writing a -10 degrees C offset stores max6621_temp_mc2reg(-10000) = (-10 << 6) = 0xfd80; the read then computes 0xfd80 >> 6 = 1014 -> 1014000 instead of -10000.
Cast the register value to s16 before scaling so the read preserves the sign the write path encodes. The temperature input path already uses an s8 intermediate and is left unchanged.
--- drivers/hwmon/max6621.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/hwmon/max6621.c +2 -2
@@ -239,7 +239,7 @@ max6621_read(struct device *dev, enum hwif (ret)return ret; - *val = (regval >>MAX6621_REG_TEMP_SHIFT ) * + *val = ((s16 )regval >>MAX6621_REG_TEMP_SHIFT ) *1000L ;break ;
@@ -254,7 +254,7 @@ max6621_read(struct device *dev, enum hwif (ret)return ret; - *val = regval *1000L ; + *val = (s16 )regval *1000L ;break ;case hwmon_temp_crit_alarm:
Greg Kroah-Hartman
maintainerPATCH 480/713 hwmon: (max6621) fix temperature clamp range
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cong Nguyen <[email protected]>
commit 24fbeb83d9b750a36da42cb835a154d80fd3d495 upstream.
MAX6621_TEMP_INPUT_MIN and MAX6621_TEMP_INPUT_MAX are used to clamp the writable offset and critical thresholds. They are defined as -127000 and 128000.
The driver decodes the temperature through an s8 and its own comment in max6621_read() documents an 8-bit two's complement value, whose range is -128 to +127 degrees C. The current limits therefore reject the valid -128 degrees C and accept +128 degrees C, which does not fit the 8-bit range.
Correct the limits to -128000 and 127000.
--- drivers/hwmon/max6621.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/hwmon/max6621.c +2 -2
@@ -17,8 +17,8 @@#define MAX6621_DRV_NAME "max6621"#define MAX6621_TEMP_INPUT_REG_NUM 9 -#define MAX6621_TEMP_INPUT_MIN -127000 -#define MAX6621_TEMP_INPUT_MAX 128000 +#define MAX6621_TEMP_INPUT_MIN -128000 +#define MAX6621_TEMP_INPUT_MAX 127000#define MAX6621_TEMP_ALERT_CHAN_SHIFT 1#define MAX6621_TEMP_S0D0_REG 0x00
Greg Kroah-Hartman
maintainerPATCH 481/713 i2c: mxs: fix DMA channel leak on probe error
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ruoyu Wang <[email protected]>
commit 777979e627115734052b323d2721cdb500e81dcf upstream.
mxs_i2c_probe() requests an exclusive DMA channel before resetting the controller and registering the I2C adapter. If either later operation fails, probe returns without releasing the channel because the remove callback is not invoked after a failed probe.
Use devm_dma_request_chan() so the device core releases the channel on probe failure and driver detach. Remove the manual release from the remove callback because the channel is now device-managed.
This issue was found by a static analysis checker and confirmed by manual source review.
--- drivers/i2c/busses/i2c-mxs.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-)
drivers/i2c/busses/i2c-mxs.c +1 -4
@@ -839,7 +839,7 @@ static int mxs_i2c_probe(struct platform }/* Setup the DMA */ - i2c->dmach =dma_request_chan (dev,"rx-tx" ); + i2c->dmach =devm_dma_request_chan (dev,"rx-tx" );if (IS_ERR (i2c->dmach)) {return dev_err_probe (dev,PTR_ERR (i2c->dmach),"Failed to request dma\n" );
@@ -877,9 +877,6 @@ static void mxs_i2c_remove(struct platfoi2c_del_adapter (&i2c->adapter); -if (i2c->dmach) -dma_release_channel (i2c->dmach); -writel (MXS_I2C_CTRL0_SFTRST , i2c->regs +MXS_I2C_CTRL0_SET ); }
Greg Kroah-Hartman
maintainerPATCH 482/713 ipmi: Fix use-after-free of cmd_rcvr in _ipmi_destroy_user()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yifei Gao <[email protected]>
commit 05ec76cfbce653e07cec19b9b8b20e33449d5d87 upstream.
Commit 9e91f8a6c868 ("ipmi:msghandler: Remove srcu for the ipmi_interfaces list") dropped the synchronize_rcu() between unlinking the command receivers from intf->cmd_rcvrs and freeing them, updating only the comment that explains why the barrier is needed.
The cmd_rcvrs list is still traversed under plain RCU: find_cmd_rcvr() walks it inside rcu_read_lock(), and handle_ipmb_get_msg_cmd() borrows rcvr->user from that lookup within the same read-side section. Without the grace period, _ipmi_destroy_user() can kfree() a cmd_rcvr while a reader still holds a pointer to it, causing a use-after-free.
The rework only made srcu unnecessary for the interfaces list; the cmd_rcvrs list still relies on plain RCU. Restore the synchronize_rcu() before freeing the receivers.
Message-ID: <[email protected]>
--- drivers/char/ipmi/ipmi_msghandler.c | 1 + 1 file changed, 1 insertion(+)
drivers/char/ipmi/ipmi_msghandler.c +1 -0
@@ -1391,6 +1391,7 @@ static void _ipmi_destroy_user(struct ip } }mutex_unlock (&intf->cmd_rcvrs_mutex); +synchronize_rcu ();while (rcvrs) { rcvr = rcvrs; rcvrs = rcvr->next;
Greg Kroah-Hartman
maintainerPATCH 483/713 lockd: pin next file across nlm_inspect_file lock-drop
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 526c49cff3f72c3ec74752016380c7567040581b upstream.
nlm_traverse_files() pins the current file with f_count++ across a mutex_unlock for nlm_inspect_file(), but nothing pins the saved next pointer. A concurrent nlm_release_file() can kfree the next file during the unlock window, and the iterator dereferences freed memory on the next loop step.
Pin both current and next before the lock-drop. Advance by swapping the pinned cursors at the end of each iteration so next is always held alive across the unlock.
Always call nlm_file_release() after dropping the iteration pin, regardless of whether the file matched the predicate. Use nlm_file_inuse(), which does a live walk of the inode lock list, rather than the cached f_locks field, so skipped files that never ran nlm_inspect_file() are evaluated correctly.
Because every file in a hash bucket is now pinned and released, files skipped by the is_failover_file predicate that have no locks, blocks, shares, or external references are deleted during traversal. The old code never evaluated skipped files for cleanup. The new behavior is intentional: such files are stale and should not persist in the table.
--- fs/lockd/svcsubs.c | 53 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 23 deletions(-)
fs/lockd/svcsubs.c +30 -23
@@ -350,12 +350,10 @@ nlm_file_inuse(struct nlm_file *file)return 0 ; } -static void nlm_close_files (struct nlm_file *file) +static void nlm_file_release (struct nlm_file *file) { -if (file->f_file[O_RDONLY ]) - nlmsvc_ops->fclose (file->f_file[O_RDONLY ]); -if (file->f_file[O_WRONLY ]) - nlmsvc_ops->fclose (file->f_file[O_WRONLY ]); +if (!nlm_file_inuse (file)) +nlm_delete_file (file); }/*
@@ -365,32 +363,41 @@ static int nlm_traverse_files(void *data, nlm_host_match_fn_t match, int (*is_failover_file)(void *data, struct nlm_file *file)) { -struct hlist_node *next; -struct nlm_file *file; +struct nlm_file *file, *next;int i, ret =0 ;mutex_lock (&nlm_file_mutex);for (i =0 ; i <FILE_NRHASH ; i++) { -hlist_for_each_entry_safe (file, next, &nlm_files[i], f_list) { -if (is_failover_file && !is_failover_file (data, file)) -continue ; + file =hlist_entry_safe (nlm_files[i].first, + struct nlm_file, f_list); +if (file) file->f_count++; -mutex_unlock (&nlm_file_mutex); +while (file) { +/* +* Pin the next neighbour before we drop the mutex +* for nlm_inspect_file(); a concurrent +* nlm_release_file() under the same mutex would +* otherwise be free to unlink and kfree it during +* the unlock window, leaving us to dereference a +* freed slab when we walked to next afterwards. +*/ + next =hlist_entry_safe (file->f_list.next, + struct nlm_file, f_list); +if (next) + next->f_count++; -/* Traverse locks, blocks and shares of this file -* and update file->f_locks count */ -if (nlm_inspect_file (data, file, match)) - ret =1 ; +if (!is_failover_file ||is_failover_file (data, file)) { +mutex_unlock (&nlm_file_mutex); -mutex_lock (&nlm_file_mutex); - file->f_count--; -/* No more references to this file. Let go of it. */ -if (list_empty (&file->f_blocks) && !file->f_locks - && !file->f_shares && !file->f_count) { -hlist_del (&file->f_list); -nlm_close_files (file); -kfree (file); +if (nlm_inspect_file (data, file, match)) + ret =1 ; + +mutex_lock (&nlm_file_mutex); } + + file->f_count--; +nlm_file_release (file); + file = next; } }mutex_unlock (&nlm_file_mutex);
Greg Kroah-Hartman
maintainerPATCH 484/713 lockd: fix NLMv4 GRANTED_MSG handling
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Olga Kornievskaia <[email protected]>
commit 88e90d3f7e0251770e52d85c5319f037024b2c59 upstream.
GRANTED_MSG is a server-to-client callback, so it runs on the client, where nfsd never registers nlmsvc_ops. The nlm4svc_lookup_host() helper is for the server-side request handlers (TEST/LOCK/CANCEL/UNLOCK), which reach nlmsvc_ops->fopen and must reject requests when nfsd isn't running. GRANTED_MSG only calls nlmclnt_grant(). Instead, of calling nlm4svc_lookup_host(), which results in a client failing a GRANTED_MSG call, call nlmsvc_lookup_host().
--- fs/lockd/svc4proc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/lockd/svc4proc.c +2 -1
@@ -872,7 +872,8 @@ static __be32 nlm4svc_proc_granted_msg(struct svc_rqst *rqstp)struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;struct nlm_host *host; - host =nlm4svc_lookup_host (rqstp, argp->xdrgen.alock.caller_name, false); + host =nlmsvc_lookup_host (rqstp, argp->xdrgen.alock.caller_name.data, + argp->xdrgen.alock.caller_name.len);if (!host)return rpc_system_err;
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 485/713 lockd: fix NULL dereference on lockowner allocation failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuangpeng Bai <[email protected]>
commit 4c7fc129db061c7daab841c4f3c342d894832362 upstream.
nlmclnt_locks_init_private() installs NLM file lock operations even when nlmclnt_find_lockowner() fails to allocate a lockowner. nlmclnt_proc() then returns -ENOMEM, but the VFS still tears down the partially initialized file_lock and calls locks_release_private().
That invokes nlmclnt_locks_release_private(), which dereferences fl->fl_u.nfs_fl.owner and crashes because the owner was never installed.
Clear fl_ops before attempting to initialize the NLM private state, and install the NLM lock operations only after a lockowner has been allocated successfully.
--- fs/lockd/clntproc.c | 3 +++ 1 file changed, 3 insertions(+)
fs/lockd/clntproc.c +3 -0
@@ -487,9 +487,12 @@ static const struct file_lock_operationsstatic void nlmclnt_locks_init_private (struct file_lock *fl,struct nlm_host *host) { fl->fl_u.nfs_fl.state =0 ; + fl->fl_ops =NULL ; fl->fl_u.nfs_fl.owner =nlmclnt_find_lockowner (host, fl->c.flc_owner);INIT_LIST_HEAD (&fl->fl_u.nfs_fl.list); +if (!fl->fl_u.nfs_fl.owner) +return ; fl->fl_ops = &nlmclnt_lock_ops; }
Greg Kroah-Hartman
maintainerPATCH 486/713 lockd: fix swapped arguments in nlmsvc_match_ip()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oscar Ou <[email protected]>
commit b9060689f49dc663e9a3d069c4a65ff63a836e66 upstream.
When releasing locks by server IP address via /proc/fs/nfsd/unlock_ip, nlmsvc_unlock_all_by_ip() calls nlm_traverse_files() with the server sockaddr as the opaque @data argument:
nlm_traverse_files(server_addr, nlmsvc_match_ip, NULL);
The match callback is later invoked from nlm_traverse_locks() as:
match(lockhost, host);
where the first argument is the nlm_host that owns the lock, and the second argument is the @data that was originally passed down (here the server sockaddr). This is the convention every other match callback relies on (nlmsvc_mark_host(), nlmsvc_same_host(), nlmsvc_is_client()):
arg1 is the real nlm_host, arg2 is the caller-supplied reference value.
nlmsvc_match_ip() has had these two arguments reversed ever since the unlock-by-IP feature was introduced in commit 4373ea84c84d ("lockd:
unlock lockd locks associated with a given server ip"):
return rpc_cmp_addr(nlm_srcaddr(host), datap);
Here @host is actually the server sockaddr, so nlm_srcaddr(host) dereferences a struct sockaddr as a struct nlm_host and reads garbage at the offset of h_srcaddr; meanwhile @datap is actually the lock owner's nlm_host but is compared as a sockaddr. As a result the comparison practically never matches and locks are not released for the requested IP.
Swap the arguments so the lock owner's source address is compared against the requested server address:
return rpc_cmp_addr(nlm_srcaddr(datap), (struct sockaddr *)host);
--- fs/lockd/lockd.h | 2 +- fs/lockd/svcsubs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
fs/lockd/lockd.h +1 -1
@@ -314,7 +314,7 @@ void nsm_release(struct nsm_handle *n* This is used in garbage collection and resource reclaim * A return value != 0 means destroy the lock/block/share */ -typedef int (*nlm_host_match_fn_t )(void *cur,struct nlm_host *ref); +typedef int (*nlm_host_match_fn_t )(void *owner,struct nlm_host *ref);/* * Server-side lock handling
fs/lockd/svcsubs.c +1 -1
@@ -557,7 +557,7 @@ EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sstatic int nlmsvc_match_ip (void *datap,struct nlm_host *host) { -return rpc_cmp_addr (nlm_srcaddr (host), datap); +return rpc_cmp_addr (nlm_srcaddr (datap), (struct sockaddr *)host); }/**
Greg Kroah-Hartman
maintainerPATCH 487/713 nvme: nvme-fc: Fix nvme_fc_create_hw_io_queues() queue deletion in error path
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ewan D. Milne <[email protected]>
commit 22eb631bf86ee3246f47885e4fa94154a46863e4 upstream.
nvme_fc_create_hw_io_queues() will call __nvme_fc_delete_hw_queue() for the last queue on which __nvme_fc_create_hw_queue() reported an error when deleting all the io queues if they cannot all be created. This is incorrect since the last queue did not actually get created.
The most recent change to this code was commit 17a1ec08ce70 ("nvme/fc: simplify error handling of nvme_fc_create_hw_io_queues") which moved the cleanup to thedelete_queues: label and changed the loop bounds, however the code was not correct prior to this change in a different way. The original commit e399441de911 ("nvme-fabrics: Add host support for FC transport") had a different error which called __nvme_fc_delete_hw_queue() on queue index 0 which is used for the admin queue.
Fix this by correcting the initial loop index when deleting the io queues.
--- drivers/nvme/host/fc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/host/fc.c +1 -1
@@ -2318,7 +2318,7 @@ nvme_fc_create_hw_io_queues(struct nvme_return 0 ; delete_queues: -for (; i >0 ; i--) +for (--i; i >0 ; i--)__nvme_fc_delete_hw_queue (ctrl, &ctrl->queues[i], i);return ret; }
Greg Kroah-Hartman
maintainerPATCH 488/713 nvme: zero the discard fallback page
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yehyeong Lee <[email protected]>
commit bededeaaeff404978a5a8e2a605a6c3017cddd3e upstream.
nvme_setup_discard() always maps sizeof(struct nvme_dsm_range) * NVME_DSM_MAX_RANGES = 4096 bytes as the DSM payload however many ranges the command declares, because some devices ignore the 'Number of Ranges' field - the Fixes: commit records two that read past the declared ranges. A single-range discard fills only the first 16 bytes.
Normally the buffer comes from kzalloc() and the other 4080 bytes are zero. When that allocation fails the code falls back to the per-controller ctrl->discard_page, which nvme_init_ctrl() obtains with alloc_page(GFP_KERNEL) and nothing ever zeroes, so those 4080 bytes are whatever the page last held and are handed to the controller. Reaching it requires the kzalloc(GFP_ATOMIC | __GFP_NOWARN) to fail, that is memory pressure; it is not remotely triggerable. Failing the allocation under KMSAN reproduces it, with the leaked tail full of vmemmap struct page pointers. The extent in the report is a partial transfer of the payload, not the whole 4096 bytes; the 16-byte boundary in it is the one declared range:
[ 11.991601] BUG: KMSAN: uninit-value in dma_map_phys+0x14c8/0x1900 [ 11.991969] dma_map_phys+0x14c8/0x1900 [ 11.992220] dma_map_page_attrs+0xcf/0x130 [ 11.992485] e1000_xmit_frame+0x4099/0x6d10 [ 11.992768] dev_hard_start_xmit+0x22f/0xa80 [ 11.993068] sch_direct_xmit+0x35c/0xcb0 [ 11.993315] __dev_queue_xmit+0x1ee5/0x5eb0 [ 11.993608] ip_finish_output2+0x1903/0x1c30 [ 11.993881] ip_finish_output+0x288/0x870 [ 11.994125] ip_output+0x15e/0x400 [ 11.994365] __ip_queue_xmit+0x1e85/0x1fb0 [ 11.994639] ip_queue_xmit+0x60/0x80 [ 11.994899] __tcp_transmit_skb+0x4e71/0x5fa0 [ 11.995210] tcp_write_xmit+0x3a36/0x9160 [ 11.995533] __tcp_push_pending_frames+0xc5/0x3c0 [ 11.995854] tcp_push+0x7dc/0x840 [ 11.996076] tcp_sendmsg_locked+0x766c/0x8400 [ 11.996371] tcp_sendmsg+0x4b/0x90 [ 11.996572] inet_sendmsg+0x134/0x2a0 [ 11.996823] __sock_sendmsg+0x265/0x360 [ 11.997076] sock_sendmsg+0x100/0x1e0 [ 11.997293] nvme_tcp_try_send+0x196f/0x6370 [ 11.997605] nvme_tcp_queue_rq+0x1d54/0x20b0 [ 11.997882] blk_mq_dispatch_rq_list+0x5ee/0x2e50 [ 11.998175] __blk_mq_sched_dispatch_requests+0x16dc/0x24a0 [ 11.998539] blk_mq_sched_dispatch_requests+0x11b/0x2c0 [ 11.998865] blk_mq_run_work_fn+0x13b/0x280 [ 11.999146] process_scheduled_works+0x966/0x1ad0 [ 11.999465] worker_thread+0xe44/0x1480 [ 11.999709] kthread+0x53b/0x600 [ 11.999927] ret_from_fork+0x29f/0x7c0 [ 12.000191] ret_from_fork_asm+0x1a/0x30 [ 12.000460] [ 12.000558] Uninit was created at: [ 12.000788] __alloc_frozen_pages_noprof+0x8bf/0xd30 [ 12.001096] alloc_pages_mpol+0x1d0/0x5f0 [ 12.001326] alloc_pages_noprof+0x102/0x290 [ 12.001627] nvme_init_ctrl+0x5a3/0x9f0 [ 12.001891] nvme_tcp_create_ctrl+0xd75/0x19b0 [ 12.002170] nvmf_dev_write+0x4c68/0x4fd0 [ 12.002426] vfs_write+0x587/0x1a10 [ 12.002636] __x64_sys_write+0x207/0x4f0 [ 12.002874] x64_sys_call+0x2ff0/0x3ea0 [ 12.003123] do_syscall_64+0x147/0x3b0 [ 12.003400] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 12.003680] [ 12.003777] Bytes 16-2843 of 2844 are uninitialized [ 12.004068] Memory access of size 2844 starts at ffff888109f82000 [ 12.004412] [ 12.004530] CPU: 0 UID: 0 PID: 101 Comm: kworker/0:1H Not tainted 7.2.0-rc5-NVMECTL-gf5098b6bae76 #1 PREEMPT(lazy) [ 12.005127] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 [ 12.005762] Workqueue: kblockd blk_mq_run_work_fn [ 12.006073] =====================================================
Allocate the page with __GFP_ZERO. The single allocation site covers every use of it: bytes no discard has written stay zero, and bytes one did write hold that controller's own range list, which it has already been sent.
--- drivers/nvme/host/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/host/core.c +1 -1
@@ -5195,7 +5195,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrBUILD_BUG_ON (NVME_DSM_MAX_RANGES *sizeof (struct nvme_dsm_range ) >PAGE_SIZE ); - ctrl->discard_page =alloc_page (GFP_KERNEL ); + ctrl->discard_page =alloc_page (GFP_KERNEL | __GFP_ZERO);if (!ctrl->discard_page) { ret = -ENOMEM ; goto out;
Greg Kroah-Hartman
maintainerPATCH 489/713 nvme-pci: disable controller on admin queue IRQ setup failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit 08660a5c8d497f43191635d97efd31cd35051f15 upstream.
nvme_pci_configure_admin_queue() enables the controller and then requests the admin queue interrupt. If queue_request_irq() fails it returns without disabling the controller, and no caller compensates: nvme_pci_enable() only frees the IRQ vectors and calls pci_disable_device(), after which nvme_dev_disable() treats the controller as dead and skips nvme_disable_ctrl(). The controller is left enabled (CC.EN set) on this error path.
Disable it in the failure path, while the PCI device is still enabled so the CC.EN clear handshake completes.
This issue was identified during our ongoing static-analysis research while reviewing kernel code.
--- drivers/nvme/host/pci.c | 1 + 1 file changed, 1 insertion(+)
drivers/nvme/host/pci.c +1 -0
@@ -2400,6 +2400,7 @@ static int nvme_pci_configure_admin_queu result =queue_request_irq (nvmeq);if (result) { dev->online_queues--; +nvme_disable_ctrl (&dev->ctrl, false);return result; }
Greg Kroah-Hartman
maintainerPATCH 490/713 nvme-tcp: do not accept C2HData based on blk_rq_payload_bytes() alone
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yehyeong Lee <[email protected]>
commit 3a4aa9e6ad3e35f8e24d5eaf38ee4d437075fb36 upstream.
Commit 25e5cb780e62 ("nvme-tcp: fix possible crash in write_zeroes processing") established that blk_rq_payload_bytes() must not be read without first checking blk_rq_nr_phys_segments(), and recorded the result in nvme_tcp_setup_cmd_pdu() as req->data_len. The receive side was left as it was.
The two differ for REQ_OP_WRITE_ZEROES, which has no physical segments but a non-zero blk_rq_bytes(), so setup leaves req->iter untouched while the receive gate lets a C2HData through and nvme_tcp_recv_data() copies into whatever the previous command on that tag left there. The driver-private area is zeroed only when the tag set is allocated.
Reproduced with a test target that leaves a residual iterator on a tag and then sends a C2HData for a WRITE_ZEROES command on the same tag:
BUG: KASAN: wild-memory-access in _copy_to_iter+0x642/0x1330 Write of size 512 at addr ffe728c2175dfa81 by task kworker/0:1H/103 CPU: 0 UID: 0 PID: 103 Comm: kworker/0:1H Not tainted 7.2.0-rc5-NVMETCP-gf5098b6bae76 #1 PREEMPT(lazy) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Workqueue: nvme_tcp_wq nvme_tcp_io_work Call Trace: <TASK> dump_stack_lvl+0x53/0x70 kasan_report+0xce/0x100 ? _copy_to_iter+0x642/0x1330 kasan_check_range+0x105/0x1b0 __asan_memcpy+0x3c/0x60 _copy_to_iter+0x642/0x1330 ? __pfx_sock_has_perm+0x10/0x10 ? worker_thread+0x45b/0xd10 ? __pfx__copy_to_iter+0x10/0x10 ? _raw_spin_lock_bh+0x83/0xe0 ? __pfx__raw_spin_lock_bh+0x10/0x10 __skb_datagram_iter+0xf3/0x820 ? __pfx_simple_copy_to_iter+0x10/0x10 ? __asan_memcpy+0x3c/0x60 ? skb_copy_bits+0x58d/0x830 skb_copy_datagram_iter+0x37/0x120 nvme_tcp_recv_skb+0xa07/0x4320 ? __pfx_nvme_tcp_recv_skb+0x10/0x10 __tcp_read_sock+0x1ab/0x810 ? __pfx_nvme_tcp_recv_skb+0x10/0x10 ? __pfx_lock_sock_nested+0x10/0x10 ? __pfx___tcp_read_sock+0x10/0x10 nvme_tcp_try_recv+0x152/0x1e0 ? __pfx_nvme_tcp_try_recv+0x10/0x10 ? __pfx_mutex_unlock+0x10/0x10 nvme_tcp_io_work+0x1e4/0x6c0 ? __schedule+0x181a/0x49f0 ? __pfx_nvme_tcp_io_work+0x10/0x10 process_one_work+0x633/0x1030
Keep the blk_rq_payload_bytes() test and add req->data_len to it. The old test is what rejects a C2HData naming a tag that is no longer in flight, because blk_update_request() zeroes rq->__data_len on completion; req->data_len and req->curr_bio are driver-private and survive completion, so they cannot stand in for it. Setup initialises the iterator only when both req->curr_bio and req->data_len are set, so the gate now tests the same two.
--- drivers/nvme/host/tcp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/nvme/host/tcp.c +3 -1
@@ -644,6 +644,7 @@ static int nvme_tcp_process_nvme_cqe(strstatic int nvme_tcp_handle_c2h_data (struct nvme_tcp_queue *queue,struct nvme_tcp_data_pdu *pdu) { +struct nvme_tcp_request *req;struct request *rq; rq =nvme_find_rq (nvme_tcp_tagset (queue), pdu->command_id);
@@ -654,7 +655,8 @@ static int nvme_tcp_handle_c2h_data(strureturn -ENOENT ; } -if (!blk_rq_payload_bytes (rq)) { + req =blk_mq_rq_to_pdu (rq); +if (!blk_rq_payload_bytes (rq) || !req->curr_bio || !req->data_len) {dev_err (queue->ctrl->ctrl.device,"queue %d tag %#x unexpected data\n" ,nvme_tcp_queue_id (queue), rq->tag);
Greg Kroah-Hartman
maintainerPATCH 491/713 nvme-tcp: fix host memory disclosure on R2T for a read command
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yehyeong Lee <[email protected]>
commit 6efbc52237facda35d2d874fe1765bb4839275d8 upstream.
nvme_tcp_handle_r2t() does not check the direction of the request the R2T refers to. A malicious controller can send an R2T for a READ and the host will answer it: nvme_tcp_setup_h2c_data_pdu() builds the H2CData header and nvme_tcp_try_send_data() sends the request's data buffer. That buffer is the READ destination, so its contents go to the controller.
The command then completes normally and nothing is logged.
Against a test controller that answers every READ with an R2T, a 4096 byte buffered read returned all 4096 bytes, split over two R2Ts. The pages contained stale kernel data, including an array of struct page pointers.
Reject an R2T for a request that is not a write.
--- drivers/nvme/host/tcp.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/nvme/host/tcp.c +7 -0
@@ -750,6 +750,13 @@ static int nvme_tcp_handle_r2t(struct nv } req =blk_mq_rq_to_pdu (rq); +if (unlikely (rq_data_dir (rq) !=WRITE )) { +dev_err (queue->ctrl->ctrl.device, +"req %d unexpected r2t for a non-write command\n" , + rq->tag); +return -EPROTO ; + } +if (unlikely (!r2t_length)) { dev_err(queue->ctrl->ctrl.device, "req %d r2t len is %u, probably a bug...\n",