6.18.53-rc1 review
Subsystems (6)
Greg Kroah-Hartman
maintainerPATCH 586/1250 coresight: platform: defer connection counter increment until alloc succeeds
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jie Gan <[email protected]>
[ Upstream commit 1563ae33dc4f5ebac96b93af2ef72e72aaaa31ae ]
coresight_add_out_conn() increments nr_outconns before calling devm_krealloc_array() and again before devm_kmalloc(). If either allocation fails, the counter is already bumped while the corresponding array entry is NULL or uninitialized garbage.
coresight_add_in_conn() has the same problem with nr_inconns and devm_krealloc_array().
In both cases the probe returns -ENOMEM, which causes coresight_get_platform_data() to call coresight_release_platform_data() for cleanup. That function iterates up to nr_outconns (or nr_inconns) entries and dereferences each pointer unconditionally, hitting the NULL or garbage entry and panicking instead of failing gracefully.
Fix by moving the counter increments to after all allocations succeed, so the struct is always consistent on any error path.
--- drivers/hwtracing/coresight/coresight-platform.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/hwtracing/coresight/coresight-platform.c +6 -6
@@ -45,9 +45,8 @@ coresight_add_out_conn(struct device *dev, } } - pdata->nr_outconns++; pdata->out_conns = -devm_krealloc_array (dev, pdata->out_conns, pdata->nr_outconns, +devm_krealloc_array (dev, pdata->out_conns, pdata->nr_outconns +1 ,sizeof (*pdata->out_conns),GFP_KERNEL );if (!pdata->out_conns)return ERR_PTR (-ENOMEM );
@@ -63,7 +62,8 @@ coresight_add_out_conn(struct device *dev,* used right away. */ *conn = *new_conn; - pdata->out_conns[pdata->nr_outconns -1 ] = conn; + pdata->out_conns[pdata->nr_outconns] = conn; + pdata->nr_outconns++;return conn; }EXPORT_SYMBOL_GPL (coresight_add_out_conn);
@@ -86,13 +86,13 @@ int coresight_add_in_conn(struct coresight_connection *out_conn)return 0 ; } - pdata->nr_inconns++; pdata->in_conns = -devm_krealloc_array (dev, pdata->in_conns, pdata->nr_inconns, +devm_krealloc_array (dev, pdata->in_conns, pdata->nr_inconns +1 ,sizeof (*pdata->in_conns),GFP_KERNEL );if (!pdata->in_conns)return -ENOMEM ; - pdata->in_conns[pdata->nr_inconns -1 ] = out_conn; + pdata->in_conns[pdata->nr_inconns] = out_conn; + pdata->nr_inconns++;return 0 ; }EXPORT_SYMBOL_GPL (coresight_add_in_conn);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 587/1250 RDMA/irdma: Replace waitqueue and flag with completion
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jacob Moroni <[email protected]>
[ Upstream commit d9c8c45e6d2f438a3c8e643ae78b59454fa0fadd ]
The driver previously used a waitqueue along with an explicit request_done flag, but without proper barriers around request_done.
An earlier patch by Gui-Dong Han <[email protected]> attempted to fix this by adding the missing memory barriers. Rather than adding the barriers, this patch replaces the waitqueue+flag with a completion, which is designed for this exact purpose.
--- drivers/infiniband/hw/irdma/hw.c | 7 +++---- drivers/infiniband/hw/irdma/main.h | 3 +-- drivers/infiniband/hw/irdma/utils.c | 12 +++++------- 3 files changed, 9 insertions(+), 13 deletions(-)
drivers/infiniband/hw/irdma/hw.c +3 -4
@@ -167,8 +167,7 @@ static void irdma_complete_cqp_request(struct irdma_cqp *cqp, struct irdma_cqp_request *cqp_request) {if (cqp_request->waiting) { -WRITE_ONCE (cqp_request->request_done, true); -wake_up (&cqp_request->waitq); +complete_all (&cqp_request->comp); }else if (cqp_request->callback_fcn) { cqp_request->callback_fcn (cqp_request); }
@@ -1039,9 +1038,9 @@ static int irdma_create_cqp(struct irdma_pci_f *rf)INIT_LIST_HEAD (&cqp->cqp_avail_reqs);INIT_LIST_HEAD (&cqp->cqp_pending_reqs); -/* init the waitqueue of the cqp_requests and add them to the list */ +/* init the completion of the cqp_requests and add them to the list */ for (i =0 ; i < sqsize; i++) { -init_waitqueue_head (&cqp->cqp_requests[i].waitq); +init_completion (&cqp->cqp_requests[i].comp);list_add_tail (&cqp->cqp_requests[i].list, &cqp->cqp_avail_reqs); }init_waitqueue_head (&cqp->remove_wq);
drivers/infiniband/hw/irdma/main.h +1 -2
@@ -160,13 +160,12 @@ struct irdma_cqp_compl_info {struct irdma_cqp_request {struct cqp_cmds_info info; -wait_queue_head_t waitq; +struct completion comp;struct list_head list;refcount_t refcnt;void (*callback_fcn)(struct irdma_cqp_request *cqp_request);void *param;struct irdma_cqp_compl_info compl_info; -bool request_done;/* READ/WRITE_ONCE macros operate on it */ bool waiting:1 ;bool dynamic:1 ;bool pending:1 ;
drivers/infiniband/hw/irdma/utils.c +5 -7
@@ -442,7 +442,7 @@ struct irdma_cqp_request *irdma_alloc_and_get_cqp_request(struct irdma_cqp *cqp,if (cqp_request) { cqp_request->dynamic = true;if (wait) -init_waitqueue_head (&cqp_request->waitq); +init_completion (&cqp_request->comp); } }if (!cqp_request) {
@@ -479,7 +479,7 @@ void irdma_free_cqp_request(struct irdma_cqp *cqp,if (cqp_request->dynamic) {kfree (cqp_request); }else { -WRITE_ONCE (cqp_request->request_done, false); +reinit_completion (&cqp_request->comp); cqp_request->callback_fcn =NULL ; cqp_request->waiting = false; cqp_request->pending = false;
@@ -514,8 +514,7 @@ irdma_free_pending_cqp_request(struct irdma_cqp *cqp, {if (cqp_request->waiting) { cqp_request->compl_info.error = true; -WRITE_ONCE (cqp_request->request_done, true); -wake_up (&cqp_request->waitq); +complete_all (&cqp_request->comp); }wait_event_timeout (cqp->remove_wq,refcount_read (&cqp_request->refcnt) ==1 ,1000 );
@@ -608,9 +607,8 @@ static int irdma_wait_event(struct irdma_pci_f *rf, cqp_timeout.compl_cqp_cmds =atomic64_read (&rf->sc_dev.cqp->completed_ops); do { irdma_cqp_ce_handler(rf, &rf->ccq.sc_cq); - if (wait_event_timeout(cqp_request->waitq, - READ_ONCE(cqp_request->request_done), - msecs_to_jiffies(CQP_COMPL_WAIT_TIME_MS))) + if (wait_for_completion_timeout(&cqp_request->comp, + msecs_to_jiffies(CQP_COMPL_WAIT_TIME_MS))) break; if (cqp_request->pending)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 588/1250 RDMA/bnxt_re: Proper rollback if the ioremap fails
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvin Xavier <[email protected]>
[ Upstream commit 87267803a8c824616eb147c5dad7030a5db6f878 ]
bnxt_qplib_alloc_dpi returns success even if ioremap fails. Add the proper rollback when the ioremap fails and return -ENOMEM status.
--- drivers/infiniband/hw/bnxt_re/qplib_res.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/infiniband/hw/bnxt_re/qplib_res.c +11 -0
@@ -721,9 +721,13 @@ int bnxt_qplib_alloc_dpi(struct bnxt_qplib_res *res,break ;case BNXT_QPLIB_DPI_TYPE_WC : dpi->dbr =ioremap_wc (umaddr,PAGE_SIZE ); +if (!dpi->dbr) + goto fail_ioremap;break ;default : dpi->dbr =ioremap (umaddr,PAGE_SIZE ); +if (!dpi->dbr) + goto fail_ioremap;break ; }
@@ -731,6 +735,13 @@ int bnxt_qplib_alloc_dpi(struct bnxt_qplib_res *res,mutex_unlock (&res->dpi_tbl_lock);return 0 ; +fail_ioremap: +/* Roll back the bit we just claimed. */ +set_bit (bit_num, dpit->tbl); + dpit->app_tbl[bit_num] =NULL ; +mutex_unlock (&res->dpi_tbl_lock); +return -ENOMEM ; + } int bnxt_qplib_dealloc_dpi(struct bnxt_qplib_res *res,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 589/1250 bpf: Guard __get_user acesss with access_ok for uprobe_multi data
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Olsa <[email protected]>
[ Upstream commit 4d87a251d45b4a95eb4c0abcfab809c9f231258a ]
As reported by sashiko [1] we need to use access_ok to check the user space data bounds before we use __get-user to get it.
[1] https://lore.kernel.org/bpf/[email protected]/
--- kernel/trace/bpf_trace.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
kernel/trace/bpf_trace.c +11 -0
@@ -3168,6 +3168,7 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prunsigned long __user *uoffsets;u64 __user *ucookies;void __user *upath; +unsigned long size;u32 flags, cnt, i;struct path path;char *name;
@@ -3205,6 +3206,16 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr uref_ctr_offsets =u64_to_user_ptr (attr->link_create.uprobe_multi.ref_ctr_offsets); ucookies =u64_to_user_ptr (attr->link_create.uprobe_multi.cookies); +/* +* All uoffsets/uref_ctr_offsets/ucookies arrays have the same value +* size, we need to check their address range is safe for __get_user +* calls. +*/ + size =sizeof (*uoffsets) * cnt; +if (!access_ok (uoffsets, size) || !access_ok (uref_ctr_offsets, size) || + !access_ok (ucookies, size)) +return -EFAULT ; + name =strndup_user (upath,PATH_MAX );if (IS_ERR (name)) { err =PTR_ERR (name);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 590/1250 bnxt: fix head underflow on XDP head-grow
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joe Damato <[email protected]>
[ Upstream commit e26657fe3b85c068b01f42bb0c602f242d643ba9 ]
The xdp.py test test_xdp_native_adjst_head_grow_data crashes when run on a bnxt machine (and also crashes in NIPA).
It seems that the bug is an underflow in bnxt_rx_multi_page_skb, which builds the skb head:
napi_build_skb(data_ptr - bp->rx_offset, rxr->rx_page_size);
The problem with this expression is that in page mode, rx_offset is:
bp->rx_offset = NET_IP_ALIGN + XDP_PACKET_HEADROOM;
Which evaluates (at least on x86_64) to 258.
The test test_xdp_native_adjst_head_grow_data tests a case where the head is adjusted by -256.
When this test runs, data_ptr is shifted to frag_start + 2 (where frag_start = page_address(page) + offset).
Then, bnxt_rx_multi_page_skb is invoked and the napi_build_skb expression subtracts 258, landing at an address before frag_start. This could be either the previous fragment or the previous physical page when the offset is < 256 (e.g. if the fragment started at offset 0).
When the skb is freed, the page pool fragment reference is dropped on either the wrong page or the wrong frag of the right page. In either case, the corrupted reference count can lead to the page being prematurely recycled while still in use. Once (incorrectly) recycled, it can be handed out again and on driver teardown this would result in a double free.
The commit under fixes updated this code to handle the case where the native page size is >= 64k, but it unintentionally broke the head grow case.
To fix this, add an offset field to struct bnxt_sw_rx_bd, mirroring the existing offset field in struct bnxt_sw_rx_agg_bd. Populate it on allocation and preserve it on reuse.
In bnxt_rx_multi_page_skb, use the newly added offset field to compute the fragment start and pass that to napi_build_skb. Adjust the layout with skb_reserve.
There are two cases, the non-adjustment case and the adjustment case.
In both cases, the skb is built at page_address(page) + offset to account for the case where the native page size >= 64K and skb_reserve is called with data_ptr - (page_address(page) + offset). That difference equals bp->rx_offset when data_ptr was not moved, or bp->rx_offset + xdp_adjust when XDP adjusted the head.
Re-running the failing test with this commit applied causes the test to run successfully to completion.
The other rx_skb_func implementations don't have this issue.
--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 10 ++++++++-- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + 2 files changed, 9 insertions(+), 2 deletions(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +8 -2
@@ -981,6 +981,7 @@ int bnxt_alloc_rx_data(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, mapping += bp->rx_dma_offset; rx_buf->data = page; rx_buf->data_ptr =page_address (page) + offset + bp->rx_offset; + rx_buf->offset = offset; }else {u8 *data =__bnxt_alloc_rx_frag (bp, &mapping, rxr, gfp);
@@ -989,6 +990,7 @@ int bnxt_alloc_rx_data(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, rx_buf->data = data; rx_buf->data_ptr = data + bp->rx_offset; + rx_buf->offset =0 ; } rx_buf->mapping = mapping;
@@ -1010,6 +1012,7 @@ void bnxt_reuse_rx_data(struct bnxt_rx_ring_info *rxr, u16 cons, void *data) prod_rx_buf->data_ptr = cons_rx_buf->data_ptr; prod_rx_buf->mapping = cons_rx_buf->mapping; + prod_rx_buf->offset = cons_rx_buf->offset; prod_bd = &rxr->rx_desc_ring[RX_RING (bp, prod)][RX_IDX (prod)]; cons_bd = &rxr->rx_desc_ring[RX_RING (bp, cons)][RX_IDX (cons)];
@@ -1145,8 +1148,11 @@ static struct sk_buff *bnxt_rx_multi_page_skb(struct bnxt *bp,struct page *page = data;u16 prod = rxr->rx_prod;struct sk_buff *skb; +void *frag_start;int err; + frag_start =page_address (page) + rxr->rx_buf_ring[cons].offset; + err =bnxt_alloc_rx_data (bp, rxr, prod,GFP_ATOMIC );if (unlikely (err)) {bnxt_reuse_rx_data (rxr, cons, data);
@@ -1155,13 +1161,13 @@ static struct sk_buff *bnxt_rx_multi_page_skb(struct bnxt *bp, dma_addr -= bp->rx_dma_offset;dma_sync_single_for_cpu (&bp->pdev->dev, dma_addr, rxr->rx_page_size, bp->rx_dir); - skb =napi_build_skb (data_ptr - bp->rx_offset, rxr->rx_page_size); + skb =napi_build_skb (frag_start, rxr->rx_page_size);if (!skb) {page_pool_recycle_direct (rxr->page_pool, page);return NULL ; }skb_mark_for_recycle (skb); -skb_reserve (skb, bp->rx_offset); +skb_reserve (skb, data_ptr - (u8 *)frag_start);__skb_put (skb, len);return skb;
drivers/net/ethernet/broadcom/bnxt/bnxt.h +1 -0
@@ -902,6 +902,7 @@ struct bnxt_sw_rx_bd {void *data;u8 *data_ptr;dma_addr_t mapping; +unsigned int offset; };struct bnxt_sw_rx_agg_bd {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 591/1250 ipv4: fib: Dont dump dying fib_info in fib_leaf_notify().
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 06b693d2eb6651a63ad85bad8673de3b7d4edd6d ]
syzbot reported use-after-free in nsim_fib4_prepare_event(). [0]
The problem is that the following functions call fib_info_hold() / refcount_inc() while dumping fib_info under RCU, which is unsafe.
* mlxsw_sp_router_fib4_event()
* rocker_router_fib_event()
* nsim_fib4_prepare_event()
refcount_inc_not_zero() must be used, but it would be too late there.
Let's guarantee the lifetime of fib_info in fib_leaf_notify().
Note that IPv6 does not need the corresponding change since fib6_table_dump() holds fib6_table.tb6_lock.
[0]:refcount_t: addition on 0; use-after-free.
WARNING: lib/refcount.c:25 at refcount_warn_saturate+0x9f/0x110 lib/refcount.c:25, CPU#0: kworker/u8:15/3420
Modules linked in:
CPU: 0 UID: 0 PID: 3420 Comm: kworker/u8:15 Not tainted syzkaller #0 PREEMPT_{RT,(full)}
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026
Workqueue: netns cleanup_net
RIP: 0010:refcount_warn_saturate+0x9f/0x110 lib/refcount.c:25
Code: eb 66 85 db 74 3e 83 fb 01 75 4c e8 1b f1 22 fd 48 8d 3d 84 cb f1 0a 67 48 0f b9 3a eb 4a e8 08 f1 22 fd 48 8d 3d 81 cb f1 0a <67> 48 0f b9 3a eb 37 e8 f5 f0 22 fd 48 8d 3d 7e cb f1 0a 67 48 0f
RSP: 0018:ffffc9000f2c7270 EFLAGS: 00010293
RAX: ffffffff84a18858 RBX: 0000000000000002 RCX: ffff888032ff9ec0
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffffffff8f9353e0
RBP: 0000000000000000 R08: ffff888032ff9ec0 R09: 0000000000000005
R10: 0000000000000100 R11: 0000000000000004 R12: ffff8880570cc000
R13: dffffc0000000000 R14: ffff88802b40563c R15: ffff8880570cc000
FS: 0000000000000000(0000) GS:ffff888126173000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fb1f4d5d000 CR3: 000000006072a000 CR4: 00000000003526f0
Call Trace:
<TASK>
__refcount_add include/linux/refcount.h:-1 [inline]
__refcount_inc include/linux/refcount.h:366 [inline]
refcount_inc include/linux/refcount.h:383 [inline]
fib_info_hold include/net/ip_fib.h:629 [inline]
nsim_fib4_prepare_event drivers/net/netdevsim/fib.c:930 [inline]
nsim_fib_event_schedule_work drivers/net/netdevsim/fib.c:1000 [inline]
nsim_fib_event_nb+0x1055/0x1240 drivers/net/netdevsim/fib.c:1043
call_fib_notifier+0x45/0x80 net/core/fib_notifier.c:25
call_fib_entry_notifier net/ipv4/fib_trie.c:90 [inline]
fib_leaf_notify net/ipv4/fib_trie.c:2176 [inline]
fib_table_notify net/ipv4/fib_trie.c:2194 [inline]
fib_notify+0x36b/0x5e0 net/ipv4/fib_trie.c:2217
fib_net_dump net/core/fib_notifier.c:70 [inline]
register_fib_notifier+0x184/0x360 net/core/fib_notifier.c:108
nsim_fib_create+0x85d/0x9f0 drivers/net/netdevsim/fib.c:1596
nsim_dev_reload_create drivers/net/netdevsim/dev.c:1604 [inline]
nsim_dev_reload_up+0x374/0x7c0 drivers/net/netdevsim/dev.c:1058
devlink_reload+0x501/0x8d0 net/devlink/dev.c:475
devlink_pernet_pre_exit+0x1ff/0x420 net/devlink/core.c:558
ops_pre_exit_list net/core/net_namespace.c:161 [inline]
ops_undo_list+0x187/0x940 net/core/net_namespace.c:234
cleanup_net+0x56e/0x800 net/core/net_namespace.c:702
process_one_work kernel/workqueue.c:3314 [inline]
process_scheduled_works+0xb5d/0x1860 kernel/workqueue.c:3397
worker_thread+0xa53/0xfc0 kernel/workqueue.c:3478
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>--- include/net/ip_fib.h | 5 +++++ net/ipv4/fib_trie.c | 4 ++++ 2 files changed, 9 insertions(+)
include/net/ip_fib.h +5 -0
@@ -628,6 +628,11 @@ static inline void fib_info_hold(struct fib_info *fi)refcount_inc (&fi->fib_clntref); } +static inline bool fib_info_hold_safe (struct fib_info *fi) +{ +return refcount_inc_not_zero (&fi->fib_clntref); +} +static inline void fib_info_put (struct fib_info *fi) {if (refcount_dec_and_test (&fi->fib_clntref))
net/ipv4/fib_trie.c +4 -0
@@ -2172,10 +2172,14 @@ static int fib_leaf_notify(struct key_vector *l, struct fib_table *tb,if (fa->fa_slen == last_slen)continue ; +if (!fib_info_hold_safe (fa->fa_info)) +continue ; + last_slen = fa->fa_slen; err =call_fib_entry_notifier (nb,FIB_EVENT_ENTRY_REPLACE , l->key,KEYLENGTH - fa->fa_slen, fa, extack); +fib_info_put (fa->fa_info);if (err)return err; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 592/1250 ASoC: topology: Check PCM and DAI name strings before use
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cássio Gabriel <[email protected]>
[ Upstream commit b7e44d1986d6671342c19b82192189ca5db5dab7 ]
Topology objects store several PCM and DAI names in fixed-size UAPI arrays. Other topology parser paths validate these fields with bounded strnlen() checks before using them as C strings, but the PCM and DAI paths still pass some fixed-size arrays directly to strlen(), devm_kstrdup(), DAI lookup, and diagnostic prints.
A malformed topology blob with a non-NUL-terminated PCM, DAI, or stream capability name can therefore make the parser read past the end of the fixed-size field.
Reject unterminated PCM and DAI name fields before consuming them as C strings.
--- sound/soc/soc-topology.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-)
sound/soc/soc-topology.c +34 -3
@@ -1320,9 +1320,24 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg)return ret; } +static int soc_tplg_check_name (const char *name) +{ +if (strnlen (name,SNDRV_CTL_ELEM_ID_NAME_MAXLEN ) == +SNDRV_CTL_ELEM_ID_NAME_MAXLEN ) +return -EINVAL ; + +return 0 ; +} +static int set_stream_info (struct soc_tplg *tplg,struct snd_soc_pcm_stream *stream,struct snd_soc_tplg_stream_caps *caps) { +int ret; + + ret =soc_tplg_check_name (caps->name); +if (ret) +return ret; + stream->stream_name =devm_kstrdup (tplg->dev, caps->name,GFP_KERNEL );if (!stream->stream_name)return -ENOMEM ;
@@ -1375,7 +1390,11 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,if (dai_drv ==NULL )return -ENOMEM ; -if (strlen (pcm->dai_name)) { + ret =soc_tplg_check_name (pcm->dai_name); +if (ret) + goto err; + +if (pcm->dai_name[0 ]) { dai_drv->name =devm_kstrdup (tplg->dev, pcm->dai_name,GFP_KERNEL );if (!dai_drv->name) { ret = -ENOMEM ;
@@ -1481,7 +1500,11 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,if (tplg->ops) link->dobj.unload = tplg->ops->link_unload; -if (strlen (pcm->pcm_name)) { + ret =soc_tplg_check_name (pcm->pcm_name); +if (ret) + goto err; + +if (pcm->pcm_name[0 ]) { link->name =devm_kstrdup (tplg->dev, pcm->pcm_name,GFP_KERNEL ); link->stream_name =devm_kstrdup (tplg->dev, pcm->pcm_name,GFP_KERNEL );if (!link->name || !link->stream_name) {
@@ -1491,7 +1514,11 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, } link->id =le32_to_cpu (pcm->pcm_id); -if (strlen (pcm->dai_name)) { + ret =soc_tplg_check_name (pcm->dai_name); +if (ret) + goto err; + +if (pcm->dai_name[0 ]) { link->cpus->dai_name =devm_kstrdup (tplg->dev, pcm->dai_name,GFP_KERNEL );if (!link->cpus->dai_name) { ret = -ENOMEM ;
@@ -1843,6 +1870,10 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg,memset (&dai_component,0 ,sizeof (dai_component)); + ret =soc_tplg_check_name (d->dai_name); +if (ret) +return ret; + dai_component.dai_name = d->dai_name; dai =snd_soc_find_dai (&dai_component);if (!dai) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 593/1250 ASoC: meson: aiu: Validate written enum values
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
[ Upstream commit d65adf85477247be04ac86886f8edfaa047b5d4a ]
The AIU HDMI and internal codec mux put callbacks use the written enum value with snd_soc_enum_item_to_val() before checking whether the value is valid for the enumeration.
Reject out-of-range values before converting the enum item, matching the validation already done by the G12A HDMI and internal codec mux controls.
--- sound/soc/meson/aiu-acodec-ctrl.c | 3 +++ sound/soc/meson/aiu-codec-ctrl.c | 3 +++ 2 files changed, 6 insertions(+)
sound/soc/meson/aiu-acodec-ctrl.c +3 -0
@@ -38,6 +38,9 @@ static int aiu_acodec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol,struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;unsigned int mux, changed; +if (ucontrol->value.enumerated.item[0 ] >= e->items) +return -EINVAL ; + mux =snd_soc_enum_item_to_val (e, ucontrol->value.enumerated.item[0 ]); changed = snd_soc_component_test_bits(component, e->reg, CTRL_DIN_LRCLK_SRC,
sound/soc/meson/aiu-codec-ctrl.c +3 -0
@@ -30,6 +30,9 @@ static int aiu_codec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol,struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;unsigned int mux, changed; +if (ucontrol->value.enumerated.item[0 ] >= e->items) +return -EINVAL ; + mux =snd_soc_enum_item_to_val (e, ucontrol->value.enumerated.item[0 ]); changed = snd_soc_component_test_bits(component, e->reg, CTRL_DATA_SEL,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 594/1250 wifi: ath11k: cancel SSR work items during PCI shutdown
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wei Zhang <[email protected]>
[ Upstream commit 8c79aac429b583301f387374ff37c59be671df87 ]
A reboot can crash the kernel if it overlaps with WLAN firmware crash recovery (SSR). The crash is a NULL pointer dereference in the MHI teardown path while freeing DMA-backed MHI contexts.
Simplified trace:
dma_free_attrs
mhi_deinit_dev_ctxt [mhi]
ath11k_pci_power_down [ath11k_pci]
ath11k_pci_shutdown [ath11k_pci]
device_shutdown
kernel_restart
On the host side, SSR is driven by the MHI RDDM callback, which queues reset_work to perform device recovery. reset_work power-cycles the device by calling ath11k_hif_power_down() followed by ath11k_hif_power_up(). The power-down phase deinitializes MHI and frees DMA resources.
Shutdown/reboot runs fully asynchronously with this RDDM-driven SSR recovery flow. As a result, the shutdown path (ath11k_pci_shutdown() -> ath11k_pci_power_down()) can race with the SSR recovery sequence.
Fix this by canceling SSR-related work items during PCI shutdown, marking the device as unregistering, and serializing the RDDM callback path that checks and queues reset_work. This ensures that no new SSR recovery work can be queued once teardown has started, and that any in-flight recovery work is fully synchronized before device power-down, preventing MHI teardown and DMA resource freeing from running more than once.
Note: This issue only affects PCI/MHI-based devices. AHB-based ath11k devices do not queue reset_work in normal SSR flows.
Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04866.5-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1
--- drivers/net/wireless/ath/ath11k/mhi.c | 4 +++- drivers/net/wireless/ath/ath11k/pci.c | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-)
drivers/net/wireless/ath/ath11k/mhi.c +3 -1
@@ -1,7 +1,7 @@// SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. -* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. +* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include <linux/msi.h>
@@ -282,8 +282,10 @@ static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl,break ; } +spin_lock_bh (&ab->base_lock);if (!(test_bit (ATH11K_FLAG_UNREGISTERING , &ab->dev_flags)))queue_work (ab->workqueue_aux, &ab->reset_work); +spin_unlock_bh (&ab->base_lock);break ;default :
drivers/net/wireless/ath/ath11k/pci.c +8 -0
@@ -1214,6 +1214,14 @@ static void ath11k_pci_shutdown(struct pci_dev *pdev)struct ath11k_pci *ab_pci =ath11k_pci_priv (ab);ath11k_pci_set_irq_affinity_hint (ab_pci,NULL ); + +spin_lock_bh (&ab->base_lock); +set_bit (ATH11K_FLAG_UNREGISTERING , &ab->dev_flags); +spin_unlock_bh (&ab->base_lock); + +cancel_work_sync (&ab->reset_work); +cancel_work_sync (&ab->dump_work); +ath11k_pci_power_down (ab, false); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 595/1250 ksmbd: use memcmp() to compare ClientGUIDs
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit e8bb506e6ef749ac0336f3e579d8d02396b7d832 ]
ClientGUID is a fixed-size binary value and can contain embedded NUL bytes. strncmp() stops comparing at the first NUL byte, so different ClientGUID values can incorrectly be treated as equal.
Use memcmp() in SMB3 multichannel session binding and FSCTL_VALIDATE_NEGOTIATE_INFO to compare all SMB2_CLIENT_GUID_SIZE bytes.
--- fs/smb/server/smb2pdu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/smb/server/smb2pdu.c +2 -2
@@ -1752,7 +1752,7 @@ int smb2_sess_setup(struct ksmbd_work *work) goto out_err; } -if (strncmp (conn->ClientGUID, sess->ClientGUID, +if (memcmp (conn->ClientGUID, sess->ClientGUID,SMB2_CLIENT_GUID_SIZE )) { rc = -ENOENT ; goto out_err;
@@ -8170,7 +8170,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, goto err_out; } -if (strncmp (neg_req->Guid, conn->ClientGUID,SMB2_CLIENT_GUID_SIZE )) { +if (memcmp (neg_req->Guid, conn->ClientGUID,SMB2_CLIENT_GUID_SIZE )) { ret = -EINVAL ; goto err_out; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 596/1250 l2tp: fix tunnel and session refcount leak on seq_file release
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 9006c116dd111d457bf5d074990210f70a4ad2c8 ]
In pppol2tp_proc_open() and l2tp_dfs_seq_open(), iteration state (pd->tunnel and pd->session) is kept in seq_file private data to allow iteration across multiple read() system calls.
However, if userspace closes /proc/net/pppol2tp or /sys/kernel/debug/l2tp/tunnels before reading to end-of-file (EOF), any tunnel or session reference stored in pd->tunnel / pd->session is left un-dropped when seq_file private data is freed.
Fix this by dropping any remaining pd->tunnel and pd->session references in pppol2tp_proc_release() and l2tp_dfs_seq_release() when closing the file.
--- net/l2tp/l2tp_debugfs.c | 4 +++ net/l2tp/l2tp_ppp.c | 56 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 3 deletions(-)
net/l2tp/l2tp_debugfs.c +4 -0
@@ -306,6 +306,10 @@ static int l2tp_dfs_seq_release(struct inode *inode, struct file *file) seq = file->private_data; pd = seq->private; +if (pd->session) +l2tp_session_put (pd->session); +if (pd->tunnel) +l2tp_tunnel_put (pd->tunnel);if (pd->net)put_net_track (pd->net, &pd->ns_tracker);kfree (pd);
net/l2tp/l2tp_ppp.c +53 -3
77 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 597/1250 af_unix: Unlink scc_entry in unix_del_edge().
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 594d905195024b228c962627ae5ae7c17bd582a4 ]
Kyle Zeng reported that GC could free a dead SCC partially.
The scenario is as follows:
1) Create two SCCs:
X -. A <-> B
^--'2) Run the following concurrently:
2-1) send() sk-B to sk-B from sk-X
2-2) close() both A and B
At 2-1), there is a small window where unix_add_edges() publishes a new edge (B <-> B) to GC but its skb is not queued by skb_queue_tail().
If 2-2) completes before skb_queue_tail() and GC is triggered, it judges A <-> B as dead, but B is not freed because GC cannot collect the not-yet-queued skb holding the B <-> B edge.
X -. A <-> B -. This edge is visible
^--' ^..' but skb is not
This itself is not a problem since the next GC run will judge B as dead as well and free it finally.
X -. A <.> B -.
^--' ^--'However, X's SCC forces the next GC to call unix_walk_scc_fast(), and it iterates over A through B's scc_entry.
Let's unlink scc_entry before freeing the vertex in unix_del_edge().
--- net/unix/garbage.c | 1 + 1 file changed, 1 insertion(+)
net/unix/garbage.c +1 -0
@@ -186,6 +186,7 @@ static void unix_del_edge(struct scm_fp_list *fpl, struct unix_edge *edge)if (!vertex->out_degree) { edge->predecessor->vertex =NULL ;list_move_tail (&vertex->entry, &fpl->vertices); +list_del (&vertex->scc_entry); } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 598/1250 rtc: cmos: Use platform_get_irq_optional() in cmos_platform_probe()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rafael J. Wysocki <[email protected]>
[ Upstream commit e9f850ba66cdf6b77fb4f005e46c4b605c4de434 ]
The rtc-cmos driver can live without an IRQ and returning an error code from platform_get_irq() is not a problem for it in general, so make it call platform_get_irq_optional() in cmos_platform_probe() instead of platform_get_irq() to avoid a confusing error message printed by the latter if an IRQ cannot be found for index 0, which is possible on x86 platforms.
Additionally, on x86, if the IRQ is not defined and the system has a legacy PIC, hardcode it to RTC_IRQ, which should be safe then (and which is what the dropped PNP code did).
--- drivers/rtc/rtc-cmos.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/rtc/rtc-cmos.c +11 -2
@@ -1508,9 +1508,18 @@ static int __init cmos_platform_probe(struct platform_device *pdev) resource =platform_get_resource (pdev,IORESOURCE_IO ,0 ); else resource =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); - irq =platform_get_irq (pdev,0 ); -if (irq <0 ) + irq =platform_get_irq_optional (pdev,0 ); +if (irq <0 ) { irq =-1 ; +#ifdef CONFIG_X86 +/* +* On some x86 systems, the IRQ is not defined, but it should +* always be safe to hardcode it on systems with a legacy PIC. +*/ +if (nr_legacy_irqs ()) + irq =RTC_IRQ ; +#endif + }return cmos_do_probe (&pdev->dev, resource, irq); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 599/1250 ARM: 9484/1: enable interrupts when unhandled user faults are triggered
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xie Yuanbin <[email protected]>
[ Upstream commit e79ca91165d4fd18549c536abdb86101e889052f ]
PREEMPT_RT requires interrupts to be enabled when sending signals.
When do_DataAbort()/do_PrefetchAbort() triggers unhandled user faults, that is inf->fn() return a non-zero value, and the interrupts are not enabled within the hook function, force_sig_fault() will be called with interrupts disabled.
This can be triggered by user programs executing the bkpt instruction, with kernel config CONFIG_PERF_EVENTS=n.
Enable interrupts in do_DataAbort()/do_PrefetchAbort() when unhandled user faults are triggered to fix the issue.
--- arch/arm/mm/fault.c | 6 ++++++ 1 file changed, 6 insertions(+)
arch/arm/mm/fault.c +6 -0
@@ -649,6 +649,9 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)if (!inf->fn (addr, fsr & ~FSR_LNX_PF , regs))return ; +if (likely (user_mode (regs))) +local_irq_enable (); +pr_alert ("8<--- cut here ---\n" );pr_alert ("Unhandled fault: %s (0x%03x) at 0x%08lx\n" , inf->name, fsr, addr);
@@ -687,6 +690,9 @@ do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)if (!inf->fn (addr, ifsr |FSR_LNX_PF , regs))return ; +if (likely (user_mode (regs))) +local_irq_enable (); +pr_alert ("8<--- cut here ---\n" );pr_alert ("Unhandled prefetch abort: %s (0x%03x) at 0x%08lx\n" , inf->name, ifsr, addr);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 600/1250 ARM: ensure interrupts are enabled in __do_user_fault()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Russell King (Oracle) <[email protected]>
[ Upstream commit 59e4f3b45b96a24fc9b7a89e5f8a2168b30f95af ]
__do_user_fault() may be called from fault handling paths where the interrupts are enabled or disabled. E.g. do_page_fault() calls this with interrupts enabled, whereas do_sect_fault()->do_bad_area() will call this with interrupts disabled. Since this is a userspace fault, we know that interrupts were enabled in the parent context, so call local_irq_enable() here to give a consistent interrupt state.
This is necessary for force_sig_info() when PREEMPT_RT is enabled.
--- arch/arm/mm/fault.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
arch/arm/mm/fault.c +5 -1
@@ -190,7 +190,8 @@ __do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,/* * Something tried to access memory that isn't in our memory map.. -* User mode accesses just cause a SIGSEGV +* User mode accesses just cause a SIGSEGV. Ensure interrupts are enabled +* for preempt RT. */ static void __do_user_fault(unsigned long addr, unsigned int fsr, unsigned int sig,
@@ -198,6 +199,8 @@ __do_user_fault(unsigned long addr, unsigned int fsr, unsigned int sig, {struct task_struct *tsk = current; +local_irq_enable (); +#ifdef CONFIG_DEBUG_USER if (((user_debug &UDBG_SEGV ) && (sig ==SIGSEGV )) || ((user_debug &UDBG_BUS ) && (sig ==SIGBUS ))) {
@@ -272,6 +275,7 @@ do_kernel_address_page_fault(struct mm_struct *mm, unsigned long addr,* should not be faulting in kernel space, which includes the * vector/khelper page. Handle the branch predictor hardening * while interrupts are still disabled, then send a SIGSEGV. +* Note that __do_user_fault() will enable interrupts. */ harden_branch_predictor ();__do_user_fault (addr, fsr,SIGSEGV ,SEGV_MAPERR , regs);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 601/1250 RAS/AMD/ATL, EDAC/amd64: Only load ATL when needed
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yazen Ghannam <[email protected]>
[ Upstream commit 4c3da04827dc01dc1cfc3d03654b7de656c42d80 ]
The AMD Address Translation Library (ATL) will attempt to load on all AMD Zen/SMCA systems.
However, only systems with DRAM ECC enabled will use the library. Other systems will fail to load the library and produce an unnecessary message to the user. More importantly, that thing is dead code loaded and unused.
Remove the ATL module dependency table to prevent autoloading. Request ATL to load from EDAC once all system checks are complete.
[ bp: Massage commit message. ]
--- drivers/edac/amd64_edac.c | 2 ++ drivers/ras/amd/atl/core.c | 1 - 2 files changed, 2 insertions(+), 1 deletion(-)
drivers/edac/amd64_edac.c +2 -0
@@ -4201,6 +4201,8 @@ static int __init amd64_edac_init(void) goto err_pci; } +request_module_nowait ("amd_atl" ); +/* register stuff with EDAC MCE */ if (boot_cpu_data.x86 >=0x17 ) {amd_register_ecc_decoder (decode_umc_error);
drivers/ras/amd/atl/core.c +0 -1
@@ -190,7 +190,6 @@ static const struct x86_cpu_id amd_atl_cpuids[] = { X86_MATCH_FEATURE(X86_FEATURE_ZEN, NULL), { } }; -MODULE_DEVICE_TABLE (x86cpu, amd_atl_cpuids);static int __init amd_atl_init (void ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 602/1250 EDAC/igen6: Fix interleave boundary condition
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qiuxu Zhuo <[email protected]>
[ Upstream commit f4008169bd320eedb9ddf2b39eeb21370ddac278 ]
The address translation logic splits the memory space into interleaved and non-interleaved regions using a boundary at 2 * s_size.
The current check uses '>' and incorrectly classifies the boundary address (2 * s_size) as part of the interleaved region. This leads to incorrect channel/sub-channel selection at the region boundary.
Fix the classification by using '>=' so that the boundary address is handled in the non-interleaved region, matching the hardware layout.
--- drivers/edac/igen6_edac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/edac/igen6_edac.c +1 -1
@@ -730,7 +730,7 @@ static void decode_addr(u64 addr, u32 hash, u64 s_size, int l_map, {int intlv_bit =CHANNEL_HASH_LSB_MASK_BIT (hash) +6 ; -if (addr >2 * s_size) { +if (addr >=2 * s_size) { *sub_addr = addr - s_size; *idx = l_map;return ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 603/1250 EDAC/igen6: Fix channel selection hash
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qiuxu Zhuo <[email protected]>
[ Upstream commit 540b79536f3a89a66c5b6c490110298d43025618 ]
In channel selection hash mode, the hardware decoding logic always includes the channel interleave bit in XOR operations. However, the hash mask may or may not include this channel interleave bit. When the mask does include this bit, the current igen6_edac code performs XOR on the interleave bit twice, effectively ignoring it - which is incorrect.
Fix this issue by ensuring the hash mask always includes the interleave bit, so XOR is performed on the interleave bit exactly once.
--- drivers/edac/igen6_edac.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
drivers/edac/igen6_edac.c +11 -3
@@ -704,14 +704,22 @@ static enum mem_type get_memory_type(u32 mad_inter)static int decode_chan_idx (u64 addr,u64 mask,int intlv_bit) { -u64 hash_addr = addr & mask, hash =0 ; -u64 intlv = (addr >> intlv_bit) &1 ; +u64 hash_addr, hash =0 ;int i; +/* +* In hash mode, the @intlv_bit is the lowest selected bit of @addr +* to be XORed. While @mask may or may not include this @intlv_bit, +* we enforce that @mask includes @intlv_bit to ensure @intlv_bit is +* XORed exactly once. +*/ + mask |=1 << intlv_bit; + hash_addr = addr & mask; +for (i =6 ; i <20 ; i++) hash ^= (hash_addr >> i) &1 ; -return (int )hash ^ intlv; +return (int )hash; }static u64 decode_channel_addr (u64 addr,int intlv_bit)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 604/1250 EDAC/igen6: Fix channel address decode for non-hash mode
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qiuxu Zhuo <[email protected]>
[ Upstream commit 7b348d0d401d478f1923ba20a34a61681d1f7971 ]
In non-hash mode, decode_channel_addr() and channel index extraction used a hardcoded interleave bit position 6 instead of the actual intlv_bit parameter, causing incorrect channel address decoding.
Fix this by using intlv_bit consistently in both hash and non-hash modes.
--- drivers/edac/igen6_edac.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
drivers/edac/igen6_edac.c +5 -6
@@ -744,13 +744,12 @@ static void decode_addr(u64 addr, u32 hash, u64 s_size, int l_map,return ; } -if (CHANNEL_HASH_MODE (hash)) { - *sub_addr =decode_channel_addr (addr, intlv_bit); + *sub_addr =decode_channel_addr (addr, intlv_bit); + +if (CHANNEL_HASH_MODE (hash)) *idx =decode_chan_idx (addr,CHANNEL_HASH_MASK (hash), intlv_bit); - }else { - *sub_addr =decode_channel_addr (addr,6 ); - *idx =GET_BITFIELD (addr,6 ,6 ); - } +else + *idx =GET_BITFIELD (addr, intlv_bit, intlv_bit); }static int igen6_decode (struct decoded_addr *res)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 605/1250 EDAC/device_sysfs: Use kstrtouint() for poll_msec to prevent truncation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jad Keskes <[email protected]>
[ Upstream commit 66cc9dec919dd63d8e4b3d386f7aed3ae684e645 ]
The poll_msec sysfs store file uses simple_strtoul() which accepts an unsigned long, but the target field (poll_msec) is unsigned int. On 64-bit systems, a value > UINT_MAX is silently truncated when stored.
Fix the mismatch by using kstrtouint() instead. This rejects values larger than UINT_MAX at parse time, making truncation impossible. Also add a check for value < 1 to reject the 0-delay case, which would cause the poll work to spin without delay and consume 100% CPU.
--- drivers/edac/edac_device_sysfs.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/edac/edac_device_sysfs.c +9 -2
@@ -90,14 +90,21 @@ static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info *ctl_info, const char *data, size_t count) { -unsigned long value; +unsigned int value; +int ret;/* get the value and enforce that it is non-zero, must be at least * one millisecond for the delay period, between scans * Then cancel last outstanding delay for the work request * and set a new one. */ - value =simple_strtoul (data,NULL ,0 ); + ret =kstrtouint (data,0 , &value); +if (ret <0 ) +return ret; + +if (value <1 ) +return -EINVAL ; +edac_device_reset_delay_period (ctl_info, value);return count;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 606/1250 drm/virtio: Fix a NULL vs ERR_PTR() bug in virtio_gpu_user_framebuffer_create()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dan Carpenter <[email protected]>
[ Upstream commit 94579f24e2b526a04eb41050af0ba018c6f528e7 ]
Smatch complains that returning a NULL here will lead to a NULL pointer dereference in drm_mode_addfb2(). Return an error pointer instead.
--- drivers/gpu/drm/virtio/virtgpu_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/virtio/virtgpu_display.c +1 -1
@@ -319,7 +319,7 @@ virtio_gpu_user_framebuffer_create(struct drm_device *dev,if (ret) {kfree (virtio_gpu_fb);drm_gem_object_put (obj); -return NULL ; +return ERR_PTR (ret); }return &virtio_gpu_fb->base;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 607/1250 drm/virtio: use the DMA API for resource backing on Xen
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Leggett <[email protected]>
[ Upstream commit 6a736d2f9d0c6e6217fe7532bc4c50ceca71db78 ]
On a Xen PV domain page addresses bear no relation to the real machine addresses the host would have to use to reach it. virtio_ring.c handles this correctly, vring_use_map_api() returns true for any xen_domain() regardless of VIRTIO_F_ACCESS_PLATFORM.
virtio-gpu makes the same decision independently, but its copy looks only at the feature bit:
bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
QEMU does not set iommu_platform on virtio-vga by default, so VIRTIO_F_ACCESS_PLATFORM is not negotiated, use_dma_api is false, and virtio_gpu_object_shmem_init() describes the framebuffer's backing pages to the host with sg_phys(). Those are guest-physical addresses. In a PV domain they resolve, on the host side, to pages belonging to some other domain, so the host scans out unrelated memory.
Move the decision into virtio_gpu_use_dma_api() and give it the xen_domain() check, like vring_use_map_api() has. This additionally enables the dma_sync_sgtable_for_device() calls in virtgpu_vq.c, which are required for correctness whenever swiotlb is in play.
Reproduced with a Xen 4.21 PV dom0 nested inside QEMU 8.2 with virtio-vga, on both a distro 6.8 kernel and 6.18 LTS. A PVH dom0 works fine and doesn't need this fix because it is identity-mapped, only PV dom0s are affected.
--- drivers/gpu/drm/virtio/virtgpu_drv.h | 20 ++++++++++++++++++++ drivers/gpu/drm/virtio/virtgpu_object.c | 2 +- drivers/gpu/drm/virtio/virtgpu_vq.c | 6 +++--- 3 files changed, 24 insertions(+), 4 deletions(-)
drivers/gpu/drm/virtio/virtgpu_drv.h +20 -0
@@ -43,6 +43,8 @@#include <drm/drm_probe_helper.h> #include <drm/virtgpu_drm.h> +#include <xen/xen.h> +#define DRIVER_NAME "virtio_gpu"#define DRIVER_DESC "virtio GPU"
@@ -60,6 +62,24 @@/* See virtio_gpu_ctx_create. One additional character for NULL terminator. */ #define DEBUG_NAME_MAX_LEN 65 +/* +* Whether the host must be told about resource backing pages by DMA address +* rather than guest-physical address. +* +* This mirrors vring_use_map_api() in drivers/virtio/virtio_ring.c, including +* its xen_domain() case. +*/ +static inline bool virtio_gpu_use_dma_api (const struct virtio_device *vdev) +{ +if (!virtio_has_dma_quirk (vdev)) +return true; + +if (xen_domain ()) +return true; + +return false; +} +struct virtio_gpu_object_params {unsigned long size;bool dumb;
drivers/gpu/drm/virtio/virtgpu_object.c +1 -1
@@ -161,7 +161,7 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev,struct virtio_gpu_mem_entry **ents, unsigned int *nents) { - bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev); + bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev);struct scatterlist *sg;struct sg_table *pages;int si;
drivers/gpu/drm/virtio/virtgpu_vq.c +3 -3
@@ -723,7 +723,7 @@ int virtio_gpu_panic_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,struct virtio_gpu_object *bo =gem_to_virtio_gpu_obj (objs->objs[0 ]);struct virtio_gpu_transfer_to_host_2d *cmd_p;struct virtio_gpu_vbuffer *vbuf; -bool use_dma_api = !virtio_has_dma_quirk (vgdev->vdev); +bool use_dma_api =virtio_gpu_use_dma_api (vgdev->vdev);if (virtio_gpu_is_shmem (bo) && use_dma_api) dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,
@@ -754,7 +754,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,struct virtio_gpu_object *bo =gem_to_virtio_gpu_obj (objs->objs[0 ]);struct virtio_gpu_transfer_to_host_2d *cmd_p;struct virtio_gpu_vbuffer *vbuf; -bool use_dma_api = !virtio_has_dma_quirk (vgdev->vdev); +bool use_dma_api =virtio_gpu_use_dma_api (vgdev->vdev);if (virtio_gpu_is_shmem (bo) && use_dma_api) dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,
@@ -1190,7 +1190,7 @@ void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev,struct virtio_gpu_object *bo =gem_to_virtio_gpu_obj (objs->objs[0 ]);struct virtio_gpu_transfer_host_3d *cmd_p;struct virtio_gpu_vbuffer *vbuf; -bool use_dma_api = !virtio_has_dma_quirk (vgdev->vdev); +bool use_dma_api =virtio_gpu_use_dma_api (vgdev->vdev);if (virtio_gpu_is_shmem (bo) && use_dma_api) dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 608/1250 accel/qaic: Address potential out-of-bounds read in resp_worker()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Youssef Samir <[email protected]>
[ Upstream commit ab243f74ab4084ca5c8dec608cb5b0deb27db067 ]
Although 'commit 2feec5ae5df7 ("accel/qaic: Handle DBC deactivation if the owner went away")' fixes the scenario it was intended for by walking the message and only decoding QAIC_TRANS_DEACTIVATE_FROM_DEV, if present, it skipped over the bounds checking code that is included in decode_message(). This could lead to issues such as reading past the slab allocation's end, infinite loops or kernel panics. For those issues to happen, a malformed wire message is needed to be sent from the device.
Instead of duplicating the bounds checking code already present in decode_message(), use the function inside resp_worker().
--- drivers/accel/qaic/qaic_control.c | 46 ++++++++++++++++--------------- 1 file changed, 24 insertions(+), 22 deletions(-)
drivers/accel/qaic/qaic_control.c +24 -22
@@ -958,11 +958,13 @@ static int decode_status(struct qaic_device *qdev, void *trans, struct manage_msstatic int decode_message (struct qaic_device *qdev,struct manage_msg *user_msg,struct wire_msg *msg,struct ioctl_resources *resources, -struct qaic_user *usr) +struct qaic_user *usr,bool orphaned_deactivate) { +u32 msg_hdr_count =le32_to_cpu (msg->hdr.count);u32 msg_hdr_len =le32_to_cpu (msg->hdr.len);struct wire_trans_hdr *trans_hdr;u32 msg_len =0 ; +int trans_type;int ret;int i;
@@ -970,10 +972,12 @@ static int decode_message(struct qaic_device *qdev, struct manage_msg *user_msg, msg_hdr_len > QAIC_MANAGE_MAX_MSG_LENGTH)return -EINVAL ; - user_msg->len =0 ; - user_msg->count =le32_to_cpu (msg->hdr.count); +if (user_msg) { + user_msg->len =0 ; + user_msg->count = msg_hdr_count; + } -for (i =0 ; i < user_msg->count; ++i) { +for (i =0 ; i < msg_hdr_count; ++i) {u32 hdr_len;if (msg_len > msg_hdr_len -sizeof (*trans_hdr))
@@ -985,7 +989,20 @@ static int decode_message(struct qaic_device *qdev, struct manage_msg *user_msg, size_add(msg_len, hdr_len) > msg_hdr_len)return -EINVAL ; -switch (le32_to_cpu (trans_hdr->type)) { + trans_type =le32_to_cpu (trans_hdr->type); +/* +* orphaned_deactivate is the case where a deactivate response +* is received from the device after the user owning the DBC, +* and the message requesting deactivation, has gone away. +* In this case, only process QAIC_TRANS_DEACTIVATE_FROM_DEV +* transaction and skip the others. +*/ +if (orphaned_deactivate && trans_type !=QAIC_TRANS_DEACTIVATE_FROM_DEV ) { + msg_len += hdr_len; +continue ; + } + +switch (trans_type) {case QAIC_TRANS_PASSTHROUGH_FROM_DEV : ret =decode_passthrough (qdev, trans_hdr, user_msg, &msg_len);break ;
@@ -1277,7 +1294,7 @@ static int qaic_manage(struct qaic_device *qdev, struct qaic_user *usr, struct m goto dma_cont_failed; } - ret =decode_message (qdev, user_msg, rsp, &resources, usr); + ret =decode_message (qdev, user_msg, rsp, &resources, usr, false); dma_cont_failed:free_dbc_buf (qdev, &resources);
@@ -1442,22 +1459,7 @@ static void resp_worker(struct work_struct *work)* response to the QAIC_TRANS_TERMINATE_TO_DEV transaction, * otherwise, the user can issue an soc_reset to the device. */ -u32 msg_count =le32_to_cpu (msg->hdr.count); -u32 msg_len =le32_to_cpu (msg->hdr.len); -u32 len =0 ; -int j; - -for (j =0 ; j < msg_count && len < msg_len; ++j) { -struct wire_trans_hdr *trans_hdr; - - trans_hdr = (struct wire_trans_hdr *)(msg->data + len); -if (le32_to_cpu (trans_hdr->type) ==QAIC_TRANS_DEACTIVATE_FROM_DEV ) { -if (decode_deactivate (qdev, trans_hdr, &len,NULL )) - len +=le32_to_cpu (trans_hdr->len); - }else { - len +=le32_to_cpu (trans_hdr->len); - } - } +decode_message (qdev,NULL , msg,NULL ,NULL , true);/* request must have timed out, drop packet */ kfree (msg); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 609/1250 nvme-rdma: fix -EIO cleanup order in queue_rq
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xixin Liu <[email protected]>
[ Upstream commit d61828199c6cb4b76d48403c77023cd4bb9d09fc ]
On -EIO, the RDMA queue_rq path reports a host path error and then still cleans up the command and unmaps the SQE DMA. The path error helper completes the request, so that is double cleanup and DMA unmap after the request is already complete.
Unmap the SQE first, then report the host path error. Skip the outer command cleanup on that path.
--- drivers/nvme/host/rdma.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-)
drivers/nvme/host/rdma.c +10 -8
@@ -2001,7 +2001,7 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,struct ib_device *dev;bool queue_ready =test_bit (NVME_RDMA_Q_LIVE , &queue->flags);blk_status_t ret; -int err; +int err =0 ;WARN_ON_ONCE (rq->tag <0 );
@@ -2057,16 +2057,18 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx, err_unmap:nvme_rdma_unmap_data (queue, rq); err: -if (err == -EIO ) - ret =nvme_host_path_error (rq); -else if (err == -ENOMEM || err == -EAGAIN ) - ret =BLK_STS_RESOURCE ; -else - ret =BLK_STS_IOERR ; -nvme_cleanup_cmd (rq); +if (err != -EIO ) { +nvme_cleanup_cmd (rq); +if (err == -ENOMEM || err == -EAGAIN ) + ret =BLK_STS_RESOURCE ; +else + ret =BLK_STS_IOERR ; + } unmap_qe:ib_dma_unmap_single (dev, req->sqe.dma,sizeof (struct nvme_command ),DMA_TO_DEVICE ); +if (err == -EIO ) +return nvme_host_path_error (rq);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 610/1250 nvmet-rdma: fix queue leak when connect backlog is exceeded
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xixin Liu <[email protected]>
[ Upstream commit fb1ed67788e21832b614c23767a088c08cfdd2f2 ]
When pending disconnecting queues exceed the backlog limit, the connect path only drops the device reference and leaks the newly allocated queue and its IB resources.
--- drivers/nvme/target/rdma.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-)
drivers/nvme/target/rdma.c +7 -8
@@ -1628,19 +1628,13 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id,mutex_unlock (&nvmet_rdma_queue_mutex);if (pending >NVMET_RDMA_BACKLOG ) { ret =NVME_SC_CONNECT_CTRL_BUSY ; - goto put_device; + goto free_queue; } } ret =nvmet_rdma_cm_accept (cm_id, queue, &event->param.conn); -if (ret) { -/* -* Don't destroy the cm_id in free path, as we implicitly -* destroy the cm_id here with non-zero ret code. -*/ - queue->cm_id =NULL ; +if (ret) goto free_queue; - }mutex_lock (&nvmet_rdma_queue_mutex);list_add_tail (&queue->queue_list, &nvmet_rdma_queue_list);
@@ -1649,6 +1643,11 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id,return 0 ; free_queue: +/* +* Don't destroy the cm_id in free path, as we implicitly +* destroy the cm_id here with non-zero ret code. +*/ + queue->cm_id =NULL ;nvmet_rdma_free_queue (queue); put_device:kref_put (&ndev->ref, nvmet_rdma_free_dev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 611/1250 sched_ext: Fix nonexistent field in sched-ext.rst example
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Liang Luo <[email protected]>
[ Upstream commit 4fb8d6379d2c7ceecb2b3e111954d29089d59492 ]
The ops.exit() example in sched-ext.rst reads ei->type, but struct scx_exit_info has never had a type field - the exit reason is exposed as ei->kind since the struct was introduced. A scheduler written following the example fails to compile with
error: no member named 'type' in 'struct scx_exit_info'
Use ei->kind.
--- Documentation/scheduler/sched-ext.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Documentation/scheduler/sched-ext.rst +1 -1
@@ -181,7 +181,7 @@ optional. The following modified excerpt is from void BPF_STRUCT_OPS(simple_exit, struct scx_exit_info *ei) { - exit_type = ei->type; + exit_type = ei->kind; } SEC(".struct_ops")
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 612/1250 selftests/cgroup: Fix cg_run_in_subcgroups ignoring arg parameter
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hongfu Li <[email protected]>
[ Upstream commit a8c6daab4b0e276508b7ffdd66c60fd3020a9178 ]
cg_run_in_subcgroups() discards its arg and always passes NULL to cg_run(), turning the (void *)100 from test_kmem_dead_cgroups() into NULL so no allocation occurs.
This makes test_kmem_dead_cgroups() falsely pass without exercising the "dying cgroup with charged slab" scenario it intends to test.
Pass the arg through to cg_run() to fix this.
--- tools/testing/selftests/cgroup/test_kmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/cgroup/test_kmem.c +1 -1
@@ -144,7 +144,7 @@ static int cg_run_in_subcgroups(const char *parent,return -1 ; } -if (cg_run (child, fn,NULL )) { +if (cg_run (child, fn, arg)) {cg_destroy (child);free (child);return -1 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 613/1250 bpf: Fix REG INVARIANTS VIOLATION on speculative pointer arithmetic
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 150aeba624e8b7cac51c39440d7e8e1fd11de9a0 ]
Take the following unprivileged program as an example:
r0 = bpf_map_lookup_elem(...) /* PTR_TO_MAP_VALUE, offset 0 */ ... 14: r0 += r1 /* r1 is a bounded scalar */ 15: r9 = r0
Loading it triggers a verifier warning from reg_bounds_sanity_check():
verifier bug: REG INVARIANTS VIOLATION (alu): const subreg tnum out
of sync with range bounds r64={.base=0x0, .size=0x0}
r32={.base=0x0, .size=0xffffffff} var_off=(0x0, 0x0)What happens:
1. Processing insn 14 (r0 += r1) in adjust_ptr_min_max_vals(), the new offset is computed into dst_reg's var_off and 32/64-bit ranges.
2. Because pointer registers do not track 32-bit subregister bounds, __mark_reg32_unbounded() first sets r32 to the full range; r32 is re-derived from the offset at the end of the function by reg_bounds_sync().
3. On the unprivileged path, sanitize_ptr_alu() is called and, via sanitize_speculative_path() -> push_stack(), snapshots the current register state and schedules the next instruction (insn 15) to be verified directly as a speculative path.
4. That snapshot is taken between step 2 and the final reg_bounds_sync():
at this point dst_reg's var_off still holds the (const) original offset while r32 has just been blanked to the full range, i.e. the two are out of sync. When the speculative path later verifies insn 15 (r9 = r0), the inconsistent state reaches reg_bounds_sanity_check() and trips the warning.
var_off and the 32-bit range must always be consistent. There are two ways to keep the snapshot consistent:
1. sync var_off and r32 before the snapshot so they match, or
2. leave r32 at its original (already consistent) value and blank it only after the snapshot.
The whole point of sanitize_ptr_alu() is to insert a harmless masking sequence that keeps the access in bounds under speculation, so the state it snapshots should faithfully represent that. Take approach 2: move __mark_reg32_unbounded() to after sanitize_ptr_alu(), so the speculative snapshot keeps the pointer's original, consistent r32. The non-speculative path is unchanged: r32 is still blanked before the offset is applied and re-derived by reg_bounds_sync().
--- kernel/bpf/verifier.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
kernel/bpf/verifier.c +8 -3
@@ -14805,9 +14805,6 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, !check_reg_sane_offset_ptr(env, ptr_reg, ptr_reg->type))return -EINVAL ; -/* pointer types do not carry 32-bit bounds at the moment. */ -__mark_reg32_unbounded (dst_reg); -if (sanitize_needed (opcode)) { ret =sanitize_ptr_alu (env, insn, ptr_reg, off_reg, dst_reg, &info, false);
@@ -14815,6 +14812,14 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,return sanitize_err (env, insn, ret, off_reg, dst_reg); } +/* +* Pointer types do not carry 32-bit bounds at the moment. Blank r32 +* only after sanitize_ptr_alu() may have snapshotted dst_reg into a +* speculative path: otherwise reg_bounds_sanity_check() might hit some +* constraints violations. +*/ +__mark_reg32_unbounded (dst_reg); +switch (opcode) {case BPF_ADD :/* We can take a fixed offset as long as it doesn't overflow
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 614/1250 drm/gud: validate GUD_ROTATION_0 is present in supported rotations
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sajal Gupta <[email protected]>
[ Upstream commit cb732d027aa18e1fcf9d2797f47d20b179ebc59c ]
The rotation argument to drm_plane_create_rotation_property() is set to DRM_MODE_ROTATE_0, and the device reported rotation bitmask is used as the supported_rotations argument. The driver never validates that GUD_ROTATION_0 is present, so a device that omits it from its GUD_PROPERTY_ROTATION triggers theWARN_ON(rotation & ~supported_rotations) in drm_plane_create_rotation_property()
Fix this by skipping the creation of rotation property if the device doesn't have the GUD_ROTATION_0 bit
--- drivers/gpu/drm/gud/gud_drv.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/gpu/drm/gud/gud_drv.c +2 -0
@@ -289,6 +289,8 @@ static int gud_plane_add_properties(struct gud_device *gdrm)* but mask out any additions on future devices. */ val &=GUD_ROTATION_MASK ; +if (!(val &GUD_ROTATION_0 )) +continue ; ret =drm_plane_create_rotation_property (&gdrm->plane,DRM_MODE_ROTATE_0 , val);break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 615/1250 accel/amdxdna: Remove __counted_by from struct amdxdna_cmd_chain
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lizhi Hou <[email protected]>
[ Upstream commit b3709d354545e70388177500761f92d906c4dfd6 ]
struct amdxdna_cmd_chain contains a flexible array annotated with __counted_by(command_count). Since the structure is stored in shared AMDXDNA_BO_SHARE memory, userspace can modify command_count concurrently. If command_count is changed to zero, the bounds check generated from __counted_by may fail and trigger a kernel panic.
Remove __counted_by to avoid relying on the userspace-controlled command_count for the flexible array bounds check.
--- drivers/accel/amdxdna/amdxdna_ctx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/accel/amdxdna/amdxdna_ctx.h +1 -1
@@ -51,7 +51,7 @@ struct amdxdna_cmd_chain {u32 submit_index;u32 error_index;u32 reserved[3 ]; - u64 data[] __counted_by(command_count); +u64 data[]; };/* Exec buffer command header format */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 616/1250 ufs: do not treat unreadable directory blocks as empty
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ali Ahmet Memis <[email protected]>
[ Upstream commit 08edfb34ee9ca54383970c65ed3a6013e84f5e16 ]
ufs_empty_dir() scans every directory block to decide whether a directory is empty before rmdir() removes it. When ufs_get_folio() cannot read or validate a block it returns an error pointer, and the loop currently skips that block with continue and keeps scanning the remaining blocks.
If none of the readable blocks hold an entry, the function returns 1 and the caller unlinks the directory. A directory whose contents live in a block that cannot be read, for example because of an I/O error or corrupted directory metadata, is therefore seen as empty and removed, losing the entries it still holds.
Follow the ext2 behaviour and treat an unreadable block as a reason to consider the directory not empty, so rmdir() fails instead of discarding data that could not be verified.
--- fs/ufs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/ufs/dir.c +1 -1
@@ -589,7 +589,7 @@ int ufs_empty_dir(struct inode * inode) kaddr =ufs_get_folio (inode, i, &folio);if (IS_ERR (kaddr)) -continue ; +return 0 ; de = (struct ufs_dir_entry *)kaddr; kaddr +=ufs_last_byte (inode, i) -UFS_DIR_REC_LEN (1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 617/1250 drm/cirrus-qemu: Validate BAR0 size during probe
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Slawomir Stepien <[email protected]>
[ Upstream commit 92312d333bf700798f92f30406c721bce87506f3 ]
The cirrus-qemu driver relies on CIRRUS_VRAM_SIZE (4 MB) to validate framebuffer sizes. However, during PCI probe, the driver mapped BAR0 without verifying that its size matches CIRRUS_VRAM_SIZE.
If a PCI device with a BAR0 smaller than 4 MB is bound to the driver, the mapped VRAM will be smaller than expected. Because validation checks assume 4 MB VRAM, framebuffers larger than the mapped memory can be created.
When the display plane is updated (e.g. during release), cirrus_primary_plane_helper_atomic_update() copies the framebuffer to VRAM using drm_fb_memcpy(). Writing past the end of the mapped I/O memory causes a supervisor write page fault:
BUG: unable to handle page fault for address: ffffc9000389c000 ... RIP: 0010:memcpy_toio+0x7c/0xe0 arch/x86/lib/iomem.c:110 ... Call Trace: <TASK> iosys_map_memcpy_to include/linux/iosys-map.h:285 [inline] drm_fb_memcpy+0x325/0x5d0 drivers/gpu/drm/drm_format_helper.c:442 cirrus_primary_plane_helper_atomic_update+0x98a/0xb00 drivers/gpu/drm/tiny/cirrus-qemu.c:358 drm_atomic_helper_commit_planes+0x626/0xea0 drivers/gpu/drm/drm_atomic_helper.c:3038 drm_atomic_helper_commit_tail+0x60/0x510 drivers/gpu/drm/drm_atomic_helper.c:1989 commit_tail+0x2b1/0x3c0 drivers/gpu/drm/drm_atomic_helper.c:2074 drm_atomic_helper_commit+0xa77/0xb10 drivers/gpu/drm/drm_atomic_helper.c:2312
Fix this by validating in cirrus_pci_probe() that the PCI BAR0 resource is not less than CIRRUS_VRAM_SIZE, returning -ENODEV if it is less.
--- drivers/gpu/drm/tiny/cirrus-qemu.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/gpu/drm/tiny/cirrus-qemu.c +3 -0
@@ -570,6 +570,9 @@ static int cirrus_pci_probe(struct pci_dev *pdev,struct cirrus_device *cirrus;int ret; +if (pci_resource_len (pdev,0 ) <CIRRUS_VRAM_SIZE ) +return -ENODEV ; + ret =aperture_remove_conflicting_pci_devices (pdev, cirrus_driver.name);if (ret)return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 618/1250 net: icmp: avoid invalid transport header access in icmp_send tracepoint
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 7fcc2fe39fed1cb98a7374a113ff3800e8f9af80 ]
syzbot reported a WARNING triggered by DEBUG_NET_WARN_ON_ONCE():
WARNING: at skb_transport_header include/linux/skbuff.h:3087 [inline] WARNING: at udp_hdr include/linux/udp.h:23 [inline] WARNING: at do_trace_event_raw_event_icmp_send include/trace/events/icmp.h:30 [inline] WARNING: at trace_event_raw_event_icmp_send+0x48c/0x6ec include/trace/events/icmp.h:11 Call trace: skb_transport_header include/linux/skbuff.h:3087 [inline] udp_hdr include/linux/udp.h:23 [inline] do_trace_event_raw_event_icmp_send include/trace/events/icmp.h:30 [inline] trace_event_raw_event_icmp_send+0x48c/0x6ec include/trace/events/icmp.h:11 __traceiter_icmp_send include/trace/events/icmp.h:11 [inline] __do_trace_icmp_send include/trace/events/icmp.h:11 [inline] trace_icmp_send+0x320/0x49c include/trace/events/icmp.h:11 __icmp_send+0xcfc/0x11d8 net/ipv4/icmp.c:1013 ipv4_send_dest_unreach net/ipv4/route.c:1280 [inline] ipv4_link_failure+0x57c/0x8dc net/ipv4/route.c:1287 dst_link_failure include/net/dst.h:438 [inline] vti_tunnel_xmit+0xe40/0x17a4 net/ipv4/ip_vti.c:307
TP_fast_assign() unconditionally calls udp_hdr(skb) before checking whether the packet is UDP. Furthermore, __icmp_send() can be invoked from paths (e.g., link failures, ARP errors, forwarding, AF_PACKET) where skb->transport_header was never initialized (~0U).
Under CONFIG_DEBUG_NET=y, calling skb_transport_header(skb) triggers DEBUG_NET_WARN_ON_ONCE(!skb_transport_header_was_set(skb)).
Fix this by:
1. Only parsing transport info when iph->protocol == IPPROTO_UDP.
2. Using skb_header_pointer() at skb_network_offset(skb) + (iph->ihl << 2) to safely fetch the UDP header without assuming transport_header is set.
--- include/trace/events/icmp.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
include/trace/events/icmp.h +8 -5
@@ -27,17 +27,20 @@ TRACE_EVENT(icmp_send,TP_fast_assign ( struct iphdr *iph = ip_hdr(skb); -struct udphdr *uh =udp_hdr (skb); -int proto_4 = iph->protocol; +struct udphdr _uh, *uh =NULL ;__be32 *p32; __entry->skbaddr = skb; __entry->type = type; __entry->code = code; -if (proto_4 !=IPPROTO_UDP || (u8 *)uh < skb->head || - (u8 *)uh +sizeof (struct udphdr ) - >skb_tail_pointer (skb)) { +if (iph->protocol ==IPPROTO_UDP ) + uh =skb_header_pointer (skb, +skb_network_offset (skb) + + (iph->ihl <<2 ), +sizeof (_uh), &_uh); + +if (!uh) { __entry->sport =0 ; __entry->dport =0 ; __entry->ulen =0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 619/1250 tcp: use GFP_ATOMIC in tcp_send_active_reset()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 18666c73afe95eeca8707c699b63f96ce3acda42 ]
tcp_send_active_reset() can be called from contexts where gfp_any() (in tcp_disconnect()) or sk->sk_allocation (in __tcp_close() and mptcp_do_fastclose()) evaluates to GFP_KERNEL, which includes __GFP_FS and __GFP_DIRECT_RECLAIM.
Allocating with GFP_KERNEL while holding the socket lock (sk_lock) creates a lockdep dependency:
sk_lock -> fs_reclaim
This causes false-positive lockdep circular locking warnings with storage subsystems (such as nvme-tcp) that acquire socket locks in block I/O paths and invoke tcp_disconnect() or close sockets upon teardown:
set->srcu -> sk_lock -> fs_reclaim -> elevator_lock -> set->srcu
Active resets are small RST packet headers that should never enter direct reclaim or block while holding socket locks.
Use sk_gfp_mask(sk, GFP_ATOMIC | __GFP_NOWARN) inside tcp_send_active_reset() and remove its priority argument. This preserves __GFP_MEMALLOC access for SOCK_MEMALLOC sockets, suppresses allocation failure warnings, and aligns with other control packet allocations (e.g. tcp_send_fin(), __tcp_send_ack(), tcp_xmit_probe_skb()).
--- include/net/tcp.h | 3 +-- net/ipv4/tcp.c | 14 ++++++-------- net/ipv4/tcp_output.c | 4 ++-- net/ipv4/tcp_timer.c | 6 +++--- net/mptcp/protocol.c | 3 +-- net/mptcp/protocol.h | 2 +- 6 files changed, 14 insertions(+), 18 deletions(-)
include/net/tcp.h +1 -2
@@ -731,8 +731,7 @@ int tcp_fragment(struct sock *sk, enum tcp_queue tcp_queue,void tcp_send_probe0 (struct sock *);int tcp_write_wakeup (struct sock *,int mib);void tcp_send_fin (struct sock *sk); -void tcp_send_active_reset (struct sock *sk,gfp_t priority, -enum sk_rst_reason reason); +void tcp_send_active_reset (struct sock *sk,enum sk_rst_reason reason);int tcp_send_synack (struct sock *);void tcp_push_one (struct sock *,unsigned int mss_now);void __tcp_send_ack (struct sock *sk,u32 rcv_nxt,u16 flags);
net/ipv4/tcp.c +6 -8
@@ -3166,8 +3166,7 @@ void __tcp_close(struct sock *sk, long timeout)/* Unread data was tossed, zap the connection. */ NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPABORTONCLOSE );tcp_set_state (sk,TCP_CLOSE ); -tcp_send_active_reset (sk, sk->sk_allocation, -SK_RST_REASON_TCP_ABORT_ON_CLOSE ); +tcp_send_active_reset (sk,SK_RST_REASON_TCP_ABORT_ON_CLOSE ); }else if (sock_flag (sk,SOCK_LINGER ) && !sk->sk_lingertime) {/* Check zero linger _after_ checking for unread data. */ sk->sk_prot->disconnect (sk,0 );
@@ -3241,7 +3240,7 @@ void __tcp_close(struct sock *sk, long timeout)struct tcp_sock *tp =tcp_sk (sk);if (READ_ONCE (tp->linger2) <0 ) {tcp_set_state (sk,TCP_CLOSE ); -tcp_send_active_reset (sk,GFP_ATOMIC , +tcp_send_active_reset (sk,SK_RST_REASON_TCP_ABORT_ON_LINGER );__NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPABORTONLINGER );
@@ -3260,7 +3259,7 @@ void __tcp_close(struct sock *sk, long timeout)if (sk->sk_state !=TCP_CLOSE ) {if (tcp_check_oom (sk,0 )) {tcp_set_state (sk,TCP_CLOSE ); -tcp_send_active_reset (sk,GFP_ATOMIC , +tcp_send_active_reset (sk,SK_RST_REASON_TCP_ABORT_ON_MEMORY );__NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPABORTONMEMORY );
@@ -3361,14 +3360,14 @@ int tcp_disconnect(struct sock *sk, int flags) }else if (unlikely (tp->repair)) {WRITE_ONCE (sk->sk_err,ECONNABORTED ); }else if (tcp_need_reset (old_state)) { -tcp_send_active_reset (sk,gfp_any (),SK_RST_REASON_TCP_STATE ); +tcp_send_active_reset (sk,SK_RST_REASON_TCP_STATE );WRITE_ONCE (sk->sk_err,ECONNRESET ); }else if (tp->snd_nxt != tp->write_seq && (1 << old_state) & (TCPF_CLOSING |TCPF_LAST_ACK )) {/* The last check adjusts for discrepancy of Linux wrt. RFC * states */ -tcp_send_active_reset (sk,gfp_any (), +tcp_send_active_reset (sk,SK_RST_REASON_TCP_DISCONNECT_WITH_DATA );WRITE_ONCE (sk->sk_err,ECONNRESET ); }else if (old_state ==TCP_SYN_SENT )
@@ -5088,8 +5087,7 @@ int tcp_abort(struct sock *sk, int err)bh_lock_sock (sk);if (tcp_need_reset (sk->sk_state)) -tcp_send_active_reset (sk,GFP_ATOMIC , -SK_RST_REASON_TCP_STATE ); +tcp_send_active_reset (sk,SK_RST_REASON_TCP_STATE );tcp_done_with_error (sk, err);bh_unlock_sock (sk);
net/ipv4/tcp_output.c +2 -2
@@ -3784,9 +3784,9 @@ void tcp_send_fin(struct sock *sk)* was unread data in the receive queue. This behavior is recommended * by RFC 2525, section 2.17. -DaveM */ -void tcp_send_active_reset (struct sock *sk,gfp_t priority, -enum sk_rst_reason reason) +void tcp_send_active_reset (struct sock *sk,enum sk_rst_reason reason) { +gfp_t priority =sk_gfp_mask (sk,GFP_ATOMIC | __GFP_NOWARN);struct sk_buff *skb;TCP_INC_STATS (sock_net (sk),TCP_MIB_OUTRSTS );
net/ipv4/tcp_timer.c +3 -3
@@ -125,7 +125,7 @@ static int tcp_out_of_resources(struct sock *sk, bool do_reset) (!tp->snd_wnd && !tp->packets_out)) do_reset = true;if (do_reset) -tcp_send_active_reset (sk,GFP_ATOMIC , +tcp_send_active_reset (sk,SK_RST_REASON_TCP_ABORT_ON_MEMORY );tcp_done (sk);__NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPABORTONMEMORY );
@@ -809,7 +809,7 @@ static void tcp_keepalive_timer(struct timer_list *t) goto out; } } -tcp_send_active_reset (sk,GFP_ATOMIC ,SK_RST_REASON_TCP_STATE ); +tcp_send_active_reset (sk,SK_RST_REASON_TCP_STATE ); goto death; }
@@ -836,7 +836,7 @@ static void tcp_keepalive_timer(struct timer_list *t) icsk->icsk_probes_out > 0) || (user_timeout == 0 && icsk->icsk_probes_out >= keepalive_probes(tp))) { -tcp_send_active_reset (sk,GFP_ATOMIC , +tcp_send_active_reset (sk,SK_RST_REASON_TCP_KEEPALIVE_TIMEOUT );tcp_write_err (sk); goto out;
net/mptcp/protocol.c +1 -2
@@ -2851,8 +2851,7 @@ static void mptcp_do_fastclose(struct sock *sk)*/ inet_csk (ssk)->icsk_ack.rcv_mss =TCP_MIN_MSS ; -tcp_send_active_reset (ssk, ssk->sk_allocation, -SK_RST_REASON_TCP_ABORT_ON_CLOSE ); +tcp_send_active_reset (ssk,SK_RST_REASON_TCP_ABORT_ON_CLOSE ); unlock:release_sock (ssk); }
net/mptcp/protocol.h +1 -1
@@ -647,7 +647,7 @@ mptcp_send_active_reset_reason(struct sock *sk)enum sk_rst_reason reason; reason =sk_rst_convert_mptcp_reason (subflow->reset_reason); -tcp_send_active_reset (sk,GFP_ATOMIC , reason); +tcp_send_active_reset (sk, reason); }/* Made the fwd mem carried by the given skb available to the msk,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 620/1250 net: iptunnel: fix stale transport header during tunnel decapsulation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dong Chenchen <[email protected]>
[ Upstream commit 28a57fb2c5df4deb42a06e52fd36c14b37aa0034 ]
Syzbot reported a crash in qdisc_pkt_len_segs_init() caused by a stale transport_header offset after tunnel decapsulation.
BUG: unable to handle page fault for address: ffffed102091a42e Oops: Oops: 0000 [#1] SMP KASAN NOPTI CPU: 0 UID: 0 PID: 340 Comm: qdisc_uaf_repro Not tainted 7.2.0-rc4-00061-g248951ddc14d #256 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 RIP: 0010:__asan_load2 <IRQ>
qdisc_pkt_len_segs_init (net/core/dev.c:4145) __dev_queue_xmit (net/core/dev.c:4787) br_dev_queue_push_xmit (net/bridge/br_forward.c:53) br_handle_frame_finish (net/bridge/br_input.c:229) br_handle_frame (net/bridge/br_input.c:315) __netif_receive_skb_core.constprop.0 (net/core/dev.c:6099) __netif_receive_skb_list_core (net/core/dev.c:6287) netif_receive_skb_list_internal (net/core/dev.c:6445) napi_complete_done (net/core/dev.c:6813) gro_cell_poll (net/core/gro_cells.c:74) __napi_poll (net/core/dev.c:7735)net_rx_action (net/core/dev.c:7798 net/core/dev.c:7955) handle_softirqs (kernel/softirq.c:622)do_softirq (kernel/softirq.c:523 kernel/softirq.c:510 ) __local_bh_enable_ip (kernel/softirq.c:450) tun_get_user (drivers/net/tun.c:1986 (discriminator 1)) tun_chr_write_iter (drivers/net/tun.c:2032)
The issue is completely latent until qdisc read transport header in commit 7fb4c1967011 ("net: pull headers in qdisc_pkt_len_segs_init()"). The crash requires four conditions to line up:
1. The incoming packet is encapsulated and carries GSO metadata. The outer transport header offset is stored in skb->transport_header while the packet is still in the outer tunnel context.
2. The tunnel receiver strips the outer headers. skb->data is advanced to the inner frame, but skb->transport_header is left pointing to the now-removed outer L4 header, so it becomes a negative offset relative to the new data.
3. The inner frame is not delivered to the local IP stack. Instead, it is forwarded at L2 by a bridge or HSR, so ip_rcv_core() never runs and the transport header is not reset to the inner L4 offset.
4. The forwarding path calls __dev_queue_xmit(), which enters qdisc_pkt_len_segs_init(). That function computes the GSO header length from skb_transport_offset(skb). Because the offset is negative, the unsigned cast overflows and pskb_may_pull(skb, hdr_len + sizeof(struct tcphdr)) reads past the end of the skb, triggering a KASAN fault or page fault.
The issue specifically requires GSO packets (shinfo->gso_size != 0), which are processed/aggregated through gro_cells. Fix this by clearing transport_header to the ~0U sentinel in gro_cell for all tunnnel driver. GTP does not support GRO/GSO, drop the evil GSO packets in GTP directly.
--- drivers/net/gtp.c | 5 +++++ include/linux/skbuff.h | 5 +++++ net/core/gro_cells.c | 2 ++ 3 files changed, 12 insertions(+)
drivers/net/gtp.c +5 -0
@@ -318,6 +318,11 @@ static int gtp_inner_proto(struct sk_buff *skb, unsigned int hdrlen,static int gtp_rx (struct pdp_ctx *pctx,struct sk_buff *skb,unsigned int hdrlen,unsigned int role,__u16 inner_proto) { +if (skb_is_gso (skb)) { +netdev_dbg (pctx->dev,"GSO is not supported in GTP\n" ); + goto err; + } +if (!gtp_check_ms (skb, pctx, hdrlen, role, inner_proto)) {netdev_dbg (pctx->dev,"No PDP ctx for this MS\n" );return 1 ;
include/linux/skbuff.h +5 -0
@@ -3066,6 +3066,11 @@ static inline bool skb_transport_header_was_set(const struct sk_buff *skb) return skb->transport_header != (typeof(skb->transport_header))~0U; } +static inline void skb_unset_transport_header (struct sk_buff *skb) +{ + skb->transport_header = (typeof(skb->transport_header))~0U; +} +static inline unsigned char *skb_transport_header (const struct sk_buff *skb) {DEBUG_NET_WARN_ON_ONCE (!skb_transport_header_was_set (skb));
net/core/gro_cells.c +2 -0
@@ -22,6 +22,8 @@ int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb)if (unlikely (!(dev->flags &IFF_UP ))) goto drop; +skb_unset_transport_header (skb); +if (!gcells->cells ||skb_cloned (skb) ||netif_elide_gro (dev)) { res =netif_rx (skb); goto unlock;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 621/1250 net/sched: act_api: budget all shared attributes in notify skbs
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Victor Nogueira <[email protected]>
[ Upstream commit 13eb543cebef6d6c3ec42e31afe3856f51b7126b ]
tcf_action_shared_attrs_size() is supposed to return an upper bound on the netlink attributes every action dump emits outside of TCA_ACT_OPTIONS, so that tcf_add_notify_msg(), tcf_del_notify_msg() and friends can allocate an skb large enough for the reply. It has fallen behind the dump path and is now an underestimate for every single action.
Attributes, such as, TCA_ACT_IN_HW_COUNT and TCA_STATS_BASIC_HW are emitted unconditionally and never accounted for. TCA_STATS_PKT64, TCA_ACT_USED_HW_STATS, TCA_STATS_RATE_EST, TCA_STATS_RATE_EST64 require specific conditions, but are also not accounted for.
Fix the issue by budgeting all of them so that we have a legitimate upper bound. Even tough for of them require specific conditions, they are cheap so, to avoid overcomplicating, we opted to account for them unconditionally as well to account for a real worst case scenario.
--- net/sched/act_api.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
net/sched/act_api.c +11 -2
@@ -443,12 +443,21 @@ static size_t tcf_action_shared_attrs_size(const struct tc_action *act) +nla_total_size (IFNAMSIZ )/* TCA_ACT_KIND */ + cookie_len/* TCA_ACT_COOKIE */ +nla_total_size (sizeof (struct nla_bitfield32 ))/* TCA_ACT_HW_STATS */ +/* TCA_ACT_USED_HW_STATS */ + +nla_total_size (sizeof (struct nla_bitfield32 )) + +nla_total_size (sizeof (u32))/* TCA_ACT_IN_HW_COUNT */ +nla_total_size (0 )/* TCA_ACT_STATS nested */ +nla_total_size (sizeof (struct nla_bitfield32 ))/* TCA_ACT_FLAGS */ /* TCA_STATS_BASIC */ +nla_total_size_64bit (sizeof (struct gnet_stats_basic )) -/* TCA_STATS_PKT64 */ - +nla_total_size_64bit (sizeof (u64)) +/* TCA_STATS_BASIC_HW */ + +nla_total_size_64bit (sizeof (struct gnet_stats_basic )) +/* TCA_STATS_PKT64, emitted by both of the basic copies above */ + +2 *nla_total_size_64bit (sizeof (u64)) +/* TCA_STATS_RATE_EST */ + +nla_total_size_64bit (sizeof (struct gnet_stats_rate_est )) +/* TCA_STATS_RATE_EST64 */ + +nla_total_size_64bit (sizeof (struct gnet_stats_rate_est64 ))/* TCA_STATS_QUEUE */ +nla_total_size_64bit (sizeof (struct gnet_stats_queue )) +nla_total_size (0 )/* TCA_ACT_OPTIONS nested */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 622/1250 net/sched: act_api: size the RTM_GETACTION reply from the actions
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Victor Nogueira <[email protected]>
[ Upstream commit e9ca46ebc3262b498626c4095826b8fa034bbf21 ]
tca_action_gd() already walks every requested action and accumulates attr_size += tcf_action_fill_size(act), then wraps the result in tcf_action_full_attrs_size(). For RTM_DELACTION that value is handed to tcf_del_notify_msg(), which allocates max(attr_size, NLMSG_GOODSIZE). For RTM_GETACTION it is silently discarded and tcf_get_notify() allocates a fixed NLMSG_GOODSIZE skb instead.
Any action whose dump exceeds that fixed budget therefore cannot be read back. For example, act_pedit overruns the budget with 32 actions of four munge keys each, act_police with 32 policers once the optional rate/peakrate/result/avrate attributes are present
Fix this by passing attr_size through and allocate the reply the way the add and delete paths do.
Note on exposure: RTM_GETACTION is the only one of the three action commands that is not capability checked - tc_ctl_action() requires CAP_NET_ADMIN for RTM_NEWACTION and RTM_DELACTION only - so this turns a fixed NLMSG_GOODSIZE reply into a user sized allocation on an unprivileged path. It is bounded by TCA_ACT_MAX_PRIO actions per request, and tca_action_gd() does not reject duplicate indices, so a single large action can be requested 32 times; an act_bpf program near BPF_MAXINSNS is about 32KB of dump, or roughly 1MB for one request. Creating such an action still requires CAP_NET_ADMIN, and the add and delete paths have sized their skbs this way since the Fixes commit. Should this ever need bounding, GFP_KERNEL_ACCOUNT would charge the reply to the caller's memcg.
--- net/sched/act_api.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
net/sched/act_api.c +4 -3
@@ -1697,12 +1697,12 @@ static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[],static int tcf_get_notify (struct net *net,u32 portid,struct nlmsghdr *n, -struct tc_action *actions[],int event, +struct tc_action *actions[],size_t attr_size,int event,struct netlink_ext_ack *extack) {struct sk_buff *skb; - skb =alloc_skb (NLMSG_GOODSIZE ,GFP_KERNEL ); + skb =alloc_skb (max (attr_size,NLMSG_GOODSIZE ),GFP_KERNEL );if (!skb)return -ENOBUFS ; if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event,
@@ -2053,7 +2053,8 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, attr_size =tcf_action_full_attrs_size (attr_size);if (event ==RTM_GETACTION ) - ret =tcf_get_notify (net, portid, n, actions, event, extack); + ret =tcf_get_notify (net, portid, n, actions, attr_size, event, + extack);else {/* delete */ ret =tcf_del_notify (net, n, actions, portid, attr_size, extack);if (ret)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 623/1250 net/sched: act_api: fix skb sizing and action leak on reoffload delete
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Victor Nogueira <[email protected]>
[ Upstream commit 251367a0a3319fa565daf7468b0afd933b1f5ab1 ]
tcf_reoffload_del_notify_msg() sizes the RTM_DELACTION skb with tcf_action_fill_size(action) alone. Unlike every other notification path it never wraps that in tcf_action_full_attrs_size(), so the nlmsg_put() header, struct tcamsg and the TCA_ACT_TAB nest that tca_get_fill() emits - 24 bytes on x86_64 - are not budgeted. As long as the single action stays well under NLMSG_GOODSIZE the floor in alloc_skb() hides this, but once its fill size crosses NLMSG_GOODSIZE the allocation is exactly 24 bytes short and tca_get_fill() runs out of tailroom. That is now easy to reach for an offloadable act_pedit with a large tcfp_nkeys, which commit 8e2efb3f45a5 ("net/sched: add get_fill_size callbacks for actions missing them") started accounting for properly.
When that happens tcf_reoffload_del_notify() returns early, before tcf_idr_release_unsafe(), and tcf_action_reoffload_cb() discards the return value:
if (tc_act_skip_sw(p->tcfa_flags) && !tc_act_in_hw(p)) tcf_reoffload_del_notify(net, p);
The action has just lost its last hardware instance and is skip_sw, so it is left installed while processing no packets, and with no notification to tell userspace about it. An -ENOBUFS from alloc_skb() gets the same treatment.
Fix this by budgeting the message header the way the add and delete paths do, and release the action even when the notification cannot be built - dropping the notification is strictly better than leaking a dead action, and there is no caller left to report the error to.
--- net/sched/act_api.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
net/sched/act_api.c +11 -6
@@ -1867,11 +1867,13 @@ static int tcf_action_delete(struct net *net, struct tc_action *actions[])static struct sk_buff *tcf_reoffload_del_notify_msg (struct net *net,struct tc_action *action) { -size_t attr_size =tcf_action_fill_size (action);struct tc_action *actions[TCA_ACT_MAX_PRIO ] = { [0 ] = action, };struct sk_buff *skb; +size_t attr_size; + + attr_size =tcf_action_full_attrs_size (tcf_action_fill_size (action)); skb =alloc_skb (max (attr_size,NLMSG_GOODSIZE ),GFP_KERNEL );if (!skb)
@@ -1888,15 +1890,18 @@ static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net,static int tcf_reoffload_del_notify (struct net *net,struct tc_action *action) {const struct tc_action_ops *ops = action->ops; -struct sk_buff *skb; +struct sk_buff *skb =NULL ;int ret; -if (!rtnl_notify_needed (net,0 ,RTNLGRP_TC )) { - skb =NULL ; - }else { +if (rtnl_notify_needed (net,0 ,RTNLGRP_TC )) { skb =tcf_reoffload_del_notify_msg (net, action); +/* The action has already lost its hardware instance and is +* skip_sw, so it must be released whether or not the +* notification can be built. Drop the notification rather +* than leave an action behind that processes no packets. +*/ if (IS_ERR (skb)) -return PTR_ERR (skb); + skb =NULL ; } ret =tcf_idr_release_unsafe (action);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 624/1250 sctp: fix a TOCTOU race in SCTP_CMD_TIMER_START
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xin Long <[email protected]>
[ Upstream commit 2188569e7e1b0bc3f3b557dc97ab7a02befc11c8 ]
The SCTP_CMD_TIMER_START handler checks timer_pending() before calling timer_reduce(). The timer can expire and detach between these operations, causing timer_reduce() to rearm the timer without taking the association reference required for the newly armed timer.
The timer callback later unconditionally drops its association reference, which can leave the association reference count unbalanced and result in use-after-free during association teardown.
Use the return value of timer_reduce() to determine whether the timer was actually armed. Take the association reference only when timer_reduce() successfully starts a new timer, closing the race between checking the timer state and rearming it.
This issue was reported by Nico Yip (@_cyeaa_) working with TrendAI Zero Day Initiative.
--- net/sctp/sm_sideeffect.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)
net/sctp/sm_sideeffect.c +1 -10
@@ -1545,17 +1545,8 @@ static int sctp_cmd_interpreter(enum sctp_event_type event_type, timeout = asoc->timeouts[cmd->obj.to];BUG_ON (!timeout); -/* -* SCTP has a hard time with timer starts. Because we process -* timer starts as side effects, it can be hard to tell if we -* have already started a timer or not, which leads to BUG -* halts when we call add_timer. So here, instead of just starting -* a timer, if the timer is already started, and just mod -* the timer with the shorter of the two expiration times -*/ -if (!timer_pending (timer)) +if (!timer_reduce (timer, jiffies + timeout))sctp_association_hold (asoc); -timer_reduce (timer, jiffies + timeout);break ;case SCTP_CMD_TIMER_RESTART :
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 625/1250 scsi: mpi3mr: Fix NULL pointer dereference in mpi3mr_sas_port_add()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Milan P. Gandhi <[email protected]>
[ Upstream commit dba9e2181ca5e875f98b8b9b4535cdaab87dcb0d ]
sas_port_alloc_num() can return NULL on memory allocation failure. The return value is passed directly to sas_port_add() without a NULL check, which causes a NULL pointer dereference.
Additionally, if sas_port_add() fails, the allocated port is not freed before jumping to out_fail, leaking the sas_port structure. Call sas_port_free() to properly release it.
--- drivers/scsi/mpi3mr/mpi3mr_transport.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/scsi/mpi3mr/mpi3mr_transport.c +6 -0
@@ -1430,9 +1430,15 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc, } port =sas_port_alloc_num (mr_sas_node->parent_dev); +if (!port) { +ioc_err (mrioc,"failure at %s:%d/%s()!\n" , + __FILE__, __LINE__, __func__); + goto out_fail; + }if ((sas_port_add (port))) {ioc_err (mrioc,"failure at %s:%d/%s()!\n" , __FILE__, __LINE__, __func__); +sas_port_free (port); goto out_fail; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 626/1250 scsi: mpi3mr: Fix target device refcount leak in mpi3mr_sas_port_add()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Milan P. Gandhi <[email protected]>
[ Upstream commit 419d129f970aaa6567dbac366b0c93784bf9ec97 ]
mpi3mr_get_tgtdev_by_addr() increments the target device kref when it returns a device. If a subsequent error triggers a goto out_fail after the tgtdev reference is acquired, the reference is never released because the out_fail path does not call mpi3mr_tgtdev_put(). This prevents the target device structure from ever being freed.
Add a tgtdev put in the out_fail path, guarded by a NULL check since tgtdev is only acquired for SAS_END_DEVICE types and the same cleanup path is shared by earlier error cases where tgtdev is still NULL.
--- drivers/scsi/mpi3mr/mpi3mr_transport.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/scsi/mpi3mr/mpi3mr_transport.c +2 -0
@@ -1509,6 +1509,8 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc,list_for_each_entry_safe (mr_sas_phy, next, &mr_sas_port->phy_list, port_siblings)list_del (&mr_sas_phy->port_siblings); +if (tgtdev) +mpi3mr_tgtdev_put (tgtdev);kfree (mr_sas_port);return NULL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 627/1250 smb/client: validate new EOF for insert range
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huiwen He <[email protected]>
[ Upstream commit 1519dc88c87f5346dae0464d7d6da1b6bf1f6e8e ]
smb3_insert_range() does not check if the new file size (i_size + len) is valid. This allows FALLOC_FL_INSERT_RANGE to bypass RLIMIT_FSIZE, exceed s_maxbytes, or produce a size outside the loff_t range.
Use check_add_overflow() to calculate the new EOF. Validate it with inode_newsize_ok() before modifying the file.
Reproducer, using a file on a CIFS mount:
bash -c '
FILE=/mnt/cifs/repro
trap "" SIGXFSZ
ulimit -f 3072 # RLIMIT_FSIZE = 3 MiB
# A regular write is stopped at 3 MiB.
dd if=/dev/zero of="$FILE" bs=1M count=4 status=none stat -c "size after write: %s" "$FILE"
# Insert 2 MiB into a 2 MiB file. truncate -s 2M "$FILE" fallocate -i -o 0 -l 2M "$FILE" stat -c "size after insert: %s" "$FILE"
'
Before this change, the regular write stops at the 3 MiB limit, but insert range grows the file to 4 MiB:
dd: error writing '/mnt/cifs/repro': File too large size after write: 3145728 size after insert: 4194304
After this change, insert range also fails at the limit and leaves the 2 MiB file unchanged:
dd: error writing '/mnt/cifs/repro': File too large size after write: 3145728 fallocate: fallocate failed: File too large size after insert: 2097152
--- fs/smb/client/smb2ops.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
fs/smb/client/smb2ops.c +10 -2
@@ -3891,7 +3891,8 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,struct cifsFileInfo *cfile = file->private_data;struct inode *inode =file_inode (file);struct cifsInodeInfo *cifsi =CIFS_I (inode); -__u64 count, old_eof, new_eof; +u64 count; +loff_t old_eof, new_eof; xid =get_xid ();
@@ -3901,8 +3902,15 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, goto out; } +if (check_add_overflow (old_eof, len, &new_eof)) { + rc = -EFBIG ; + goto out; + } + rc =inode_newsize_ok (inode, new_eof); +if (rc) + goto out; + count = old_eof - off; - new_eof = old_eof + len;filemap_invalidate_lock (inode->i_mapping); rc =filemap_write_and_wait_range (inode->i_mapping, off, new_eof -1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 628/1250 smb/client: validate new EOF for zero range
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huiwen He <[email protected]>
[ Upstream commit 88972e35750792e717af287dc71f42a03b5cbce4 ]
When FALLOC_FL_ZERO_RANGE is used without FALLOC_FL_KEEP_SIZE, smb3_zero_range() may extend EOF without checking RLIMIT_FSIZE, allowing the file to grow beyond the caller's file-size limit.
Fix this by calling inode_newsize_ok() before sending the zero-range request when the operation would extend EOF.
Reproducer, using a file on a CIFS mount:
bash -c ' FILE=/mnt/cifs/repro trap "" SIGXFSZ ulimit -f 3072 truncate -s 2M "$FILE" fallocate --zero-range -o 0 -l 4M "$FILE" echo "fallocate rc=$?" stat -c "file size=%s" "$FILE" '
Before this change, the operation succeeds despite the 3 MiB limit:
fallocate rc=0 file size=4194304
After this change, fallocate fails and leaves the file at 2 MiB.
--- fs/smb/client/smb2ops.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
fs/smb/client/smb2ops.c +8 -1
@@ -3380,6 +3380,13 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,trace_smb3_zero_enter (xid, cfile->fid.persistent_fid, tcon->tid, ses->Suid, offset, len); + new_size = offset + len; +if (!keep_size &&i_size_read (inode) < new_size) { + rc =inode_newsize_ok (inode, new_size); +if (rc) + goto out; + } +filemap_invalidate_lock (inode->i_mapping); i_size =i_size_read (inode);
@@ -3411,7 +3418,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,/* * do we also need to change the size of the file? */ - new_size = offset + len;if (keep_size == false && (unsigned long long )i_size_read (inode) < new_size) { rc =SMB2_set_eof (xid, tcon, cfile->fid.persistent_fid, cfile->fid.volatile_fid, cfile->pid, new_size);
@@ -3426,6 +3432,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, zero_range_exit:filemap_invalidate_unlock (inode->i_mapping); + out:free_xid (xid);if (rc) trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 629/1250 smb/client: mark file sparse before emulating insert range
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huiwen He <[email protected]>
[ Upstream commit cd03ce4950d80147ac8f20bc03c42b75b0352407 ]
The SMB client emulates FALLOC_FL_INSERT_RANGE with SET_EOF, COPYCHUNK and SET_ZERO_DATA.
SET_ZERO_DATA creates a hole only when the file is sparse. On a non-sparse file, it clears the inserted range but leaves its blocks allocated, causing the extent count check in xfstests generic/064 to fail.
Fix this by marking the file sparse before modifying it.
This patch produces the expected sparse extents in xfstests generic/064 only when the server-reported block size is compatible with the server's deallocation granularity.
For ksmbd, the reported block size follows the backing filesystem, and the test passes. For Samba, the test passes with a block size matching the backend granularity, for example, 4 KiB on Btrfs, but not with the default 1 KiB value. For Windows Server 2022, 4 KiB inserts do not generate holes, while aligned inserts of 64 KiB or larger do.
--- fs/smb/client/smb2ops.c | 5 +++++ 1 file changed, 5 insertions(+)
fs/smb/client/smb2ops.c +5 -0
@@ -3919,6 +3919,11 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, count = old_eof - off; +/* SET_ZERO_DATA creates a hole only in a sparse file. */ + rc =smb2_set_sparse (xid, tcon, cfile, inode, true); +if (rc) + goto out; +filemap_invalidate_lock (inode->i_mapping); rc =filemap_write_and_wait_range (inode->i_mapping, off, new_eof -1 );if (rc <0 )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 630/1250 smb: move copychunk definitions to common/smb2pdu.h
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: ZhangGuoDong <[email protected]>
[ Upstream commit cc26f593dc193567bdb059a6ffde58e627a44f65 ]
Rename 3 places:
- copychunk_ioctl -> copychunk_ioctl_req
- copychunk -> srv_copychunk
- server: ResumeKey -> SourceKeyU64
Merge the struct members of the server and the client, then move duplicate definitions to common header file.
Stable-dep-of: 0923ae9f23cc ("smb/client: fix data corruption in emulated insert range")
--- fs/smb/client/cifspdu.h | 8 -------- fs/smb/client/smb2ops.c | 6 +++--- fs/smb/client/smb2pdu.h | 24 ------------------------ fs/smb/common/smb2pdu.h | 29 +++++++++++++++++++++++++++++ fs/smb/server/smb2pdu.c | 4 ++-- fs/smb/server/smb2pdu.h | 20 -------------------- 6 files changed, 34 insertions(+), 57 deletions(-)
fs/smb/client/cifspdu.h +0 -8
@@ -1323,14 +1323,6 @@ typedef struct smb_com_ntransact_rsp {/* parms and data follow */ } __attribute__((packed)) NTRANSACT_RSP; -/* See MS-SMB 2.2.7.2.1.1 */ -struct srv_copychunk { -__le64 SourceOffset; -__le64 DestinationOffset; -__le32 CopyLength; -__u32 Reserved; -} __packed; - typedef struct smb_com_transaction_ioctl_req { struct smb_hdr hdr; /* wct = 23 */ __u8 MaxSetupCount;
fs/smb/client/smb2ops.c +3 -3
@@ -1539,7 +1539,7 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,static int SMB2_request_res_key (const unsigned int xid,struct cifs_tcon *tcon,u64 persistent_fid,u64 volatile_fid, -struct copychunk_ioctl *pcchunk) +struct copychunk_ioctl_req *pcchunk) {int rc;unsigned int ret_data_len;
@@ -1879,10 +1879,10 @@ smb2_copychunk_range(const unsigned int xid, {int rc =0 ;unsigned int ret_data_len =0 ; -struct copychunk_ioctl *cc_req =NULL ; +struct copychunk_ioctl_req *cc_req =NULL ;struct copychunk_ioctl_rsp *cc_rsp =NULL ;struct cifs_tcon *tcon; -struct copychunk *chunk; +struct srv_copychunk *chunk;u32 chunks, chunk_count, chunk_bytes;u32 copy_bytes, copy_bytes_left;u32 chunks_written, bytes_written;
fs/smb/client/smb2pdu.h +0 -24
@@ -193,36 +193,12 @@ struct crt_sd_ctxt {struct smb3_sd sd; } __packed; - -#define COPY_CHUNK_RES_KEY_SIZE 24struct resume_key_req {char ResumeKey[COPY_CHUNK_RES_KEY_SIZE ];__le32 ContextLength;/* MBZ */ char Context[];/* ignored, Windows sets to 4 bytes of zero */ } __packed; - -struct copychunk { -__le64 SourceOffset; -__le64 TargetOffset; -__le32 Length; -__le32 Reserved; -} __packed; - -/* this goes in the ioctl buffer when doing a copychunk request */ -struct copychunk_ioctl { -char SourceKey[COPY_CHUNK_RES_KEY_SIZE ]; -__le32 ChunkCount; -__le32 Reserved; -struct copychunk Chunks[]; -} __packed; - -struct copychunk_ioctl_rsp { -__le32 ChunksWritten; -__le32 ChunkBytesWritten; -__le32 TotalBytesWritten; -} __packed; -/* See MS-FSCC 2.3.29 and 2.3.30 */ struct get_retrieval_pointer_count_req {__le64 StartingVcn;/* virtual cluster number (signed) */
fs/smb/common/smb2pdu.h +29 -0
@@ -1388,6 +1388,35 @@ struct smb2_ioctl_req {__u8 Buffer[]; } __packed; +/* See MS-SMB2 2.2.31.1.1 */ +struct srv_copychunk { +__le64 SourceOffset; +__le64 TargetOffset; +__le32 Length; +__le32 Reserved; +} __packed; + +#define COPY_CHUNK_RES_KEY_SIZE 24 + +/* See MS-SMB2 2.2.31.1 */ +/* this goes in the ioctl buffer when doing a copychunk request */ +struct copychunk_ioctl_req { +union { +char SourceKey[COPY_CHUNK_RES_KEY_SIZE ]; +__le64 SourceKeyU64[3 ]; + }; +__le32 ChunkCount; +__le32 Reserved; + struct srv_copychunk Chunks[] __counted_by_le(ChunkCount); +} __packed; + +/* See MS-SMB2 2.2.32.1 */ +struct copychunk_ioctl_rsp { +__le32 ChunksWritten; +__le32 ChunkBytesWritten; +__le32 TotalBytesWritten; +} __packed; +struct smb2_ioctl_rsp {struct smb2_hdr hdr;__le16 StructureSize;/* Must be 49 */
fs/smb/server/smb2pdu.c +2 -2
@@ -7965,11 +7965,11 @@ static int fsctl_copychunk(struct ksmbd_work *work, } src_fp =ksmbd_lookup_foreign_fd (work, -le64_to_cpu (ci_req->ResumeKey[0 ])); +le64_to_cpu (ci_req->SourceKeyU64[0 ])); dst_fp =ksmbd_lookup_fd_slow (work, volatile_id, persistent_id); ret = -EINVAL ;if (!src_fp || - src_fp->persistent_id !=le64_to_cpu (ci_req->ResumeKey[1 ])) { + src_fp->persistent_id !=le64_to_cpu (ci_req->SourceKeyU64[1 ])) { rsp->hdr.Status =STATUS_OBJECT_NAME_NOT_FOUND ; goto out; }
fs/smb/server/smb2pdu.h +0 -20
@@ -187,26 +187,6 @@ struct resume_key_ioctl_rsp {__u8 Context[4 ];/* ignored, Windows sets to 4 bytes of zero */ } __packed; -struct srv_copychunk { -__le64 SourceOffset; -__le64 TargetOffset; -__le32 Length; -__le32 Reserved; -} __packed; - -struct copychunk_ioctl_req { -__le64 ResumeKey[3 ]; -__le32 ChunkCount; -__le32 Reserved; - struct srv_copychunk Chunks[] __counted_by_le(ChunkCount); -} __packed; - -struct copychunk_ioctl_rsp { -__le32 ChunksWritten; -__le32 ChunkBytesWritten; -__le32 TotalBytesWritten; -} __packed; -struct file_sparse {__u8 SetSparse; } __packed;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 631/1250 smb/client: fix data corruption in emulated insert range
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huiwen He <[email protected]>
[ Upstream commit 0923ae9f23cc9460b0df6fc124cd56ec4436411b ]
smb3_insert_range() shifts [off, EOF) right with COPYCHUNK, copying from low to high offsets. When the ranges overlap, the copy can overwrite source data that has not yet been copied. For a 1 MiB insert at offset 0:
offset: 0 1M 2M 3M 4M 5M
before: | A | B | C | D | expected: | hole | A | B | C | D | current: | hole | A | A | A | A | (corrupted)
Let x be the insertion offset, L the total length to move, delta the insert length, and C the normal chunk size allowed by the server. Insert range maps
[x, x + L) -> [x + delta, x + delta + L).
When delta >= L, the complete source and target ranges are disjoint, so the normal copy order and chunk size are safe:
offset: 0 4 8 12 16 20 24 28 32
source: [--S0--][--S1--][--S2--][--S3--]
target: [--T0--][--T1--][--T2--][--T3--]
When delta < L, the complete source and target ranges overlap, so the copy must proceed from EOF backwards. There are two subcases.
If delta >= C, each corresponding source and target chunk is disjoint. The 1 MiB example has L = 4 MiB and delta = C = 1 MiB:
offset: 0 1M 2M 3M 4M 5M
source: [--S0--][--S1--][--S2--][--S3--]
target: [--T0--][--T1--][--T2--][--T3--]
Copying S0 from [0, 1M) to [1M, 2M) overwrites S1 before it is copied. Processing chunks from EOF backwards prevents this inter-chunk overwrite.
If delta < C, the source and target ranges of a normal chunk also overlap. For example, with L = 16, delta = 2 and C = 4:
offset: 0 2 4 6 8 10 12 14 16 18
source: [--S0--][--S1--][--S2--][--S3--]
target: [--T0--][--T1--][--T2--][--T3--]
Here S0 and T0 overlap over [2,4), S1 and T1 over [6,8), and so on. Backward ordering cannot control how the server copies bytes inside one descriptor, so the chunk size must be limited to delta.
Fix this by copying overlapping right shifts from EOF backwards. Limit the chunk size to delta when delta < C so that each chunk's source and target ranges do not overlap. Using larger chunks would require a way to identify servers that safely handle overlapping COPYCHUNK descriptors.
Therefore:
delta >= L:
keep the normal copy order and chunk size
delta < L:
delta >= C: copy backwards and keep the normal chunk size delta < C: copy backwards and limit the chunk size to delta
Only the delta < C subcase requires reducing the chunk size for data integrity.
Reproducer:
bash -c '
MNT=/mnt/scratch
# Generate four 1 MiB random blocks: [A][B][C][D]. dd if=/dev/urandom of=/tmp/src bs=1M count=4 status=none
# With C = 1 MiB, test delta = C and delta < C. for delta in 1M 1K; do
truncate -s 0 /tmp/expected truncate -s "$delta" /tmp/expected cat /tmp/src >> /tmp/expected
cp /tmp/src "$MNT/file"
fallocate --insert-range -o 0 -l "$delta" "$MNT/file"
if cmp -s /tmp/expected "$MNT/file"; then
echo "delta=$delta: OK"
else
echo "delta=$delta: CORRUPTED"
fi
done
'
The corruption reproduces with Samba and ksmbd, while Windows handles the overlapping COPYCHUNK ranges safely.
The 1 MiB case tests delta >= C, while the 1 KiB case tests delta < C. Before this change, the reproducer reports:
delta=1M: CORRUPTED
delta=1K: CORRUPTED
After this change, it passes against both ksmbd and Samba:
delta=1M: OK
delta=1K: OK
--- fs/smb/client/smb2ops.c | 132 ++++++++++++++++++++++++++++++++-------- 1 file changed, 106 insertions(+), 26 deletions(-)
fs/smb/client/smb2ops.c +106 -26
@@ -1831,31 +1831,31 @@ smb2_ioctl_query_info(const unsigned int xid,* * @tcon: destination file tcon * @bytes_left: how many bytes are left to copy +* @chunk_size: maximum size of a single chunk * * Return: maximum number of chunks with which Chunks[] can be filled. */ static inline u32 -calc_chunk_count (struct cifs_tcon *tcon,u64 bytes_left) +calc_chunk_count (struct cifs_tcon *tcon,u64 bytes_left,u32 chunk_size) {u32 max_chunks =READ_ONCE (tcon->max_chunks);u32 max_bytes_copy =READ_ONCE (tcon->max_bytes_copy); -u32 max_bytes_chunk =READ_ONCE (tcon->max_bytes_chunk);u64 need;u32 allowed; -if (!max_bytes_chunk || !max_bytes_copy || !max_chunks) +if (!chunk_size || !max_bytes_copy || !max_chunks)return 0 ;/* chunks needed for the remaining bytes */ - need =DIV_ROUND_UP_ULL (bytes_left, max_bytes_chunk); + need =DIV_ROUND_UP_ULL (bytes_left, chunk_size);/* chunks allowed per cc request */ - allowed =DIV_ROUND_UP (max_bytes_copy, max_bytes_chunk); + allowed =DIV_ROUND_UP (max_bytes_copy, chunk_size);return (u32 )umin (need,umin (max_chunks, allowed)); }/** -* smb2_copychunk_range - server-side copy of data range +* __smb2_copychunk_range - server-side copy of data range * * @xid: transaction id * @src_file: source file
@@ -1867,15 +1867,15 @@ calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left)* Obtains a resume key for @src_file and issues FSCTL_SRV_COPYCHUNK_WRITE * IOCTLs, splitting the request into chunks limited by tcon->max_*. * -* Return: @len on success; negative errno on failure. +* Return: 0 on success; negative errno on failure. */ -static ssize_t -smb2_copychunk_range (const unsigned int xid, -struct cifsFileInfo *src_file, -struct cifsFileInfo *dst_file, -u64 src_off, -u64 len, -u64 dst_off) +static int +__smb2_copychunk_range (const unsigned int xid, +struct cifsFileInfo *src_file, +struct cifsFileInfo *dst_file, +u64 src_off, +u64 len, +u64 dst_off) {int rc =0 ;unsigned int ret_data_len =0 ;
@@ -1883,12 +1883,14 @@ smb2_copychunk_range(const unsigned int xid,struct copychunk_ioctl_rsp *cc_rsp =NULL ;struct cifs_tcon *tcon;struct srv_copychunk *chunk; -u32 chunks, chunk_count, chunk_bytes; +u32 chunks, chunk_count, chunk_bytes, chunk_size;u32 copy_bytes, copy_bytes_left;u32 chunks_written, bytes_written;u64 total_bytes_left = len;u64 src_off_prev, dst_off_prev; +u64 max_chunk =0 ;u32 retries =0 ; +bool reverse = false; tcon =tlink_tcon (dst_file->tlink);
@@ -1896,8 +1898,50 @@ smb2_copychunk_range(const unsigned int xid, dst_file->fid.volatile_fid, tcon->tid, tcon->ses->Suid, src_off, dst_off, len); +/* +* Same-file left shifts are safe in forward order. For a right shift, +* let L be the copy length, delta the distance between the source and +* destination, and C the normal chunk size: +* +* delta >= L: copy forwards using C +* delta < L: +* delta >= C: copy backwards using C +* delta < C: copy backwards with chunks limited to delta +* +* Copying backwards prevents one chunk from overwriting data needed by +* a later chunk. Limiting the chunk size to delta prevents an individual +* chunk from overlapping itself. +* This limit can be removed once all supported servers handle overlapping +* descriptors safely. +* +* A small right shift over a large range may therefore require many +* chunks. +*/ +if (src_file == dst_file && dst_off > src_off) { +u64 delta = dst_off - src_off; + +if (delta < len) { + reverse = true; + max_chunk = delta; + } + } + +/* +* A backward copy walks the offsets down from the end of the range. +* Do this once, outside the retry loop, so a retry does not move the +* offsets again. +*/ +if (reverse) { + src_off += len; + dst_off += len; + } + retry: - chunk_count =calc_chunk_count (tcon, total_bytes_left); + chunk_size =READ_ONCE (tcon->max_bytes_chunk); +if (max_chunk && max_chunk < chunk_size) + chunk_size = (u32 )max_chunk; + + chunk_count =calc_chunk_count (tcon, total_bytes_left, chunk_size);if (!chunk_count) { rc = -EOPNOTSUPP ; goto out;
@@ -1932,16 +1976,21 @@ smb2_copychunk_range(const unsigned int xid,while (copy_bytes_left >0 && chunks < chunk_count) { chunk = &cc_req->Chunks[chunks++]; + chunk_bytes =umin (copy_bytes_left, chunk_size); +if (reverse) { + src_off -= chunk_bytes; + dst_off -= chunk_bytes; + } + chunk->SourceOffset =cpu_to_le64 (src_off); chunk->TargetOffset =cpu_to_le64 (dst_off); - - chunk_bytes =umin (copy_bytes_left, tcon->max_bytes_chunk); - chunk->Length =cpu_to_le32 (chunk_bytes);/* Buffer is zeroed, no need to set chunk->Reserved = 0 */ - src_off += chunk_bytes; - dst_off += chunk_bytes; +if (!reverse) { + src_off += chunk_bytes; + dst_off += chunk_bytes; + } copy_bytes_left -= chunk_bytes; copy_bytes += chunk_bytes;
@@ -1982,6 +2031,18 @@ smb2_copychunk_range(const unsigned int xid, goto out; } +/* +* A successful COPYCHUNK should copy every descriptor (MS-SMB2 +* 3.3.5.15.6). Reject a short backward copy because the rewind +* below only supports forward copying. +*/ +if (unlikely (reverse && bytes_written < copy_bytes)) { +cifs_tcon_dbg (VFS ,"Copychunk short write %u/%u (reverse)\n" , + bytes_written, copy_bytes); + rc = -EIO ; + goto out; + } +/* Partial write: rewind */ if (bytes_written < copy_bytes) {u32 delta = copy_bytes - bytes_written;
@@ -2043,10 +2104,27 @@ smb2_copychunk_range(const unsigned int xid,trace_smb3_copychunk_done (xid, src_file->fid.volatile_fid, dst_file->fid.volatile_fid, tcon->tid, tcon->ses->Suid, src_off, dst_off, len); -return len; +return 0 ; } } +static ssize_t +smb2_copychunk_range (const unsigned int xid, +struct cifsFileInfo *src_file, +struct cifsFileInfo *dst_file, +u64 src_off, +u64 len, +u64 dst_off) +{ +int rc; + + rc =__smb2_copychunk_range (xid, src_file, dst_file, src_off, len, + dst_off); +if (rc) +return rc; +return len; +} +static int smb2_flush_file (const unsigned int xid,struct cifs_tcon *tcon,struct cifs_fid *fid)
@@ -3898,7 +3976,6 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,struct cifsFileInfo *cfile = file->private_data;struct inode *inode =file_inode (file);struct cifsInodeInfo *cifsi =CIFS_I (inode); -u64 count;loff_t old_eof, new_eof; xid =get_xid ();
@@ -3917,8 +3994,6 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,if (rc) goto out; - count = old_eof - off; -/* SET_ZERO_DATA creates a hole only in a sparse file. */ rc =smb2_set_sparse (xid, tcon, cfile, inode, true);if (rc)
@@ -3940,7 +4015,12 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,netfs_resize_file (&cifsi->netfs,i_size_read (inode), true);fscache_resize_cookie (cifs_inode_cookie (inode),i_size_read (inode)); - rc =smb2_copychunk_range (xid, cfile, cfile, off, count, off + len); +/* +* Move [off, old_eof) right by len. The helper copies backwards if the +* source and destination ranges overlap. +*/ + rc =__smb2_copychunk_range (xid, cfile, cfile, off, old_eof - off, + off + len);if (rc <0 ) goto out_2; cifsi->netfs.zero_point = new_eof;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 632/1250 smb/client: fix integer truncation in collapse range
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huiwen He <[email protected]>
[ Upstream commit 7811701d6af7db76481a82b9bc3c4adf7863acf5 ]
smb3_collapse_range() stores the ssize_t return value of smb2_copychunk_range() in an int. A successful copy larger than INT_MAX is truncated to a negative value and treated as an error.
Reproducer:
MNT=/mnt/scratch truncate -s 2056M "$MNT/file" fallocate --collapse-range -o 1M -l 1M "$MNT/file"
Fix this by using __smb2_copychunk_range(), which reports success as zero instead of returning the copied byte count.
Before this change, the reproducer fails with:
fallocate: fallocate failed: Success
and the file size remains unchanged at 2056 MiB. After this change, the reproducer succeeds and the file size becomes the expected 2055 MiB.
--- fs/smb/client/smb2ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/smb/client/smb2ops.c +2 -2
@@ -3944,8 +3944,8 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, ictx->zero_point = old_eof;netfs_wait_for_outstanding_io (inode); - rc =smb2_copychunk_range (xid, cfile, cfile, off + len, - old_eof - off - len, off); + rc =__smb2_copychunk_range (xid, cfile, cfile, off + len, + old_eof - off - len, off);if (rc <0 ) goto out_2;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 633/1250 smb: client: transport: Fix debug printing in __release_mid()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit d83a21bb26015bfdd79b0440fe816b271b8bbab3 ]
Long time ago during upgrading printk():s to the respective pr_<level>() calls one misconversion happened and nobody has noticed that. So, previously printk(KERN_DEBUG) + printk() worked as one long debug print since the trailing '\n' is only present in the followup printk() format string. The culprit change missed that and split the message to two on the different levels. Restore the original behaviour to make users be less confused in the most likely never happen cases of partially getting that message.
--- fs/smb/client/transport.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
fs/smb/client/transport.c +5 -6
@@ -105,12 +105,11 @@ void __release_mid(struct kref *refcount)trace_smb3_slow_rsp (smb_cmd, midEntry->mid, midEntry->pid, midEntry->when_sent, midEntry->when_received);if (cifsFYI &CIFS_TIMER ) { -pr_debug ("slow rsp: cmd %d mid %llu" , - midEntry->command, midEntry->mid); -cifs_info ("A: 0x%lx S: 0x%lx R: 0x%lx\n" , - now - midEntry->when_alloc, - now - midEntry->when_sent, - now - midEntry->when_received); +pr_debug ("slow rsp: cmd %d mid %llu A: 0x%lx S: 0x%lx R: 0x%lx\n" , + midEntry->command, midEntry->mid, + now - midEntry->when_alloc, + now - midEntry->when_sent, + now - midEntry->when_received); } }#endif
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 634/1250 sctp: fix soft lockup from unpadded ASCONF-ACK parameter iteration
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henry Martin <[email protected]>
[ Upstream commit 2cb0b0b1ed69430bf73740377ea0a1c44c50db63 ]
sctp_verify_asconf() walks ASCONF-ACK parameters with sctp_walk_params(), which advances by SCTP_PAD4(length), while the consumer sctp_get_asconf_response() iterates the same parameters advancing by the raw length, without padding. A single odd-length parameter desynchronises the two walks and makes the consumer interpret attacker-controlled bytes at a misaligned offset.
When those bytes yield a length of zero, the while loop over asconf_ack_len makes no progress, spinning forever in softirq context, and the watchdog reports a soft lockup. All reads stay within the received skb, so the lockup is a pure remote denial of service. A remote peer can trigger it with a crafted ASCONF-ACK on an ADD-IP enabled association with an outstanding ASCONF (RFC 5061 section 4.1.2 requires the chunk to be authenticated, but the predefined empty key id 0 allows the peer to compute the same association HMAC from publicly exchanged parameters, so the gate does not help).
The SCTP_PARAM_ERR_CAUSE case of sctp_verify_asconf() also performs no length check, letting a parameter without a complete error header reach the consumer, which reads errhdr.cause past the end of the parameter, an out-of-bounds read.
Reject SCTP_PARAM_ERR_CAUSE parameters shorter than sizeof(struct sctp_addip_param) + sizeof(struct sctp_errhdr) at the verifier, and advance the consumer iterator with the same padding rule as the verifier to keep the two walks in lockstep. The verifier change guarantees a complete error header in every ERR_CAUSE parameter the consumer can see, so the consumer's asconf_ack_len check is dropped and it returns err_param->cause directly. The consumer padding fix is still required because odd lengths remain valid for SCTP_PARAM_ERR_CAUSE per RFC 5061.
The issue was found by ZeroHive, a vulnerability hunting agent at Tencent Yunding Lab.
--- net/sctp/sm_make_chunk.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
net/sctp/sm_make_chunk.c +6 -8
@@ -3212,6 +3212,9 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, *errp = param.p;switch (param.p->type) {case SCTP_PARAM_ERR_CAUSE : +if (length <sizeof (struct sctp_addip_param ) + +sizeof (struct sctp_errhdr )) +return false;break ;case SCTP_PARAM_IPV4_ADDRESS :if (length !=sizeof (struct sctp_ipv4addr_param ))
@@ -3445,20 +3448,15 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack,case SCTP_PARAM_ERR_CAUSE : length =sizeof (*asconf_ack_param); err_param = (void *)asconf_ack_param + length; - asconf_ack_len -= length; -if (asconf_ack_len >0 ) -return err_param->cause; -else -return SCTP_ERROR_INV_PARAM ; -break ; +return err_param->cause;default :return SCTP_ERROR_INV_PARAM ; } } length =ntohs (asconf_ack_param->param_hdr.length); - asconf_ack_param = (void *)asconf_ack_param + length; - asconf_ack_len -= length; + asconf_ack_param = (void *)asconf_ack_param +SCTP_PAD4 (length); + asconf_ack_len -=SCTP_PAD4 (length); }return err_code;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 635/1250 raw: annotate disconnect-side IPv4 match writers
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xuanqiang Luo <[email protected]>
[ Upstream commit ac08d183dac0441e41f77bbad50798fe609d90f1 ]
raw_v4_match() reads inet_daddr, inet_rcv_saddr and sk_bound_dev_if locklessly under RCU. Bind and connect writers are annotated, but __udp_disconnect() still clears the same fields using plain stores.
Commit 18f116931f52e ("raw: annotate lockless match fields in raw_v4_match()") added the lockless readers and annotated the raw bind and datagram connect writers. Its v4 revision intentionally left the shared disconnect-side IPv4 writers for follow-up cleanup.
Complete that follow-up by using WRITE_ONCE() for the disconnect-side stores, including the inet_rcv_saddr reset in inet_reset_saddr(), to pair with the lockless raw socket matcher.
--- include/net/ip.h | 3 ++- net/ipv4/udp.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-)
include/net/ip.h +2 -1
@@ -704,7 +704,8 @@ static inline void ip_ipgre_mc_map(__be32 naddr, const unsigned char *broadcast,static __inline__void inet_reset_saddr (struct sock *sk) { -inet_sk (sk)->inet_rcv_saddr =inet_sk (sk)->inet_saddr =0 ; +inet_sk (sk)->inet_saddr =0 ; +WRITE_ONCE (inet_sk (sk)->inet_rcv_saddr,0 );#if IS_ENABLED (CONFIG_IPV6 )if (sk->sk_family ==PF_INET6 ) {struct ipv6_pinfo *np =inet6_sk (sk);
net/ipv4/udp.c +2 -2
@@ -2200,10 +2200,10 @@ int __udp_disconnect(struct sock *sk, int flags)*/ sk->sk_state =TCP_CLOSE ; - inet->inet_daddr =0 ; +WRITE_ONCE (inet->inet_daddr,0 ); inet->inet_dport =0 ;sock_rps_reset_rxhash (sk); - sk->sk_bound_dev_if =0 ; +WRITE_ONCE (sk->sk_bound_dev_if,0 );if (!(sk->sk_userlocks &SOCK_BINDADDR_LOCK )) {inet_reset_saddr (sk); if (sk->sk_prot->rehash &&
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 636/1250 net: amd-xgbe: discard rx packets with bad FCS
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: James Nugraha <[email protected]>
[ Upstream commit ac8d6b28d48c5d951dcd923d33e461588e762a6d ]
amd-xgbe driver currently sets the MAC_RCR.DCRCC bit whenever RX is enabled. This disables hardware FCS validation, causing packets with bad FCS to be accepted unconditionally.
This change unsets DCRCC so that packets with bad FCS will be dropped, in-line with typical behaviours of many other network controllers.
Tests:
- Verified that packets with bad FCS are now dropped.
- Verified that receiving packets with bad FCS will increment the rx_crc_errors counter.
--- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/net/ethernet/amd/xgbe/xgbe-dev.c +1 -2
@@ -3299,7 +3299,7 @@ static void xgbe_enable_rx(struct xgbe_prv_data *pdata)XGMAC_IOWRITE (pdata,MAC_RQC0R , reg_val);/* Enable MAC Rx */ -XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,DCRCC ,1 ); +XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,DCRCC ,0 );XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,CST ,1 );XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,ACS ,1 );XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,RE ,1 );
@@ -3310,7 +3310,6 @@ static void xgbe_disable_rx(struct xgbe_prv_data *pdata)unsigned int i;/* Disable MAC Rx */ -XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,DCRCC ,0 );XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,CST ,0 );XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,ACS ,0 );XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,RE ,0 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 637/1250 vxlan: mdb: Fix use-after-free in vxlan_mdb_remote_src_del()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
[ Upstream commit 4aa61c88b4e292e10abdfd791334b8272108d68a ]
vxlan_mdb_is_valid_source(), which validates MDBE_ATTR_SOURCE and every MDBE_ATTR_SRC_LIST member, accepts the all-zeros address.
A source list is only accepted on a (*, G) entry, whose source is the all-zeros address, and for each member of the list an (S, G) entry is derived from it by substituting the source. Entries are keyed by a plain memcmp() of struct vxlan_mdb_entry_key, so if MDBE_ATTR_SOURCE is present and holds the all-zeros address and the source list holds it as well, the derived (S, G) key is byte-identical to the (*, G) key and resolves to the same entry. Omitting MDBE_ATTR_SOURCE is not equivalent, as the key is then left with a zero address family.
vxlan_mdb_remote_src_del() removes the forwarding entry of a source before freeing the source entry:
vxlan_mdb_remote_src_fwd_del(vxlan, group, remote, &ent->addr); vxlan_mdb_remote_src_entry_del(ent);
With the keys aliased, the first call deletes the remote of the entry that owns 'ent' instead of a separate (S, G) entry, and frees 'ent'. The second call then runs on the freed entry, and its hlist_del() reads ->pprev and ->next out of it and writes through them.
Adding the (*, G) entry with NLM_F_REPLACE and no source list marks the all-zeros source for deletion and reaches this from the sweep at the end of vxlan_mdb_remote_srcs_replace().
BUG: KASAN: slab-use-after-free in __vxlan_mdb_add+0x1cd/0xd70 Read of size 8 at addr ffff888102852500 by task poc/84 __vxlan_mdb_add+0x1cd/0xd70 vxlan_mdb_add+0xc0/0x140 rtnl_mdb_add+0x157/0x2a0 rtnetlink_rcv_msg+0x207/0x5a0 Allocated by task 84: __kmalloc_cache_noprof+0x153/0x360 vxlan_mdb_remote_srcs_add+0x2eb/0x440 __vxlan_mdb_add+0x803/0xd70 Freed by task 84: kfree+0x14c/0x3b0 vxlan_mdb_remote_del+0x129/0x1a0 __vxlan_mdb_del+0x4f/0xe0 vxlan_mdb_remote_src_fwd_del.isra.0+0x162/0x1b0 __vxlan_mdb_add+0x1c5/0xd70
The MDB operations are netns-scoped, so an unprivileged user can perform them in a new user and network namespace.
Reject the all-zeros address in vxlan_mdb_is_valid_source(), which covers both call sites. A (*, G) entry is expressed by omitting the source, so nothing legitimate is refused.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- drivers/net/vxlan/vxlan_mdb.c | 8 ++++++++ tools/testing/selftests/net/test_vxlan_mdb.sh | 6 ++++++ 2 files changed, 14 insertions(+)
drivers/net/vxlan/vxlan_mdb.c +8 -0
@@ -354,6 +354,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto,NL_SET_ERR_MSG_MOD (extack,"IPv4 multicast source address is not allowed" );return false; } +if (ipv4_is_zeronet (nla_get_in_addr (attr))) { +NL_SET_ERR_MSG_MOD (extack,"IPv4 all-zeros source address is not allowed" ); +return false; + }break ;#if IS_ENABLED (CONFIG_IPV6 )case htons (ETH_P_IPV6 ): {
@@ -368,6 +372,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto,NL_SET_ERR_MSG_MOD (extack,"IPv6 multicast source address is not allowed" ); return false; } +if (ipv6_addr_any (&src)) { +NL_SET_ERR_MSG_MOD (extack,"IPv6 all-zeros source address is not allowed" ); +return false; + }break ; }#endif
tools/testing/selftests/net/test_vxlan_mdb.sh +6 -0
@@ -685,6 +685,9 @@ star_g_common() run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent filter_mode exclude source_list $grp dst $vtep_ip src_vni 10010" log_test $? 255 "Invalid source in source list" + run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent filter_mode exclude source_list $all_zeros_grp dst $vtep_ip src_vni 10010" + log_test $? 255 "All-zeros source in source list" + run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent source_list $src1 dst $vtep_ip src_vni 10010" log_test $? 255 "Source list without filter mode" }
@@ -784,6 +787,9 @@ sg_common() run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp src $grp permanent dst $vtep_ip src_vni 10010" log_test $? 255 "(S, G) with an invalid source list" + run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp src $all_zeros_grp permanent dst $vtep_ip src_vni 10010" + log_test $? 255 "(S, G) with an all-zeros source" + run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $all_zeros_grp src $src permanent dst $vtep_ip src_vni 10010" log_test $? 255 "All-zeros group with source" }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 638/1250 watchdog: msc313e: Fix NULL pointer dereference in PM callbacks
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tzung-Bi Shih <[email protected]>
[ Upstream commit e3eceb76515910746e6268c4e4ac1c07516ebd7b ]
msc313e_wdt_probe() doesn't set the driver data for the platform device. As a result, dev_get_drvdata() in msc313e_wdt_suspend() and msc313e_wdt_resume() will return NULL, leading to a NULL pointer dereference afterward.
Set the platform device driver data in msc313e_wdt_probe().
--- drivers/watchdog/msc313e_wdt.c | 1 + 1 file changed, 1 insertion(+)
drivers/watchdog/msc313e_wdt.c +1 -0
@@ -125,6 +125,7 @@ static int msc313e_wdt_probe(struct platform_device *pdev)set_bit (WDOG_HW_RUNNING , &priv->wdev.status);watchdog_set_drvdata (&priv->wdev, priv); +platform_set_drvdata (pdev, priv);watchdog_init_timeout (&priv->wdev, timeout, dev);watchdog_stop_on_reboot (&priv->wdev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 639/1250 perf symbol: Do not use debug file as the binary type
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Adrian Hunter <[email protected]>
[ Upstream commit ae9464c65e9d1ad4df4fed516cee9bca3bc614cc ]
dso__load() sets the binary type of a DSO to the type of the first symbol source found. For a DSO with a separate debug file linked via .gnu-debuglink, that is DSO_BINARY_TYPE__DEBUGLINK, which makes dso__get_filename() return the name of the debug file instead of the file that was actually executed.
Consumers that need to read instruction bytes, such as Intel PT decoding in 'perf script', then read from the debug file and produce wrong instructions.
Prefer DSO_BINARY_TYPE__BUILD_ID_CACHE, and otherwise DSO_BINARY_TYPE__SYSTEM_PATH_DSO, over debug-only types, which restores the behaviour of using a file that contains the executed instructions.
This is a workaround. Properly separating the binary file used for instructions from the file used for debug symbols is left for later.
Example:
Create a shared object with a separate .gnu_debuglink debug file. Note that 'objcopy --only-keep-debug' leaves .text as NOBITS, so instructions read from the debug file are zeros:
# cat > foo.c << EOF
unsigned long foo_work(unsigned long n)
{
unsigned long s = 0; for (unsigned long i = 0; i < n; i++)
s = s * 31 + i;
return s;
}
EOF
# cat > main.c << EOF
#include <stdio.h>
unsigned long foo_work(unsigned long n);
int main(void)
{
printf("%lu\n", foo_work(1000));
return 0;
}
EOF
# gcc -g -O2 -shared -fPIC -o libfoo.so foo.c
# gcc -g -O2 -o main main.c -L. -lfoo -Wl,-rpath,'$ORIGIN'
# objcopy --only-keep-debug libfoo.so libfoo.so.debug
# objcopy --strip-debug libfoo.so
# objcopy --add-gnu-debuglink=libfoo.so.debug libfoo.so
# perf record -e intel_pt//u ./main Note that branch samples must be requested, because it is the resolving of the branch target symbol that causes dso__load() to be called, and hence the binary type to be set, before the decoder walks the code. With '--itrace=e' alone, nothing loads symbols for libfoo.so, the binary type is left as DSO_BINARY_TYPE__NOT_FOUND, the correct file is read anyway, and no errors are reported either way.
Before:
# perf.before script --itrace=be 2>&1 | grep "instruction trace error" instruction trace error type 1 time 2350.467489498 cpu 9 pid 75634 tid 75634 ip 0x77d48480718f code 6: Trace doesn't match instruction instruction trace error type 1 time 2350.467489832 cpu 9 pid 75634 tid 75634 ip 0x77d484807341 code 6: Trace doesn't match instruction instruction trace error type 1 time 2350.467496412 cpu 9 pid 75634 tid 75634 ip 0x5b4de37a8074 code 6: Trace doesn't match instruction instruction trace error type 1 time 2350.467593393 cpu 9 pid 75634 tid 75634 ip 0x77d4848070d0 code 6: Trace doesn't match instruction instruction trace error type 1 time 2350.467593954 cpu 9 pid 75634 tid 75634 ip 0x77d4848075a8 code 6: Trace doesn't match instruction instruction trace error type 1 time 2350.467595728 cpu 9 pid 75634 tid 75634 ip 0x77d4848324de code 6: Trace doesn't match instruction 6 instruction trace errors
After:
# perf script --itrace=be 2>&1 | grep "instruction trace error" #
--- tools/perf/util/symbol.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
tools/perf/util/symbol.c +10 -1
@@ -1829,7 +1829,16 @@ int dso__load(struct dso *dso, struct map *map)if (next_slot) { ss_pos++; -if (dso__binary_type (dso) ==DSO_BINARY_TYPE__NOT_FOUND ) +/* +* The binary type is used to find the file containing +* the executed instructions, so prefer the types that +* refer to the actual object over debug-only files such +* as DSO_BINARY_TYPE__DEBUGLINK. +*/ +if (dso__binary_type (dso) ==DSO_BINARY_TYPE__NOT_FOUND || + symtab_type ==DSO_BINARY_TYPE__BUILD_ID_CACHE || + (symtab_type ==DSO_BINARY_TYPE__SYSTEM_PATH_DSO && +dso__binary_type (dso) !=DSO_BINARY_TYPE__BUILD_ID_CACHE ))dso__set_binary_type (dso, symtab_type);if (syms_ss && runtime_ss)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 640/1250 OPP: of: Fix potential multiplication overflow when calculating freq
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Colin Ian King <[email protected]>
[ Upstream commit e11811a552252740bd396ec38378e9570ee16578 ]
The multiplication be32_to_cpup(val++) * 1000 is performed using 32 bit unsigned integers and hence uses a 32 bit multiplication; this will overflow if be32_to_cpup(val++) is greater than 4294967 (which is very unlikely at present). The result is assigned to an unsigned long (which is a 64 bit value on 64 bit systems), so fix this potential overflow by casting the first operand of the multiplication to an unsigned int.
--- drivers/opp/of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/opp/of.c +1 -1
@@ -1037,7 +1037,7 @@ static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table) val = prop->value;while (nr) { -unsigned long freq =be32_to_cpup (val++) *1000 ; +unsigned long freq = (unsigned long )be32_to_cpup (val++) *1000 ;unsigned long volt =be32_to_cpup (val++);struct dev_pm_opp_data data = { .freq = freq,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 641/1250 perf powerpc-vpadtl: Fix raw_size of DTL samples
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wang Yan <[email protected]>
[ Upstream commit aadea57f532882d8bab444646863c7ef8a778ff1 ]
In powerpc_vpadtl_sample(), raw_data of the synthetic sample points to a struct powerpc_vpadtl_entry (48 bytes), but raw_size is set to sizeof(record). record is a struct powerpc_vpadtl_entry pointer, so sizeof(record) is the size of the pointer (8 bytes on 64-bit) rather than the size of the record itself.
As a result, consumers that bound their access to raw_data by raw_size only see or copy the first 8 bytes of each DTL entry instead of the full record.
Use sizeof(*record) so that raw_size reflects the actual length of the raw data.
--- tools/perf/util/powerpc-vpadtl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/perf/util/powerpc-vpadtl.c +1 -1
@@ -192,7 +192,7 @@ static int powerpc_vpadtl_sample(struct powerpc_vpadtl_entry *record, sample.cpumode =PERF_RECORD_MISC_KERNEL ; sample.time = save; sample.raw_data = record; - sample.raw_size =sizeof (record); + sample.raw_size =sizeof (*record); event.sample.header.type =PERF_RECORD_SAMPLE ; event.sample.header.misc = sample.cpumode; event.sample.header.size =sizeof (struct perf_event_header );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 642/1250 netfs: Fix unbuffered/DIO write partial transfer error return
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit c753a33664e4e86246f7491a93d9a77c1a673b5d ]
Fix unbuffered/DIO write to return the amount of data transferred in preference to an error if a partial transfer has been achieved, and to prefer an error stashed in the request over the one returned by netfs_unbuffered_write() (likely -EINTR or -ERESTARTSYS).
--- fs/netfs/direct_write.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-)
fs/netfs/direct_write.c +5 -7
@@ -139,13 +139,11 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq)if (test_bit (NETFS_SREQ_NEED_RETRY , &subreq->flags)) { retry = true; }else if (test_bit (NETFS_SREQ_FAILED , &subreq->flags)) { - ret = subreq->error; - wreq->error = ret; + wreq->error = subreq->error;netfs_see_subrequest (subreq, netfs_sreq_trace_see_failed); subreq =NULL ;break ; } - ret =0 ;if (!retry) {netfs_unbuffered_write_collect (wreq, stream, subreq);
@@ -292,11 +290,11 @@ ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter * ret = -EIOCBQUEUED ; }else { ret =netfs_unbuffered_write (wreq); -if (ret <0 ) { -_debug ("begin = %zd" , ret); - }else { +if (wreq->transferred) { iocb->ki_pos += wreq->transferred; - ret = wreq->transferred ?: wreq->error; + ret = wreq->transferred; + }else if (wreq->error) { + ret = wreq->error; }netfs_put_request (wreq, netfs_rreq_trace_put_complete);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 643/1250 netfs: Fix error vs transferred passed to ->ki_complete()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 0bfe2571a6af653611860d0e24c4e4c83bae7a54 ]
Fix netfs_unbuffered_write_done() to pass the amount written to ->ki_complete() rather than the error in the event of a partially complete transfer.
--- fs/netfs/direct_write.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/netfs/direct_write.c +1 -1
@@ -51,7 +51,7 @@ static void netfs_unbuffered_write_done(struct netfs_io_request *wreq) wreq->iocb->ki_pos += written;if (wreq->iocb->ki_complete) {trace_netfs_rreq (wreq, netfs_rreq_trace_ki_complete); - wreq->iocb->ki_complete (wreq->iocb, wreq->error ?: written); + wreq->iocb->ki_complete (wreq->iocb, written ?: wreq->error); } wreq->iocb =VFS_PTR_POISON ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 644/1250 netfs: Fix i_size update for partial transfer
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 741416a8003b77e636dafade408f808d96ac3f47 ]
Fix netfs_unbuffered_write_done() to pass the amount written to netfs_update_i_size() in the event of a partial transfer that ends in an error.
That said, it might be better for the filesystem to mark the inode data as invalid and recheck it in case something like a network error occurred that prevented the reply from the server from being received.
--- fs/netfs/direct_write.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/netfs/direct_write.c +1 -1
@@ -21,7 +21,7 @@ static void netfs_unbuffered_write_done(struct netfs_io_request *wreq)/* Okay, declare that all I/O is complete. */ trace_netfs_rreq (wreq, netfs_rreq_trace_write_done); -if (!wreq->error) +if (wreq->transferred)netfs_update_i_size (ictx, &ictx->inode, wreq->start, wreq->transferred); if (wreq->origin == NETFS_DIO_WRITE &&
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 645/1250 netfs: Fix subreq ref leak
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 3c30087e27598d9d359763e8be9bd3017fe08348 ]
Fix a subrequest ref leak in netfs_unbuffered_write() in the event that subreq->io_iter ends up zero length during preparation.
--- fs/netfs/direct_write.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
fs/netfs/direct_write.c +7 -1
@@ -121,8 +121,14 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq) }iov_iter_truncate (&subreq->io_iter, wreq->len - wreq->transferred); -if (!iov_iter_count (&subreq->io_iter)) +if (!iov_iter_count (&subreq->io_iter)) { +pr_warn ("netfs: Unexpected zero-length iterator R=%08x\n" , + wreq->debug_id); +__set_bit (NETFS_SREQ_FAILED , &subreq->flags); +netfs_write_subrequest_terminated (subreq, -EIO ); + wreq->error = -EIO ;break ; + } subreq->len = netfs_limit_iter(&subreq->io_iter, 0, stream->sreq_max_len,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 646/1250 netfs: break unbuffered write when netfs_alloc_subrequest() fails
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Edward Adam Davis <[email protected]>
[ Upstream commit 8fb45a934661419c04a44d4cfea1e0df7dcf2805 ]
syzbot reported a null-ptr-deref below [1] following a fault injection in netfs_alloc_subrequest(). [0]
When netfs_alloc_subrequest() fails, subreq is NULL. Later, netfs_prepare_write() tries to initialize members of subreq(e.g., source), the issue in [1] is triggered.
Let's handle the error of netfs_prepare_write() properly.
[0]FAULT_INJECTION: forcing a failure.
name failslab, interval 1, probability 0, space 0, times 0
Call Trace: netfs_alloc_subrequest+0x116/0x3f0 netfs_prepare_write+0x76/0x7b0 netfs_unbuffered_write+0x75c/0x2020 netfs_unbuffered_write_iter_locked+0x7d6/0xa80 netfs_unbuffered_write_iter+0x442/0x720 v9fs_file_write_iter+0xbf/0x100 vfs_write+0x6ac/0x1050
[1]
KASAN: null-ptr-deref in range [0x00000000000000a8-0x00000000000000af] RIP: 0010:netfs_prepare_write+0xbc/0x7b0 fs/netfs/write_issue.c:173 Call Trace: netfs_unbuffered_write+0x75c/0x2020 fs/netfs/direct_write.c:111 netfs_unbuffered_write_iter_locked+0x7d6/0xa80 fs/netfs/direct_write.c:290 netfs_unbuffered_write_iter+0x442/0x720 fs/netfs/direct_write.c:382 v9fs_file_write_iter+0xbf/0x100 fs/9p/vfs_file.c:409 new_sync_write fs/read_write.c:595 [inline]
--- fs/netfs/direct_write.c | 5 +++++ fs/netfs/write_issue.c | 2 ++ 2 files changed, 7 insertions(+)
fs/netfs/direct_write.c +5 -0
@@ -110,6 +110,11 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq)if (!subreq) {netfs_prepare_write (wreq, stream, wreq->start + wreq->transferred); subreq = stream->construct; +if (!subreq) { + wreq->error = -ENOMEM ; + ret = -ENOMEM ; +break ; + } stream->construct =NULL ; }
fs/netfs/write_issue.c +2 -0
@@ -170,6 +170,8 @@ void netfs_prepare_write(struct netfs_io_request *wreq,rolling_buffer_make_space (&wreq->buffer); subreq =netfs_alloc_subrequest (wreq); +if (!subreq) +return ; subreq->source = stream->source; subreq->start = start; subreq->stream_nr = stream->stream_nr;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 647/1250 cachefiles: Fix potential UAF/KASAN warning
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit a67632c8c2688d6e0091529bcefe54bc5ee80e9b ]
Currently, trace_cachefiles_coherency() is being passed a pointer to a __be64 lain over the coherency data in struct cachefiles_xattr so that it can display the first 8 bytes. However, the data is of variable length and could even be 0 bytes. This could lead to a UAF or KASAN warning.
Fix this by making sure the buffer has room for at least 8 bytes and that those 8 bytes are pre-cleared.
Further, those bytes are not 8-byte aligned, so fix the tracepoint to extract the data as four 2-byte words (they are 2-byte aligned) and reassemble the __be64. The compiler will convert this into a single 8-byte load where the CPU supports it.
--- fs/cachefiles/xattr.c | 16 ++++++++-------- include/trace/events/cachefiles.h | 19 +++++++++++++++++-- 2 files changed, 25 insertions(+), 10 deletions(-)
fs/cachefiles/xattr.c +8 -8
@@ -13,6 +13,7 @@#include <linux/quotaops.h> #include <linux/xattr.h> #include <linux/slab.h> +#include <linux/unaligned.h> #include "internal.h" #define CACHEFILES_COOKIE_TYPE_DATA 1
@@ -50,7 +51,7 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object)_enter ("%x,#%d" , object->debug_id, len); - buf =kmalloc (sizeof (struct cachefiles_xattr ) + len,GFP_KERNEL ); + buf =kmalloc (sizeof (struct cachefiles_xattr ) +max (len,sizeof (__be64)),GFP_KERNEL );if (!buf)return -ENOMEM ;
@@ -60,6 +61,7 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object) buf->content = object->content_info;if (test_bit (FSCACHE_COOKIE_LOCAL_WRITE , &object->cookie->flags)) buf->content =CACHEFILES_CONTENT_DIRTY ; +put_unaligned_be64 (0 , (__be64 *)buf->data);if (len >0 )memcpy (buf->data,fscache_get_aux (object->cookie), len);
@@ -77,8 +79,7 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object)trace_cachefiles_vfs_error (object,file_inode (file), ret, cachefiles_trace_setxattr_error);trace_cachefiles_coherency (object,file_inode (file)->i_ino, -be64_to_cpup ((__be64 *)buf->data), - buf->content, + buf->data, buf->content, cachefiles_coherency_set_fail);if (ret != -ENOMEM ) cachefiles_io_error_obj(
@@ -86,8 +87,7 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object) "Failed to set xattr with error %d", ret); }else {trace_cachefiles_coherency (object,file_inode (file)->i_ino, -be64_to_cpup ((__be64 *)buf->data), - buf->content, + buf->data, buf->content, cachefiles_coherency_set_ok); }
@@ -110,9 +110,10 @@ int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *fileint ret = -ESTALE ; tlen =sizeof (struct cachefiles_xattr ) + len; - buf =kmalloc (tlen,GFP_KERNEL ); + buf =kmalloc (sizeof (struct cachefiles_xattr ) +max (len,sizeof (__be64)),GFP_KERNEL );if (!buf)return -ENOMEM ; +put_unaligned_be64 (0 , (__be64 *)buf->data); xlen =cachefiles_inject_read_error ();if (xlen ==0 )
@@ -148,8 +149,7 @@ int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file out:trace_cachefiles_coherency (object,file_inode (file)->i_ino, -be64_to_cpup ((__be64 *)buf->data), - buf->content, why); + buf->data, buf->content, why);kfree (buf);return ret; }
include/trace/events/cachefiles.h +17 -2
@@ -380,7 +380,7 @@ TRACE_EVENT(cachefiles_rename, TRACE_EVENT(cachefiles_coherency, TP_PROTO(struct cachefiles_object *obj, ino_t ino, - u64 disk_aux, + const void *disk_aux, enum cachefiles_content content, enum cachefiles_coherency_trace why),
@@ -397,12 +397,27 @@ TRACE_EVENT(cachefiles_coherency, ),TP_fast_assign ( +union { +__be16 s[4 ]; +__be64 ll; + } x; + __entry->obj = obj->debug_id; __entry->why = why; __entry->content = content; __entry->ino = ino; __entry->aux =be64_to_cpup ((__be64 *)obj->cookie->inline_aux); - __entry->disk_aux = disk_aux; + +/* cachefiles_xattr::data is 2-byte aligned but not 8-byte aligned. */ +if (disk_aux) { + x.s[0 ] = ((__be16 *)disk_aux)[0 ]; + x.s[1 ] = ((__be16 *)disk_aux)[1 ]; + x.s[2 ] = ((__be16 *)disk_aux)[2 ]; + x.s[3 ] = ((__be16 *)disk_aux)[3 ]; + __entry->disk_aux =be64_to_cpu (x.ll); + }else { + __entry->disk_aux =0 ; + } ), TP_printk("o=%08x %s B=%llx c=%u aux=%llx dsk=%llx",
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 648/1250 ALSA: pcm: Serialize PCM mmap with buffer reallocation to fix page UAF
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yilin Zhang <[email protected]>
[ Upstream commit 9b110a9dcecc59516c77cb3c0caf1f492f75df2d ]
snd_pcm_hw_params() and snd_pcm_hw_free() guard buffer reallocation with an mmap_count check performed under the PCM stream lock, but the lock is released long before the buffer is actually freed:snd_pcm_sync_stop(), constraint refinement and do_free_pages() all happen in between. snd_pcm_mmap_data(), on the other hand, takes no lock at all: it validates against the old buffer's state and dma_bytes, remaps its pages into the VMA, and only then increments mmap_count.
A concurrent mmap() can therefore slip in between the check and the free. remap_pfn_range() installs writable PTEs for the old buffer's pages without taking page references, and the subsequent do_free_pages() returns those pages to the page allocator while the VMA still maps them. This leaves a stale, writable mapping of freed
pages: a page-level use-after-free that can be leveraged for local privilege escalation.
Make snd_pcm_mmap_data() participate in the buffer-access scheme introduced for hw_params/hw_free: acquire runtime->buffer_accessing before validating and remapping, and release it afterwards. Buffer reallocation already fails with -EBUSY while accessors are active, and the mmap side now fails with -EBUSY while a reallocation is in progress, so the validate/remap sequence and the check/free sequence can no longer interleave.
A reproducer that turns this race into a stale writable mapping of the freed DMA buffer pages is available on request.
--- sound/core/pcm_native.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-)
sound/core/pcm_native.c +25 -10
@@ -3991,20 +3991,33 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,return -EINVAL ; } runtime = substream->runtime; -if (runtime->state ==SNDRV_PCM_STATE_OPEN ) -return -EBADFD ; -if (!(runtime->info &SNDRV_PCM_INFO_MMAP )) -return -ENXIO ; +/* don't race with buffer reallocation in hw_params/hw_free */ +if (!atomic_inc_unless_negative (&runtime->buffer_accessing)) +return -EBUSY ; +if (runtime->state ==SNDRV_PCM_STATE_OPEN ) { + err = -EBADFD ; + goto out; + } +if (!(runtime->info &SNDRV_PCM_INFO_MMAP )) { + err = -ENXIO ; + goto out; + }if (runtime->access ==SNDRV_PCM_ACCESS_RW_INTERLEAVED || - runtime->access ==SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ) -return -EINVAL ; + runtime->access ==SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ) { + err = -EINVAL ; + goto out; + } size = area->vm_end - area->vm_start; offset = area->vm_pgoff <<PAGE_SHIFT ; dma_bytes =PAGE_ALIGN (runtime->dma_bytes); -if ((size_t )size > dma_bytes) -return -EINVAL ; -if (offset > dma_bytes - size) -return -EINVAL ; +if ((size_t )size > dma_bytes) { + err = -EINVAL ; + goto out; + } +if (offset > dma_bytes - size) { + err = -EINVAL ; + goto out; + } area->vm_ops = &snd_pcm_vm_ops_data; area->vm_private_data = substream;
@@ -4014,6 +4027,8 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, err =snd_pcm_lib_default_mmap (substream, area);if (!err)atomic_inc (&substream->mmap_count); +out: +atomic_dec (&runtime->buffer_accessing);return err; }EXPORT_SYMBOL (snd_pcm_mmap_data);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 649/1250 ksmbd: propagate DACL parsing errors
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit c61dc7b1b4a3234b4aa3965502908a292238805c ]
parse_dacl() silently accepts truncated ACEs and allocation failures, allowing set_info_sec() to continue with an incomplete ACL conversion.
Return parsing and allocation errors to parse_sec_desc() so malformed security descriptors are rejected before inode attributes or ACL xattrs are updated.
--- fs/smb/server/smbacl.c | 63 +++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 25 deletions(-)
fs/smb/server/smbacl.c +38 -25
@@ -380,10 +380,10 @@ void free_acl_state(struct posix_acl_state *state)kfree (state->groups); } -static void parse_dacl (struct mnt_idmap *idmap, -struct smb_acl *pdacl,char *end_of_acl, -struct smb_sid *pownersid,struct smb_sid *pgrpsid, -struct smb_fattr *fattr) +static int parse_dacl (struct mnt_idmap *idmap, +struct smb_acl *pdacl,char *end_of_acl, +struct smb_sid *pownersid,struct smb_sid *pgrpsid, +struct smb_fattr *fattr) {int i, ret;u16 num_aces =0 ;
@@ -397,13 +397,13 @@ static void parse_dacl(struct mnt_idmap *idmap,bool owner_found = false, group_found = false, others_found = false;if (!pdacl) -return ; +return 0 ;/* validate that we do not go past end of acl */ if (end_of_acl < (char *)pdacl +sizeof (struct smb_acl ) || end_of_acl < (char *)pdacl +le16_to_cpu (pdacl->size)) {pr_err ("ACL too small to parse DACL\n" ); -return ; +return -EINVAL ; } ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n",
@@ -415,31 +415,31 @@ static void parse_dacl(struct mnt_idmap *idmap, num_aces =le16_to_cpu (pdacl->num_aces);if (num_aces <=0 ) -return ; +return 0 ; dacl_size =le16_to_cpu (pdacl->size);if (dacl_size <sizeof (struct smb_acl )) -return ; +return -EINVAL ;if (num_aces > (dacl_size -sizeof (struct smb_acl )) / (offsetof(struct smb_ace , sid) + offsetof(struct smb_sid , sub_auth) +sizeof (__le16))) -return ; +return -EINVAL ; ret =init_acl_state (&acl_state, num_aces);if (ret) -return ; +return ret; ret =init_acl_state (&default_acl_state, num_aces);if (ret) {free_acl_state (&acl_state); -return ; +return ret; } ppace =kmalloc_array (num_aces,sizeof (struct smb_ace *),KSMBD_DEFAULT_GFP );if (!ppace) {free_acl_state (&default_acl_state);free_acl_state (&acl_state); -return ; +return -ENOMEM ; }/*
@@ -448,8 +448,10 @@ static void parse_dacl(struct mnt_idmap *idmap,* user/group/other have no permissions */ for (i =0 ; i < num_aces; ++i) { -if (end_of_acl - acl_base < acl_size) -break ; +if (end_of_acl - acl_base < acl_size) { + ret = -EINVAL ; + goto out; + } ppace[i] = (struct smb_ace *)(acl_base + acl_size); acl_base = (char *)ppace[i];
@@ -462,8 +464,10 @@ static void parse_dacl(struct mnt_idmap *idmap, (end_of_acl - acl_base < acl_size +sizeof (__le32) * ppace[i]->sid.num_subauth) || (le16_to_cpu (ppace[i]->size) < - acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) - break; + acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) { + ret = -EINVAL; + goto out; + } acl_size =le16_to_cpu (ppace[i]->size); ppace[i]->access_req =
@@ -538,7 +542,6 @@ static void parse_dacl(struct mnt_idmap *idmap, ((acl_mode &0700 ) >>6 ) |0004 ; } } -kfree (ppace);if (owner_found) {/* The owner must be set to at least read-only. */
@@ -581,10 +584,12 @@ static void parse_dacl(struct mnt_idmap *idmap, fattr->cf_acls =posix_acl_alloc (acl_state.users->n + acl_state.groups->n +4 ,KSMBD_DEFAULT_GFP ); -if (fattr->cf_acls) { - cf_pace = fattr->cf_acls->a_entries; -posix_state_to_acl (&acl_state, cf_pace); +if (!fattr->cf_acls) { + ret = -ENOMEM ; + goto out; } + cf_pace = fattr->cf_acls->a_entries; +posix_state_to_acl (&acl_state, cf_pace); } }
@@ -595,14 +600,20 @@ static void parse_dacl(struct mnt_idmap *idmap, fattr->cf_dacls =posix_acl_alloc (default_acl_state.users->n + default_acl_state.groups->n +4 ,KSMBD_DEFAULT_GFP ); -if (fattr->cf_dacls) { - cf_pdace = fattr->cf_dacls->a_entries; -posix_state_to_acl (&default_acl_state, cf_pdace); +if (!fattr->cf_dacls) { + ret = -ENOMEM ; + goto out; } + cf_pdace = fattr->cf_dacls->a_entries; +posix_state_to_acl (&default_acl_state, cf_pdace); } } + ret =0 ; +out: +kfree (ppace);free_acl_state (&acl_state);free_acl_state (&default_acl_state); +return ret; } static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
@@ -963,8 +974,10 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,if (dacloffset <sizeof (struct smb_ntsd ))return -EINVAL ; -parse_dacl (idmap, dacl_ptr, end_of_acl, - owner_sid_ptr, group_sid_ptr, fattr); + rc =parse_dacl (idmap, dacl_ptr, end_of_acl, + owner_sid_ptr, group_sid_ptr, fattr); +if (rc) +return rc; }return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 650/1250 ksmbd: rate limit unmapped SID errors
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit feca5e70fc963b088377b20879e8cd8237c2fd7d ]
A client can include many structurally valid but unmapped SIDs in a DACL. Logging every mapping failure lets one request generate hundreds of kernel error messages.
Rate limit the message to prevent an authenticated client from flooding the kernel log.
--- fs/smb/server/smbacl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/smb/server/smbacl.c +2 -2
@@ -525,8 +525,8 @@ static int parse_dacl(struct mnt_idmap *idmap, temp_fattr.cf_uid =INVALID_UID ; ret =sid_to_id (idmap, &ppace[i]->sid,SIDOWNER , &temp_fattr);if (ret ||uid_eq (temp_fattr.cf_uid,INVALID_UID )) { -pr_err ("%s: Error %d mapping Owner SID to uid\n" , - __func__, ret); +pr_err_ratelimited ("%s: Error %d mapping Owner SID to uid\n" , + __func__, ret);continue ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 651/1250 s390/vtime: Use __this_cpu_read() / get rid of READ_ONCE()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Heiko Carstens <[email protected]>
[ Upstream commit e282ccd308de7b2a65eb4596da8030c18b543071 ]
do_account_vtime() runs always with interrupts disabled, therefore use __this_cpu_read() instead of this_cpu_read() to get rid of a pointless preempt_disable() / preempt_enable() pair.
Also there are no concurrent writers to the cpu time accounting fields in lowcore. Therefore get rid of READ_ONCE() usages.
Stable-dep-of: ca1f4a5ecab0 ("s390/time: Use jiffies instead of jiffies_64")
--- arch/s390/kernel/vtime.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-)
arch/s390/kernel/vtime.c +7 -14
@@ -137,23 +137,16 @@ static int do_account_vtime(struct task_struct *tsk) lc->system_timer += timer;/* Update MT utilization calculation */ -if (smp_cpu_mtid && -time_after64 (jiffies_64,this_cpu_read (mt_scaling_jiffies))) +if (smp_cpu_mtid &&time_after64 (jiffies_64,__this_cpu_read (mt_scaling_jiffies)))update_mt_scaling ();/* Calculate cputime delta */ - user =update_tsk_timer (&tsk->thread.user_timer, -READ_ONCE (lc->user_timer)); - guest =update_tsk_timer (&tsk->thread.guest_timer, -READ_ONCE (lc->guest_timer)); - system =update_tsk_timer (&tsk->thread.system_timer, -READ_ONCE (lc->system_timer)); - hardirq =update_tsk_timer (&tsk->thread.hardirq_timer, -READ_ONCE (lc->hardirq_timer)); - softirq =update_tsk_timer (&tsk->thread.softirq_timer, -READ_ONCE (lc->softirq_timer)); - lc->steal_timer += - clock - user - guest - system - hardirq - softirq; + user =update_tsk_timer (&tsk->thread.user_timer, lc->user_timer); + guest =update_tsk_timer (&tsk->thread.guest_timer, lc->guest_timer); + system =update_tsk_timer (&tsk->thread.system_timer, lc->system_timer); + hardirq =update_tsk_timer (&tsk->thread.hardirq_timer, lc->hardirq_timer); + softirq =update_tsk_timer (&tsk->thread.softirq_timer, lc->softirq_timer); + lc->steal_timer += clock - user - guest - system - hardirq - softirq;/* Push account value */ if (user) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 652/1250 s390/time: Use jiffies instead of jiffies_64
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Heiko Carstens <[email protected]>
[ Upstream commit ca1f4a5ecab084af7f405baa902edbed171b57e6 ]
Christoph Schlameuss and Alexander Egorenkov reported a data-race reported by KCSAN when jiffies_64 is read:
==================================================================
BUG: KCSAN: data-race in do_account_vtime / tick_do_update_jiffies64
write to 0x0000016599ea8600 of 8 bytes by interrupt on cpu 6:
tick_do_update_jiffies64+0x140/0x250
=============================================================>
BUG: KCSAN: data-race in do_account_vtime / tick_do_update_ji>
write to 0x0000016599ea8600 of 8 bytes by interrupt on cpu 6:
tick_do_update_jiffies64+0x140/0x250 tick_nohz_handler+0x2e6/0x300 __run_hrtimer+0x156/0x4d0 __hrtimer_run_queues+0xd2/0x150
...
system_call+0x72/0x90
read to 0x0000016599ea8600 of 8 bytes by interrupt on cpu 12:
do_account_vtime+0x7d6/0x860 vtime_flush+0x26/0xe0 update_process_times+0x32/0x160 tick_nohz_handler+0x12a/0x300
...
system_call+0x72/0x90
value changed: 0x00000000ffffaa6c -> 0x00000000ffffaa6d
...
=============================================================>
Problem is that jiffies_64 instead of jiffies is used. Both are at the same address, but only jiffies is of volatile type, which prevents this warning.
Change the vtime code so jiffies instead of jiffies_64 is used everywhere. This addresses also the inconsistency that both jiffies and jiffies_64 were used in the original patch which introduced this.
--- arch/s390/kernel/vtime.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
arch/s390/kernel/vtime.c +3 -3
@@ -31,7 +31,7 @@ static atomic64_t virt_timer_elapsed;DEFINE_PER_CPU (u64, mt_cycles[8 ]); static DEFINE_PER_CPU(u64, mt_scaling_mult) = { 1 }; static DEFINE_PER_CPU(u64, mt_scaling_div) = { 1 }; -static DEFINE_PER_CPU(u64, mt_scaling_jiffies); +static DEFINE_PER_CPU(unsigned long, mt_scaling_jiffies);static inline void set_vtimer (u64 expires) {
@@ -81,7 +81,7 @@ static void update_mt_scaling(void)memcpy (cycles_old, cycles_new,sizeof (u64) * (smp_cpu_mtid +1 )); } -__this_cpu_write (mt_scaling_jiffies, jiffies_64); +__this_cpu_write (mt_scaling_jiffies, jiffies); }static inline u64 update_tsk_timer (unsigned long *tsk_vtime,u64 new)
@@ -137,7 +137,7 @@ static int do_account_vtime(struct task_struct *tsk) lc->system_timer += timer;/* Update MT utilization calculation */ -if (smp_cpu_mtid &&time_after64 (jiffies_64,__this_cpu_read (mt_scaling_jiffies))) +if (smp_cpu_mtid &&time_after (jiffies,__this_cpu_read (mt_scaling_jiffies)))update_mt_scaling ();/* Calculate cputime delta */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 653/1250 s390/ipl: Fix NULL deref in kdump without re-IPL parm block
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasily Gorbik <[email protected]>
[ Upstream commit 7f918871112e8e7c581e99eb8e545af4e59c8367 ]
Some IPL types, like HMC FTP boot or QEMU direct kernel boot, might not provide an IPL parameter block. In this case, reipl_type_init() selects IPL_TYPE_UNKNOWN, and reipl_block_actual remains NULL.
kdump passes the re-IPL parameter block to the dump kernel through os_info. Before commit 3b9678472bab ("s390/ipl: correct kdump reipl block checksum calculation"), the os_info entry was added only for IPL types which initialized reipl_block_actual. That commit moved the os_info update to machine_crash_shutdown(), making it unconditional. As a result, set_os_info_reipl_block() dereferences reipl_block_actual for IPL_TYPE_UNKNOWN. This may happen to work by chance when address zero contains readable lowcore data and the resulting empty os_info entry is ignored by the dump kernel.
Skip the os_info update when no re-IPL parameter block is available. Kdump then collect the dump and reboot without setting re-IPL parameter block.
--- arch/s390/kernel/ipl.c | 2 ++ 1 file changed, 2 insertions(+)
arch/s390/kernel/ipl.c +2 -0
@@ -1157,6 +1157,8 @@ static struct attribute_group reipl_nss_attr_group = {void set_os_info_reipl_block (void ) { +if (!reipl_block_actual) +return ;os_info_entry_add_data (OS_INFO_REIPL_BLOCK , reipl_block_actual, reipl_block_actual->hdr.len); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 654/1250 s390/ipl: Fix NULL deref in dump_reipl without re-IPL parm block
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasily Gorbik <[email protected]>
[ Upstream commit 37f61b71cbc0caefc01022a19ee56fc2510e2e6e ]
Unlike kdump, which passes the re-IPL parameter block through os_info, the stand-alone dump passes it through the IPL parm block address and checksum in lowcore.
Some IPL types, like HMC FTP boot or QEMU direct kernel boot, might not provide an IPL parameter block. In this case reipl_type_init() selects IPL_TYPE_UNKNOWN and reipl_block_actual remains NULL. Nevertheless, dump_reipl_run() unconditionally dereferences it when preparing the lowcore fields. This may happen to work by chance when address zero contains readable lowcore data. A zero IPL parameter block address is then stored in lowcore, causing the stand-alone dumper to enter disabled wait after completing the dump.
Explicitly store a zero IPL parameter block address and checksum when no re-IPL parameter block is available. This does not change the behavior:
the stand-alone dumper completes the dump and halts, while valid re-IPL parameter blocks continue to be handled as before.
--- arch/s390/kernel/ipl.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
arch/s390/kernel/ipl.c +7 -3
@@ -1929,7 +1929,8 @@ static struct shutdown_action __refdata dump_action = {static void dump_reipl_run (struct shutdown_trigger *trigger) {struct lowcore *abs_lc; -unsigned int csum; +unsigned long ipib =0 ; +unsigned int csum =0 ;/* * Set REIPL_CLEAR flag in os_info flags entry indicating
@@ -1945,9 +1946,12 @@ static void dump_reipl_run(struct shutdown_trigger *trigger) reipl_type == IPL_TYPE_UNKNOWN) os_info_flags |=OS_INFO_FLAG_REIPL_CLEAR ;os_info_entry_add_data (OS_INFO_FLAGS_ENTRY , &os_info_flags,sizeof (os_info_flags)); - csum = (__force unsigned int )cksm (reipl_block_actual, reipl_block_actual->hdr.len,0 ); +if (reipl_block_actual) { + ipib =__pa (reipl_block_actual); + csum = (__force unsigned int )cksm (reipl_block_actual, reipl_block_actual->hdr.len,0 ); + } abs_lc =get_abs_lowcore (); - abs_lc->ipib =__pa (reipl_block_actual); + abs_lc->ipib = ipib; abs_lc->ipib_checksum = csum;put_abs_lowcore (abs_lc);dump_run (trigger);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 655/1250 s390/diag324: Preserve -EBUSY return code
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sumanth Korikkar <[email protected]>
[ Upstream commit 439077c39d8f7108aea4dd8d4d819b9b864fe84c ]
When diag324 reports -EBUSY, the error code is
overwritten by the result of copy_to_user() and put_user(). As a result, the ioctl may incorrectly return success instead of -EBUSY.
Preserve the original diag324 return code and only return -EFAULT when copying data to userspace fails.
--- arch/s390/kernel/diag/diag324.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
arch/s390/kernel/diag/diag324.c +1 -2
@@ -182,8 +182,7 @@ long diag324_pibbuf(unsigned long arg) goto out; rc =copy_to_user ((void __user *)address, data->pib, data->pib->len); rc |=put_user (data->sequence, &udata->sequence); -if (rc) - rc = -EFAULT ; + rc = rc ? -EFAULT : data->rc; out:mutex_unlock (&pibmutex);return rc;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 656/1250 sched_ext: Fix timer pinning and return value in scx_central
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wanwu Li <[email protected]>
[ Upstream commit 23761359861ca4bb087540937dfea8b0716914c2 ]
central_timerfn() re-arms the timer with a hardcoded BPF_F_TIMER_CPU_PIN flag and ignores the return value, defeating start_central_timer()'s -EINVAL fallback for kernels without the flag (<6.7): on such kernels the first tick kills the timer permanently with no diagnostic. Honor timer_pinned and check the return like the initial arm does.
--- tools/sched_ext/scx_central.bpf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
tools/sched_ext/scx_central.bpf.c +5 -1
@@ -256,6 +256,7 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer)u64 now =scx_bpf_now ();u64 nr_to_kick = nr_queued;s32 i, curr_cpu; +int ret; curr_cpu =bpf_get_smp_processor_id ();if (timer_pinned && (curr_cpu != central_cpu)) {
@@ -289,7 +290,10 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer)scx_bpf_kick_cpu (cpu,SCX_KICK_PREEMPT ); } -bpf_timer_start (timer,TIMER_INTERVAL_NS ,BPF_F_TIMER_CPU_PIN ); + ret =bpf_timer_start (timer,TIMER_INTERVAL_NS , + timer_pinned ?BPF_F_TIMER_CPU_PIN :0 ); +if (ret) +scx_bpf_error ("bpf_timer_start failed (%d)" , ret);__sync_fetch_and_add (&nr_timers,1 );return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 657/1250 sched_ext: Update demo schedulers and selftests to use scx_bpf_task_set_dsq_vtime()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cheng-Yang Chou <[email protected]>
[ Upstream commit 6712c4fefca0422851b71d1a58a32ea03f69310f ]
Direct writes to p->scx.dsq_vtime are deprecated in favor of scx_bpf_task_set_dsq_vtime(). Update scx_simple, scx_flatcg, and select_cpu_vtime selftest to use the new kfunc with scale_by_task_weight_inverse().
Stable-dep-of: b6ee92d7f7f0 ("sched_ext: Fix vtime delta loss in scx_flatcg cgroup migration")
--- tools/sched_ext/scx_flatcg.bpf.c | 12 +++++++----- tools/sched_ext/scx_simple.bpf.c | 6 ++++-- .../selftests/sched_ext/select_cpu_vtime.bpf.c | 6 ++++-- 3 files changed, 15 insertions(+), 9 deletions(-)
tools/sched_ext/scx_flatcg.bpf.c +7 -5
@@ -551,9 +551,11 @@ void BPF_STRUCT_OPS(fcg_stopping, struct task_struct *p, bool runnable)* too much, determine the execution time by taking explicit timestamps * instead of depending on @p->scx.slice. */ -if (!fifo_sched) - p->scx.dsq_vtime += - (SCX_SLICE_DFL - p->scx.slice) *100 / p->scx.weight; +if (!fifo_sched) { +u64 delta =scale_by_task_weight_inverse (p,SCX_SLICE_DFL - p->scx.slice); + +scx_bpf_task_set_dsq_vtime (p, p->scx.dsq_vtime + delta); + } taskc =bpf_task_storage_get (&task_ctx, p,0 ,0 );if (!taskc) {
@@ -831,7 +833,7 @@ s32 BPF_STRUCT_OPS(fcg_init_task, struct task_struct *p,if (!(cgc =find_cgrp_ctx (args->cgroup)))return -ENOENT ; - p->scx.dsq_vtime = cgc->tvtime_now; +scx_bpf_task_set_dsq_vtime (p, cgc->tvtime_now);return 0 ; }
@@ -931,7 +933,7 @@ void BPF_STRUCT_OPS(fcg_cgroup_move, struct task_struct *p,return ; delta =time_delta (p->scx.dsq_vtime, from_cgc->tvtime_now); - p->scx.dsq_vtime = to_cgc->tvtime_now + delta; +scx_bpf_task_set_dsq_vtime (p, to_cgc->tvtime_now + delta); }s32 BPF_STRUCT_OPS_SLEEPABLE (fcg_init )
tools/sched_ext/scx_simple.bpf.c +4 -2
@@ -121,12 +121,14 @@ void BPF_STRUCT_OPS(simple_stopping, struct task_struct *p, bool runnable)* too much, determine the execution time by taking explicit timestamps * instead of depending on @p->scx.slice. */ - p->scx.dsq_vtime += (SCX_SLICE_DFL - p->scx.slice) *100 / p->scx.weight; +u64 delta =scale_by_task_weight_inverse (p,SCX_SLICE_DFL - p->scx.slice); + +scx_bpf_task_set_dsq_vtime (p, p->scx.dsq_vtime + delta); }void BPF_STRUCT_OPS (simple_enable ,struct task_struct *p) { - p->scx.dsq_vtime = vtime_now; +scx_bpf_task_set_dsq_vtime (p, vtime_now); }s32 BPF_STRUCT_OPS_SLEEPABLE (simple_init )
tools/testing/selftests/sched_ext/select_cpu_vtime.bpf.c +4 -2
@@ -66,12 +66,14 @@ void BPF_STRUCT_OPS(select_cpu_vtime_running, struct task_struct *p)void BPF_STRUCT_OPS (select_cpu_vtime_stopping ,struct task_struct *p,bool runnable) { - p->scx.dsq_vtime += (SCX_SLICE_DFL - p->scx.slice) *100 / p->scx.weight; +u64 delta =scale_by_task_weight_inverse (p,SCX_SLICE_DFL - p->scx.slice); + +scx_bpf_task_set_dsq_vtime (p, p->scx.dsq_vtime + delta); }void BPF_STRUCT_OPS (select_cpu_vtime_enable ,struct task_struct *p) { - p->scx.dsq_vtime = vtime_now; +scx_bpf_task_set_dsq_vtime (p, vtime_now); }s32 BPF_STRUCT_OPS_SLEEPABLE (select_cpu_vtime_init )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 658/1250 sched_ext: Fix vtime delta loss in scx_flatcg cgroup migration
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wanwu Li <[email protected]>
[ Upstream commit b6ee92d7f7f0498d1f776d0b125a2f6bcedf0891 ]
fcg_cgroup_move() lost the signed vtime offset across cgroup migration in the mechanical conversion to time helpers:time_delta() clamps negative deltas to 0, so a queued task (whose dsq_vtime is normally behind the source frontier) loses its accumulated vtime credit and lands exactly at the destination frontier instead of keeping its relative position. Restore the wrapping signed subtraction.
--- tools/sched_ext/scx_flatcg.bpf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/sched_ext/scx_flatcg.bpf.c +1 -1
@@ -932,7 +932,7 @@ void BPF_STRUCT_OPS(fcg_cgroup_move, struct task_struct *p,if (!(from_cgc =find_cgrp_ctx (from)) || !(to_cgc =find_cgrp_ctx (to)))return ; - delta =time_delta (p->scx.dsq_vtime, from_cgc->tvtime_now); + delta = (s64)(p->scx.dsq_vtime - from_cgc->tvtime_now);scx_bpf_task_set_dsq_vtime (p, to_cgc->tvtime_now + delta); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 659/1250 Bluetooth: btintel_pcie: Clear automask on spurious interrupts
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kiran K <[email protected]>
[ Upstream commit ea2ee8b222306208d2b094d1a11894da6c106d42 ]
On spurious interrupt where the TX and RX causes are not set, driver was not clearing the auto mask which can block all the interrupts. Driver needs to clear the automask even if no causes are set.
--- drivers/bluetooth/btintel_pcie.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/bluetooth/btintel_pcie.c +3 -0
@@ -1462,6 +1462,9 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)if (unlikely (!(intr_fh | intr_hw))) {/* Ignore interrupt, inta == 0 */ +bt_warn_ratelimited ("Bluetooth: btintel_pcie: Received spurious interrupt\n" ); +btintel_pcie_wr_reg32 (data,BTINTEL_PCIE_CSR_MSIX_AUTOMASK_ST , +BIT (entry->entry));return IRQ_NONE ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 660/1250 workqueue: reject watchdog thresholds that overflow jiffies
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiacheng Xu <[email protected]>
[ Upstream commit 068c35b5d0546c8625b3d7c61910f73775cf1216 ]
The watchdog threshold is supplied in seconds but is multiplied by HZ before being used as a jiffies interval. Reject values that exceed MAX_JIFFY_OFFSET / HZ so the multiplication cannot wrap and the time_after() comparisons remain within their supported range.
The check is performed before changing the threshold or watchdog timer. Zero remains the value used to disable the watchdog.
--- kernel/workqueue.c | 3 +++ 1 file changed, 3 insertions(+)
kernel/workqueue.c +3 -0
@@ -7736,6 +7736,9 @@ static int wq_watchdog_param_set_thresh(const char *val,if (ret)return ret; +if (thresh >MAX_JIFFY_OFFSET /HZ ) +return -ERANGE ; +if (system_percpu_wq)wq_watchdog_set_thresh (thresh);else
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 661/1250 Bluetooth: btintel: validate version TLV value lengths
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Laxman Acharya Padhya <[email protected]>
[ Upstream commit a086c0892969bf8a0151b0f12bd14a68827c88b2 ]
btintel_parse_version_tlv() verifies that a complete TLV is present in the response, but it does not ensure that the value is long enough for the specific TLV type. A short value can therefore cause an out-of-bounds read through get_unaligned_le16(), get_unaligned_le32(), or memcpy().
Reject values shorter than the minimum required by each known TLV type. Also reject responses that do not contain the Command Complete Status field.
--- drivers/bluetooth/btintel.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-)
drivers/bluetooth/btintel.c +36 -1
@@ -554,12 +554,44 @@ int btintel_version_info_tlv(struct hci_dev *hdev, }EXPORT_SYMBOL_GPL (btintel_version_info_tlv); +static u8 btintel_version_tlv_min_len (u8 type) +{ +switch (type) { +case INTEL_TLV_CNVI_TOP : +case INTEL_TLV_CNVR_TOP : +case INTEL_TLV_CNVI_BT : +case INTEL_TLV_CNVR_BT : +case INTEL_TLV_BUILD_NUM : +case INTEL_TLV_GIT_SHA1 : +return sizeof (u32); +case INTEL_TLV_DEV_REV_ID : +case INTEL_TLV_TIME_STAMP : +return sizeof (u16); +case INTEL_TLV_IMAGE_TYPE : +case INTEL_TLV_BUILD_TYPE : +case INTEL_TLV_SECURE_BOOT : +case INTEL_TLV_OTP_LOCK : +case INTEL_TLV_API_LOCK : +case INTEL_TLV_DEBUG_LOCK : +case INTEL_TLV_LIMITED_CCE : +case INTEL_TLV_SBE_TYPE : +return sizeof (u8); +case INTEL_TLV_MIN_FW : +return 3 ; +case INTEL_TLV_OTP_BDADDR : +return sizeof (bdaddr_t); +default : +return 0 ; + } +} +int btintel_parse_version_tlv (struct hci_dev *hdev,struct intel_version_tlv *version,struct sk_buff *skb) {/* Consume Command Complete Status field */ -skb_pull (skb,1 ); +if (!skb_pull (skb,1 )) +return -EINVAL ;/* Event parameters contain multiple TLVs. Read each of them * and only keep the required data. Also, it use existing legacy
@@ -579,6 +611,9 @@ int btintel_parse_version_tlv(struct hci_dev *hdev,if (skb->len < tlv->len +sizeof (*tlv))return -EINVAL ; +if (tlv->len <btintel_version_tlv_min_len (tlv->type)) +return -EINVAL ; +switch (tlv->type) {case INTEL_TLV_CNVI_TOP : version->cnvi_top =get_unaligned_le32 (tlv->val);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 662/1250 Bluetooth: btintel: bound firmware ID by TLV length
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Laxman Acharya Padhya <[email protected]>
[ Upstream commit ac8aa9e0ec93a12a60230066f199f49c3b9aac3d ]
The firmware ID is treated as a NUL-terminated string even though the TLV length is its only boundary. If the value does not contain a NUL terminator, snprintf() can read beyond the received response.
Limit the conversion to the advertised TLV value length.
--- drivers/bluetooth/btintel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/bluetooth/btintel.c +1 -1
@@ -685,7 +685,7 @@ int btintel_parse_version_tlv(struct hci_dev *hdev,break ;case INTEL_TLV_FW_ID :snprintf (version->fw_id,sizeof (version->fw_id), -"%s" , tlv->val); +"%.*s" , tlv->len, tlv->val);break ;default :/* Ignore rest of information */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 663/1250 Bluetooth: hci_core: Fix race condition during device registration
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aleksandr Nogikh <[email protected]>
[ Upstream commit 57938bbdb9bf7fd41cbd5cd509ec10c4b22bec18 ]
In hci_register_dev(), the power_on work item is queued to hdev->req_workqueue before initializing hdev->adv_monitors_idr and registering the MSFT extension via msft_register(). For devices marked with quirks such as HCI_QUIRK_RAW_DEVICE, the HCI_UNCONFIGURED flag is set on the device. When the power_on work item runs concurrently on another CPU, hci_power_on() detects that the device is unconfigured and immediately invokes hci_dev_do_close(), which calls msft_do_close().
Concurrently, msft_register() allocates the msft structure and exposes it to hdev->msft_data prior to calling mutex_init(&msft->filter_lock). If msft_do_close() executes while hdev->msft_data is already assigned but the mutex has not yet been initialized, mutex_lock(&msft->filter_lock) operates on an uninitialized mutex, triggering a DEBUG_LOCKS warning:
DEBUG_LOCKS_WARN_ON(lock->magic != lock)
WARNING: kernel/locking/mutex.c:625 at __mutex_lock_common kernel/locking/mutex.c:625 [inline] WARNING: kernel/locking/mutex.c:625 at __mutex_lock+0x12d8/0x1550 kernel/locking/mutex.c:821 ... Call Trace: <TASK> msft_do_close+0x308/0x7b0 net/bluetooth/msft.c:693 hci_dev_close_sync+0x86b/0x10a0 net/bluetooth/hci_sync.c:5522 hci_dev_do_close net/bluetooth/hci_core.c:499 [inline] hci_power_on+0x32c/0x750 net/bluetooth/hci_core.c:937 process_one_work kernel/workqueue.c:3322 [inline] process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405 worker_thread+0x92d/0xe10 kernel/workqueue.c:3486 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 </TASK>
Fix this by moving the queue_work() call in hci_register_dev() to after idr_init(&hdev->adv_monitors_idr) and msft_register(hdev) so that device structures and extensions are fully initialized before asynchronous tasks can access them. Additionally, assign hdev->msft_data in msft_register() only after mutex_init(&msft->filter_lock) has completed.
--- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/msft.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-)
net/bluetooth/hci_core.c +2 -2
@@ -2668,11 +2668,11 @@ int hci_register_dev(struct hci_dev *hdev)if (error)BT_WARN ("register suspend notifier failed error:%d\n" , error); -queue_work (hdev->req_workqueue, &hdev->power_on); -idr_init (&hdev->adv_monitors_idr);msft_register (hdev); +queue_work (hdev->req_workqueue, &hdev->power_on); +return id; err_wqueue:
net/bluetooth/msft.c +1 -1
@@ -769,8 +769,8 @@ void msft_register(struct hci_dev *hdev)INIT_LIST_HEAD (&msft->handle_map);INIT_LIST_HEAD (&msft->address_filters); - hdev->msft_data = msft;mutex_init (&msft->filter_lock); + hdev->msft_data = msft; }void msft_release (struct hci_dev *hdev)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 664/1250 Bluetooth: L2CAP: fix chan mode for LE_CONN_REQ + EXT_FLOWCTL pchan
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <[email protected]>
[ Upstream commit 4ef05db5b08b176a551b4a6287372045998806b0 ]
l2cap_new_connection() sets default value of channel mode to match the parent channel. l2cap_le_connect_req() left this at the default, and created L2CAP_MODE_EXT_FLOWCTL channels if listening pchan has that mode. This causes FLAG_DEFER_SETUP channels to reply to L2CAP_LE_CONN_REQ with L2CAP_ECRED_CONN_RSP, which is incorrect.
It can also result to stack OOB write (of l2cap_alloc_cid determined values) in l2cap_ecred_rsp_defer(), as l2cap_le_connect_req() does not limit maximum number of deferred channels or check for duplicate ident.
Fix by setting chan->mode correctly in l2cap_le_connect_req().
Also check channel mode in l2cap_ecred_rsp_defer(), and do WARN_ON_ONCE instead of OOB write to make it less brittle.
--- net/bluetooth/l2cap_core.c | 8 ++++++++ 1 file changed, 8 insertions(+)
net/bluetooth/l2cap_core.c +8 -0
@@ -3893,6 +3893,9 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)struct l2cap_ecred_conn_rsp *rsp_flex = container_of(&rsp->pdu.rsp, struct l2cap_ecred_conn_rsp, hdr); +if (chan->mode !=L2CAP_MODE_EXT_FLOWCTL ) +return ; +/* Check if channel for outgoing connection or if it wasn't deferred * since in those cases it must be skipped. */
@@ -3903,6 +3906,10 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)/* Reset ident so only one response is sent */ chan->ident =0 ; +/* Unreachable, check in l2cap_ecred_conn_req. If reached, drop rest */ +if (WARN_ON_ONCE (rsp->count >=ARRAY_SIZE (rsp->pdu.scid))) + rsp->pdu.rsp.result =cpu_to_le16 (L2CAP_CR_LE_NO_MEM ); +/* Include all channels pending with the same ident */ if (!rsp->pdu.rsp.result) rsp_flex->dcid[rsp->count++] =cpu_to_le16 (chan->scid);
@@ -5070,6 +5077,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,__set_chan_timer (chan, chan->ops->get_sndtimeo (chan)); chan->ident = cmd->ident; + chan->mode =L2CAP_MODE_LE_FLOWCTL ;if (test_bit (FLAG_DEFER_SETUP , &chan->flags)) {l2cap_state_change (chan,BT_CONNECT2 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 665/1250 Bluetooth: L2CAP: fix out-of-bounds write in l2cap_ecred_connect
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <[email protected]>
[ Upstream commit 56c2b5831d39dc84aad2573dc3e197af1a872a05 ]
l2cap_chan_connect() tries to ensure there are no more than L2CAP_ECRED_CONN_SCID_MAX pending ECRED channels, so they fit in the same L2CAP_ECRED_CONN_REQ that l2cap_ecred_connect() constructs.
However, the check only counts deferred channels. If 6 L2CAP sockets are connected at the same time in order DDDDND (D=deferred, N=non-deferred), the last can bump the total to max+1. It results to one __le16 written out of bounds of the scid array, and an invalid ECRED_CONN_REQ being sent.
Fix by leaving room for the non-deferred pending ECRED channels in the counting in l2cap_chan_connect(), so the limit can't be exceeded.
Move counting under same critical section where the channel is added. Although race conditions involving this appear unreachable, it's easier to see.
Also add WARN_ON_ONCE check in l2cap_ecred_defer_connect() to make this less brittle.
--- net/bluetooth/l2cap_core.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-)
net/bluetooth/l2cap_core.c +14 -6
@@ -1340,7 +1340,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan)struct l2cap_ecred_conn_data {struct {struct l2cap_ecred_conn_req_hdr req; -__le16 scid[5 ]; +__le16 scid[L2CAP_ECRED_CONN_SCID_MAX ]; } __packed pdu;struct l2cap_chan *chan;struct pid *pid;
@@ -1368,6 +1368,10 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data)if (test_and_set_bit (FLAG_ECRED_CONN_REQ_SENT , &chan->flags))return ; +/* Unreachable, checked in l2cap_connect (+timer drops it if reached) */ +if (WARN_ON_ONCE (conn->count >=ARRAY_SIZE (conn->pdu.scid))) +return ; +l2cap_ecred_init (chan,0 );/* Set the same ident so we can match on the rsp */
@@ -7371,6 +7375,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, goto done; } +mutex_lock (&conn->lock); +l2cap_chan_lock (chan); +if (chan->mode ==L2CAP_MODE_EXT_FLOWCTL ) {struct l2cap_chan_data data;
@@ -7378,19 +7385,20 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, data.pid = chan->ops->get_peer_pid (chan); data.count =1 ; -l2cap_chan_list (conn, l2cap_chan_by_pid, &data); +__l2cap_chan_list (conn, l2cap_chan_by_pid, &data); + +/* Leave room for non-deferred channel that ends the group. */ +if (test_bit (FLAG_DEFER_SETUP , &chan->flags)) + data.count +=1 ;/* Check if there isn't too many channels being connected */ if (data.count >L2CAP_ECRED_CONN_SCID_MAX ) {hci_conn_drop (hcon); err = -EPROTO ; - goto done; + goto chan_unlock; } } -mutex_lock (&conn->lock); -l2cap_chan_lock (chan); -if (cid &&__l2cap_get_chan_by_dcid (conn, cid)) {hci_conn_drop (hcon); err = -EBUSY ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 666/1250 Bluetooth: L2CAP: clear FLAG_DEFER_SETUP only for same PID/PSM
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <[email protected]>
[ Upstream commit 0d77683237270702fa93489ca759c89b4e970554 ]
l2cap_ecred_defer_connect() clears FLAG_DEFER_SETUP also for channels with different PID/PSM, which will not be added to the same ECRED_CONN_REQ in any case. Consequently, only one ECRED connection group can work at a time although it appears intended they would be separate for each PID/PSM combination.
Fix by clearing FLAG_DEFER_SETUP only for the connections that could be added in the request. Retain test_bit(FLAG_DEFER_SETUP) before calling get_peer_pid as it may be NULL otherwise.
--- net/bluetooth/l2cap_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/bluetooth/l2cap_core.c +4 -1
@@ -1355,7 +1355,7 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data)if (chan == conn->chan)return ; -if (!test_and_clear_bit (FLAG_DEFER_SETUP , &chan->flags)) +if (!test_bit (FLAG_DEFER_SETUP , &chan->flags))return ; pid = chan->ops->get_peer_pid (chan);
@@ -1365,6 +1365,9 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data) chan->mode != L2CAP_MODE_EXT_FLOWCTL || chan->state != BT_CONNECT)return ; +if (!test_and_clear_bit (FLAG_DEFER_SETUP , &chan->flags)) +return ; +if (test_and_set_bit (FLAG_ECRED_CONN_REQ_SENT , &chan->flags))return ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 667/1250 Bluetooth: hci_mrvl: Fix wrong return value check of wait_on_bit_timeout()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gongwei Li <[email protected]>
[ Upstream commit 2deb76c21b81e42b3282224f7dd2046fe73fd1e0 ]
wait_on_bit_timeout() returns 0 if the bit was cleared, -EINTR if the process received a signal and the mode permitted wake up on that signal, or -EAGAIN if the timeout elapsed. It never returns 1.
Hence the check "err == 1" in mrvl_load_firmware() is dead code: when the waiting task is interrupted by a signal (-EINTR), the code falls into the "else if (err)" branch and misreports it as "Firmware request timeout" with -ETIMEDOUT instead of propagating -EINTR.
Fix this by testing for -EINTR so that an interrupted firmware load is properly detected and reported.
--- drivers/bluetooth/hci_mrvl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/bluetooth/hci_mrvl.c +1 -2
@@ -307,9 +307,8 @@ static int mrvl_load_firmware(struct hci_dev *hdev, const char *name) err =wait_on_bit_timeout (&mrvl->flags,STATE_FW_REQ_PENDING ,TASK_INTERRUPTIBLE ,msecs_to_jiffies (2000 )); -if (err ==1 ) { +if (err == -EINTR ) {bt_dev_err (hdev,"Firmware load interrupted" ); - err = -EINTR ;break ; }else if (err) {bt_dev_err (hdev,"Firmware request timeout" );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 668/1250 ASoC: ab8500: Reset the audio block before configuring it
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 8e839bca7793a0b03c005f4b2b0825464290d425 ]
ResetAudn is active low, but the codec probe only deasserts it. It also clears Clk32kOut2Dis despite claiming to disable that output, and writes codec registers before releasing reset.
Pulse ResetAudn before the first audio-bank access and leave the unused 32 kHz output disabled.
--- sound/soc/codecs/ab8500-codec.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)
sound/soc/codecs/ab8500-codec.c +13 -13
@@ -1917,18 +1917,18 @@ enum ab8500_filter {static int ab8500_audio_init_audioblock (struct snd_soc_component *component) {int status; +u8 mask =AB8500_STW4500CTRL3_CLK32KOUT2DIS | +AB8500_STW4500CTRL3_RESETAUDN ;dev_dbg (component->dev,"%s: Enter.\n" , __func__); -/* Reset audio-registers and disable 32kHz-clock output 2 */ - status =ab8500_sysctrl_write (AB8500_STW4500CTRL3 , -AB8500_STW4500CTRL3_CLK32KOUT2DIS | -AB8500_STW4500CTRL3_RESETAUDN , -AB8500_STW4500CTRL3_RESETAUDN ); +/* Reset the audio registers and disable the unused 32 kHz output. */ + status =ab8500_sysctrl_write (AB8500_STW4500CTRL3 , mask, +AB8500_STW4500CTRL3_CLK32KOUT2DIS );if (status <0 )return status; -return 0 ; +return ab8500_sysctrl_write (AB8500_STW4500CTRL3 , mask, mask); } static int ab8500_audio_setup_mics(struct snd_soc_component *component,
@@ -2461,6 +2461,13 @@ static int ab8500_codec_probe(struct snd_soc_component *component)ab8500_codec_of_probe (dev, np, &codec_pdata); + status =ab8500_audio_init_audioblock (component); +if (status <0 ) { +dev_err (dev,"%s: failed to init audio-block (%d)!\n" , + __func__, status); +return status; + } + status =ab8500_audio_setup_mics (component, &codec_pdata.amics);if (status <0 ) {pr_err ("%s: Failed to setup mics (%d)!\n" , __func__, status);
@@ -2473,13 +2480,6 @@ static int ab8500_codec_probe(struct snd_soc_component *component)return status; } - status =ab8500_audio_init_audioblock (component); -if (status <0 ) { -dev_err (dev,"%s: failed to init audio-block (%d)!\n" , - __func__, status); -return status; - } -/* Override HW-defaults */ snd_soc_component_write (component,AB8500_ANACONF5 ,BIT (AB8500_ANACONF5_HSAUTOEN ));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 669/1250 ASoC: ab8500: Repair the DAPM capture graph
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 103fe1a37f040ef6ac9ed1cf33be786149d2bb15 ]
The capture stream routes point away from the stream widget. Digital microphone mux routes are unconditional and bypass their enable bits, and several widgets independently own shared AD path enable bits. The dummy ADC and DAC widgets hide the resulting power graph errors.
Connect each real AIF widget to the stream and main supply, use the mux item names on digital microphone routes, and model shared AD enables as supplies. Also make the ANC DAPM switch writable.
--- sound/soc/codecs/ab8500-codec.c | 120 +++++++++++++++----------------- 1 file changed, 56 insertions(+), 64 deletions(-)
sound/soc/codecs/ab8500-codec.c +56 -64
@@ -303,7 +303,7 @@ static const struct snd_kcontrol_new dapm_anc_in_select[] = {/* ANC - Enable/Disable */ static const struct snd_kcontrol_new dapm_anc_enable[] = {SOC_DAPM_SINGLE ("Switch" ,AB8500_ANCCONF1 , -AB8500_ANCCONF1_ENANC ,0 ,0 ), +AB8500_ANCCONF1_ENANC ,1 ,0 ), };/* ANC to Earpiece - Mute */
@@ -385,12 +385,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {/* DA/AD */ -SND_SOC_DAPM_INPUT ("ADC Input" ), -SND_SOC_DAPM_ADC ("ADC" ,"ab8500_0c" ,SND_SOC_NOPM ,0 ,0 ), - -SND_SOC_DAPM_DAC ("DAC" ,NULL ,SND_SOC_NOPM ,0 ,0 ), -SND_SOC_DAPM_OUTPUT ("DAC Output" ), -SND_SOC_DAPM_AIF_IN ("DA_IN1" ,NULL ,0 ,SND_SOC_NOPM ,0 ,0 ),SND_SOC_DAPM_AIF_IN ("DA_IN2" ,NULL ,0 ,SND_SOC_NOPM ,0 ,0 ), SND_SOC_DAPM_AIF_IN("DA_IN3", NULL, 0, SND_SOC_NOPM, 0, 0),
@@ -582,9 +576,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {SND_SOC_DAPM_MIXER ("AD3 Channel Volume" ,SND_SOC_NOPM ,0 ,0 ,NULL ,0 ), -SND_SOC_DAPM_MIXER ("AD3 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD34 ,0 , - NULL, 0), +SND_SOC_DAPM_SUPPLY ("AD34 Enable" ,AB8500_ADPATHENA , + AB8500_ADPATHENA_ENAD34, 0, NULL, 0),/* Mic 2 */
@@ -643,9 +636,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { SND_SOC_NOPM, 0, 0, NULL, 0), -SND_SOC_DAPM_MIXER ("AD12 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD12 ,0 , - NULL, 0), +SND_SOC_DAPM_SUPPLY ("AD12 Enable" ,AB8500_ADPATHENA , + AB8500_ADPATHENA_ENAD12, 0, NULL, 0),/* HD Capture path */
@@ -659,12 +651,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {SND_SOC_DAPM_MIXER ("AD6 Channel Volume" ,SND_SOC_NOPM ,0 ,0 ,NULL ,0 ), -SND_SOC_DAPM_MIXER ("AD57 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD5768 ,0 , -NULL ,0 ), -SND_SOC_DAPM_MIXER ("AD68 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD5768 ,0 , - NULL, 0), +SND_SOC_DAPM_SUPPLY ("AD5768 Enable" ,AB8500_ADPATHENA , + AB8500_ADPATHENA_ENAD5768, 0, NULL, 0),/* Digital Microphone path */
@@ -696,10 +684,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {SND_SOC_DAPM_MIXER ("AD4 Channel Volume" ,SND_SOC_NOPM ,0 ,0 ,NULL ,0 ), -SND_SOC_DAPM_MIXER ("AD4 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD34 , -0 ,NULL ,0 ), -/* Acoustical Noise Cancellation path */ SND_SOC_DAPM_INPUT("ANC Configure Input"),
@@ -747,24 +731,17 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"Main Supply", NULL, "Audio Power"}, {"Main Supply", NULL, "Audio Analog Power"}, - {"DAC", NULL, "ab8500_0p"}, - {"DAC", NULL, "Main Supply"}, - {"ADC", NULL, "ab8500_0c"}, - {"ADC", NULL, "Main Supply"}, -/* ANC Configure */ {"ANC Configure Input", NULL, "Main Supply"}, {"ANC Configure Output", NULL, "ANC Configure Input"}, -/* AD/DA */ - {"ADC", NULL, "ADC Input"}, - {"DAC Output", NULL, "DAC"}, -/* Powerup charge pump if DA1/2 is in use */ {"DA_IN1", NULL, "ab8500_0p"}, + {"DA_IN1", NULL, "Main Supply"}, {"DA_IN1", NULL, "Charge Pump"}, {"DA_IN2", NULL, "ab8500_0p"}, + {"DA_IN2", NULL, "Main Supply"}, {"DA_IN2", NULL, "Charge Pump"}, /* Headset path */
@@ -799,8 +776,10 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {/* HF or LineOut path */ {"DA_IN3", NULL, "ab8500_0p"}, + {"DA_IN3", NULL, "Main Supply"}, {"DA3 Channel Volume", NULL, "DA_IN3"}, {"DA_IN4", NULL, "ab8500_0p"}, + {"DA_IN4", NULL, "Main Supply"}, {"DA4 Channel Volume", NULL, "DA_IN4"}, {"Speaker Left Source", "Audio Path", "DA3 Channel Volume"},
@@ -858,8 +837,10 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {/* Vibrator path */ {"DA_IN5", NULL, "ab8500_0p"}, + {"DA_IN5", NULL, "Main Supply"}, {"DA5 Channel Volume", NULL, "DA_IN5"}, {"DA_IN6", NULL, "ab8500_0p"}, + {"DA_IN6", NULL, "Main Supply"}, {"DA6 Channel Volume", NULL, "DA_IN6"}, {"VIB1 DAC", NULL, "DA5 Channel Volume"},
@@ -901,13 +882,15 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"AD1 Channel Volume", NULL, "AD1 Source Select"}, {"AD2 Channel Volume", NULL, "AD2 Source Select"}, - {"AD12 Enable", NULL, "AD1 Channel Volume"}, - {"AD12 Enable", NULL, "AD2 Channel Volume"}, + {"AD1 Channel Volume", NULL, "AD12 Enable"}, + {"AD2 Channel Volume", NULL, "AD12 Enable"}, - {"AD_OUT1", NULL, "ab8500_0c"}, - {"AD_OUT1", NULL, "AD12 Enable"}, - {"AD_OUT2", NULL, "ab8500_0c"}, - {"AD_OUT2", NULL, "AD12 Enable"}, + {"ab8500_0c", NULL, "AD_OUT1"}, + {"AD_OUT1", NULL, "Main Supply"}, + {"AD_OUT1", NULL, "AD1 Channel Volume"}, + {"ab8500_0c", NULL, "AD_OUT2"}, + {"AD_OUT2", NULL, "Main Supply"}, + {"AD_OUT2", NULL, "AD2 Channel Volume"}, /* Mic 1 */
@@ -924,11 +907,11 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"AD3 Source Select", "Mic 1", "MIC1 ADC"}, {"AD3 Channel Volume", NULL, "AD3 Source Select"}, + {"AD3 Channel Volume", NULL, "AD34 Enable"}, - {"AD3 Enable", NULL, "AD3 Channel Volume"}, - - {"AD_OUT3", NULL, "ab8500_0c"}, - {"AD_OUT3", NULL, "AD3 Enable"}, + {"ab8500_0c", NULL, "AD_OUT3"}, + {"AD_OUT3", NULL, "Main Supply"}, + {"AD_OUT3", NULL, "AD3 Channel Volume"}, /* HD Capture path */
@@ -937,14 +920,15 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"AD5 Channel Volume", NULL, "AD5 Source Select"}, {"AD6 Channel Volume", NULL, "AD6 Source Select"}, + {"AD5 Channel Volume", NULL, "AD5768 Enable"}, + {"AD6 Channel Volume", NULL, "AD5768 Enable"}, - {"AD57 Enable", NULL, "AD5 Channel Volume"}, - {"AD68 Enable", NULL, "AD6 Channel Volume"}, - - {"AD_OUT57", NULL, "ab8500_0c"}, - {"AD_OUT57", NULL, "AD57 Enable"}, - {"AD_OUT68", NULL, "ab8500_0c"}, - {"AD_OUT68", NULL, "AD68 Enable"}, + {"ab8500_0c", NULL, "AD_OUT57"}, + {"AD_OUT57", NULL, "Main Supply"}, + {"AD_OUT57", NULL, "AD5 Channel Volume"}, + {"ab8500_0c", NULL, "AD_OUT68"}, + {"AD_OUT68", NULL, "Main Supply"}, + {"AD_OUT68", NULL, "AD6 Channel Volume"}, /* Digital Microphone path */
@@ -955,17 +939,25 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"DMic 5", NULL, "V-DMIC"}, {"DMic 6", NULL, "V-DMIC"}, - {"AD1 Source Select", NULL, "DMic 1"}, - {"AD2 Source Select", NULL, "DMic 2"}, - {"AD3 Source Select", NULL, "DMic 3"}, - {"AD5 Source Select", NULL, "DMic 5"}, - {"AD6 Source Select", NULL, "DMic 6"}, + {"DMIC1", NULL, "DMic 1"}, + {"DMIC2", NULL, "DMic 2"}, + {"DMIC3", NULL, "DMic 3"}, + {"DMIC4", NULL, "DMic 4"}, + {"DMIC5", NULL, "DMic 5"}, + {"DMIC6", NULL, "DMic 6"}, + + {"AD1 Source Select", "DMic 1", "DMIC1"}, + {"AD2 Source Select", "DMic 2", "DMIC2"}, + {"AD3 Source Select", "DMic 3", "DMIC3"}, + {"AD5 Source Select", "DMic 5", "DMIC5"}, + {"AD6 Source Select", "DMic 6", "DMIC6"}, - {"AD4 Channel Volume", NULL, "DMic 4"}, - {"AD4 Enable", NULL, "AD4 Channel Volume"}, + {"AD4 Channel Volume", NULL, "DMIC4"}, + {"AD4 Channel Volume", NULL, "AD34 Enable"}, - {"AD_OUT4", NULL, "ab8500_0c"}, - {"AD_OUT4", NULL, "AD4 Enable"}, + {"ab8500_0c", NULL, "AD_OUT4"}, + {"AD_OUT4", NULL, "Main Supply"}, + {"AD_OUT4", NULL, "AD4 Channel Volume"}, /* LineIn Bypass path */
@@ -990,13 +982,13 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {/* Sidetone Filter path */ - {"Sidetone Left Source", "LineIn Left", "AD12 Enable"}, - {"Sidetone Left Source", "LineIn Right", "AD12 Enable"}, - {"Sidetone Left Source", "Mic 1", "AD3 Enable"}, + {"Sidetone Left Source", "LineIn Left", "AD1 Channel Volume"}, + {"Sidetone Left Source", "LineIn Right", "AD2 Channel Volume"}, + {"Sidetone Left Source", "Mic 1", "AD3 Channel Volume"}, {"Sidetone Left Source", "Headset Left", "DA_IN1"}, - {"Sidetone Right Source", "LineIn Right", "AD12 Enable"}, - {"Sidetone Right Source", "Mic 1", "AD3 Enable"}, - {"Sidetone Right Source", "DMic 4", "AD4 Enable"}, + {"Sidetone Right Source", "LineIn Right", "AD2 Channel Volume"}, + {"Sidetone Right Source", "Mic 1", "AD3 Channel Volume"}, + {"Sidetone Right Source", "DMic 4", "AD4 Channel Volume"}, {"Sidetone Right Source", "Headset Right", "DA_IN2"}, {"STFIR1 Control", NULL, "Sidetone Left Source"},
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 670/1250 ASoC: ab8500: Correct digital interface format setup
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit f98785adf004db6b1c9f4cea9dadae7b800db72f ]
The codec programs I2S as an undelayed left-aligned format, although the hardware manual defines delayed left-aligned as I2S compatible. It also enables the master generator when the codec is a clock consumer, changes registers before the complete format has been validated, and discards register I/O errors.
Build all three interface register values before writing them, use the required one-bit I2S delay, only run the master generator for a provider configuration, and propagate write failures.
--- sound/soc/codecs/ab8500-codec.c | 175 ++++++++++++-------------------- 1 file changed, 64 insertions(+), 111 deletions(-)
sound/soc/codecs/ab8500-codec.c +64 -111
@@ -2016,149 +2016,91 @@ static int ab8500_audio_set_ear_cmv(struct snd_soc_component *component,return 0 ; } -static int ab8500_audio_set_bit_delay (struct snd_soc_dai *dai, -unsigned int delay) -{ -unsigned int mask, val; -struct snd_soc_component *component = dai->component; - - mask =BIT (AB8500_DIGIFCONF2_IF0DEL ); - val =0 ; - -switch (delay) { -case 0 : -break ; -case 1 : - val |=BIT (AB8500_DIGIFCONF2_IF0DEL ); -break ; -default : -dev_err (dai->component->dev, -"%s: ERROR: Unsupported bit-delay (0x%x)!\n" , - __func__, delay); -return -EINVAL ; - } - -dev_dbg (dai->component->dev,"%s: IF0 Bit-delay: %d bits.\n" , - __func__, delay); -snd_soc_component_update_bits (component,AB8500_DIGIFCONF2 , mask, val); - -return 0 ; -} - -/* Gates clocking according format mask */ -static int ab8500_codec_set_dai_clock_gate (struct snd_soc_component *component, -unsigned int fmt) -{ -unsigned int mask; -unsigned int val; - - mask =BIT (AB8500_DIGIFCONF1_ENMASTGEN ) | -BIT (AB8500_DIGIFCONF1_ENFSBITCLK0 ); - - val =BIT (AB8500_DIGIFCONF1_ENMASTGEN ); - -switch (fmt &SND_SOC_DAIFMT_CLOCK_MASK ) { -case SND_SOC_DAIFMT_CONT :/* continuous clock */ -dev_dbg (component->dev,"%s: IF0 Clock is continuous.\n" , - __func__); - val |=BIT (AB8500_DIGIFCONF1_ENFSBITCLK0 ); -break ; -case SND_SOC_DAIFMT_GATED :/* clock is gated */ -dev_dbg (component->dev,"%s: IF0 Clock is gated.\n" , - __func__); -break ; -default : -dev_err (component->dev, -"%s: ERROR: Unsupported clock mask (0x%x)!\n" , - __func__, fmt &SND_SOC_DAIFMT_CLOCK_MASK ); -return -EINVAL ; - } - -snd_soc_component_update_bits (component,AB8500_DIGIFCONF1 , mask, val); - -return 0 ; -} -static int ab8500_codec_set_dai_fmt (struct snd_soc_dai *dai,unsigned int fmt) { -unsigned int mask; -unsigned int val;struct snd_soc_component *component = dai->component; -int status; +unsigned int conf1_mask, conf1_val =0 ; +unsigned int conf2_mask, conf2_val =0 ; +unsigned int conf3_mask, conf3_val =0 ; +bool provider = false; +int ret;dev_dbg (component->dev,"%s: Enter (fmt = 0x%x)\n" , __func__, fmt); - mask =BIT (AB8500_DIGIFCONF3_IF1DATOIF0AD ) | + conf3_mask =BIT (AB8500_DIGIFCONF3_IF1DATOIF0AD ) |BIT (AB8500_DIGIFCONF3_IF1CLKTOIF0CLK ) |BIT (AB8500_DIGIFCONF3_IF0BFIFOEN ) |BIT (AB8500_DIGIFCONF3_IF0MASTER ); - val =0 ;switch (fmt &SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK ) {case SND_SOC_DAIFMT_CBP_CFP : -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0 Master-mode: AB8500 provider.\n" , __func__); - val |=BIT (AB8500_DIGIFCONF3_IF0MASTER ); + conf3_val |=BIT (AB8500_DIGIFCONF3_IF0MASTER ); + provider = true;break ;case SND_SOC_DAIFMT_CBC_CFC : -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0 Master-mode: AB8500 consumer.\n" , __func__);break ;case SND_SOC_DAIFMT_CBC_CFP :case SND_SOC_DAIFMT_CBP_CFC : -dev_err (dai->component->dev, +dev_err (component->dev,"%s: ERROR: The device is either a provider or a consumer.\n" , __func__); fallthrough;default : -dev_err (dai->component->dev, -"%s: ERROR: Unsupporter clocking mask 0x%x\n" , +dev_err (component->dev, +"%s: ERROR: Unsupported clocking mask 0x%x\n" , __func__, fmt &SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK );return -EINVAL ; } -snd_soc_component_update_bits (component,AB8500_DIGIFCONF3 , mask, val); - -/* Set clock gating */ - status =ab8500_codec_set_dai_clock_gate (component, fmt); -if (status) { -dev_err (dai->component->dev, -"%s: ERROR: Failed to set clock gate (%d).\n" , - __func__, status); -return status; + conf1_mask =BIT (AB8500_DIGIFCONF1_ENMASTGEN ) | +BIT (AB8500_DIGIFCONF1_ENFSBITCLK0 ); +switch (fmt &SND_SOC_DAIFMT_CLOCK_MASK ) { +case SND_SOC_DAIFMT_CONT : +if (provider) + conf1_val = conf1_mask; +break ; +case SND_SOC_DAIFMT_GATED : +if (provider) + conf1_val =BIT (AB8500_DIGIFCONF1_ENMASTGEN ); +break ; +default : +dev_err (component->dev,"%s: Unsupported clock mask 0x%x\n" , + __func__, fmt &SND_SOC_DAIFMT_CLOCK_MASK ); +return -EINVAL ; } -/* Setting data transfer format */ - - mask =BIT (AB8500_DIGIFCONF2_IF0FORMAT0 ) | -BIT (AB8500_DIGIFCONF2_IF0FORMAT1 ) | -BIT (AB8500_DIGIFCONF2_FSYNC0P ) | -BIT (AB8500_DIGIFCONF2_BITCLK0P ); - val =0 ; + conf2_mask =BIT (AB8500_DIGIFCONF2_IF0FORMAT0 ) | +BIT (AB8500_DIGIFCONF2_IF0FORMAT1 ) | +BIT (AB8500_DIGIFCONF2_IF0DEL ) | +BIT (AB8500_DIGIFCONF2_FSYNC0P ) | +BIT (AB8500_DIGIFCONF2_BITCLK0P );switch (fmt &SND_SOC_DAIFMT_FORMAT_MASK ) {case SND_SOC_DAIFMT_I2S :/* I2S mode */ -dev_dbg (dai->component->dev,"%s: IF0 Protocol: I2S\n" , __func__); - val |=BIT (AB8500_DIGIFCONF2_IF0FORMAT1 ); -ab8500_audio_set_bit_delay (dai,0 ); +dev_dbg (component->dev,"%s: IF0 Protocol: I2S\n" , __func__); + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0FORMAT1 ) | +BIT (AB8500_DIGIFCONF2_IF0DEL );break ;case SND_SOC_DAIFMT_DSP_A :/* L data MSB after FRM LRC */ -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0 Protocol: DSP A (TDM)\n" , __func__); - val |=BIT (AB8500_DIGIFCONF2_IF0FORMAT0 ); -ab8500_audio_set_bit_delay (dai,1 ); + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0FORMAT0 ) | +BIT (AB8500_DIGIFCONF2_IF0DEL );break ;case SND_SOC_DAIFMT_DSP_B :/* L data MSB during FRM LRC */ -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0 Protocol: DSP B (TDM)\n" , __func__); - val |=BIT (AB8500_DIGIFCONF2_IF0FORMAT0 ); -ab8500_audio_set_bit_delay (dai,0 ); + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0FORMAT0 );break ;default : -dev_err (dai->component->dev, +dev_err (component->dev,"%s: ERROR: Unsupported format (0x%x)!\n" , __func__, fmt &SND_SOC_DAIFMT_FORMAT_MASK );return -EINVAL ;
@@ -2166,39 +2108,50 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)switch (fmt &SND_SOC_DAIFMT_INV_MASK ) {case SND_SOC_DAIFMT_NB_NF :/* normal bit clock + frame */ -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0: Normal bit clock, normal frame\n" , __func__);break ;case SND_SOC_DAIFMT_NB_IF :/* normal BCLK + inv FRM */ -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0: Normal bit clock, inverted frame\n" , __func__); - val |=BIT (AB8500_DIGIFCONF2_FSYNC0P ); + conf2_val |=BIT (AB8500_DIGIFCONF2_FSYNC0P );break ;case SND_SOC_DAIFMT_IB_NF :/* invert BCLK + nor FRM */ -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0: Inverted bit clock, normal frame\n" , __func__); - val |=BIT (AB8500_DIGIFCONF2_BITCLK0P ); + conf2_val |=BIT (AB8500_DIGIFCONF2_BITCLK0P );break ;case SND_SOC_DAIFMT_IB_IF :/* invert BCLK + FRM */ -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0: Inverted bit clock, inverted frame\n" , __func__); - val |=BIT (AB8500_DIGIFCONF2_FSYNC0P ); - val |=BIT (AB8500_DIGIFCONF2_BITCLK0P ); + conf2_val |=BIT (AB8500_DIGIFCONF2_FSYNC0P ) | +BIT (AB8500_DIGIFCONF2_BITCLK0P );break ;default : -dev_err (dai->component->dev, +dev_err (component->dev,"%s: ERROR: Unsupported INV mask 0x%x\n" , __func__, fmt &SND_SOC_DAIFMT_INV_MASK );return -EINVAL ; } -snd_soc_component_update_bits (component,AB8500_DIGIFCONF2 , mask, val); + ret =snd_soc_component_update_bits (component,AB8500_DIGIFCONF3 , + conf3_mask, conf3_val); +if (ret <0 ) +return ret; -return 0 ; + ret =snd_soc_component_update_bits (component,AB8500_DIGIFCONF1 , + conf1_mask, conf1_val); +if (ret <0 ) +return ret; + + ret =snd_soc_component_update_bits (component,AB8500_DIGIFCONF2 , + conf2_mask, conf2_val); + +return ret <0 ? ret :0 ; } static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 671/1250 ASoC: ab8500: Validate and program TDM slots correctly
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 85cef7e2004ef5c1a715feaddb55d3f3d27bac0a ]
The interface clock ratio is selected from the slot count alone, ffs() and fls() produce one-based hardware slot numbers, eight-channel mode does not program any mappings, and all register errors are ignored. Invalid masks can also leave a partially programmed interface.
Validate the complete configuration first, derive the supported BCLK ratio from slots times slot width, use zero-based slot indices, program deterministic eight-channel maps, and propagate register failures.
--- sound/soc/codecs/ab8500-codec.c | 216 ++++++++++++++++++-------------- 1 file changed, 123 insertions(+), 93 deletions(-)
sound/soc/codecs/ab8500-codec.c +123 -93
@@ -2159,23 +2159,27 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, int slots, int slot_width) {struct snd_soc_component *component = dai->component; -unsigned int val, mask, slot, slots_active; +unsigned int active_mask, clock_ratio, slot, value, ad_out, reg; +unsigned int tx_active, rx_active; +unsigned int conf1_val, conf2_val; +unsigned int mask; +int channel, ret; mask =BIT (AB8500_DIGIFCONF2_IF0WL0 ) |BIT (AB8500_DIGIFCONF2_IF0WL1 ); - val =0 ; + conf2_val =0 ;switch (slot_width) {case 16 :break ;case 20 : - val |=BIT (AB8500_DIGIFCONF2_IF0WL0 ); + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0WL0 );break ;case 24 : - val |=BIT (AB8500_DIGIFCONF2_IF0WL1 ); + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0WL1 );break ;case 32 : - val |=BIT (AB8500_DIGIFCONF2_IF0WL1 ) | + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0WL1 ) |BIT (AB8500_DIGIFCONF2_IF0WL0 );break ;default :
@@ -2184,27 +2188,11 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,return -EINVAL ; } -dev_dbg (dai->component->dev,"%s: IF0 slot-width: %d bits.\n" , - __func__, slot_width); -snd_soc_component_update_bits (component,AB8500_DIGIFCONF2 , mask, val); - -/* Setup TDM clocking according to slot count */ -dev_dbg (dai->component->dev,"%s: Slots, total: %d\n" , __func__, slots); - mask =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ) | -BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 );switch (slots) {case 2 : - val =AB8500_MASK_NONE ; -break ;case 4 : - val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ); -break ;case 8 : - val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 ); -break ;case 16 : - val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ) | -BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 );break ;default : dev_err(dai->component->dev,
@@ -2212,92 +2200,134 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, __func__, slots);return -EINVAL ; } -snd_soc_component_update_bits (component,AB8500_DIGIFCONF1 , mask, val); - -/* Setup TDM DA according to active tx slots */ -if (tx_mask & ~0xff ) -return -EINVAL ; - - mask =AB8500_DASLOTCONFX_SLTODAX_MASK ; - tx_mask = tx_mask <<AB8500_DA_DATA0_OFFSET ; - slots_active =hweight32 (tx_mask); - -dev_dbg (dai->component->dev,"%s: Slots, active, TX: %d\n" , __func__, - slots_active); - -switch (slots_active) { -case 0 : + clock_ratio = slots * slot_width; +switch (clock_ratio) { +case 32 : + conf1_val =0 ;break ; -case 1 : - slot =ffs (tx_mask); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF1 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF3 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF2 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF4 , mask, slot); +case 64 : + conf1_val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 );break ; -case 2 : - slot =ffs (tx_mask); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF1 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF3 , mask, slot); - slot =fls (tx_mask); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF2 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF4 , mask, slot); +case 128 : + conf1_val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 );break ; -case 8 : -dev_dbg (dai->component->dev, -"%s: In 8-channel mode DA-from-slot mapping is set manually." , - __func__); +case 256 : + conf1_val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ) | +BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 );break ;default : -dev_err (dai->component->dev, -"%s: Unsupported number of active TX-slots (%d)!\n" , - __func__, slots_active); +dev_err (component->dev,"%s: Unsupported BCLK ratio (%u)!\n" , + __func__, clock_ratio);return -EINVAL ; } -/* Setup TDM AD according to active RX-slots */ + active_mask =GENMASK (min (slots,8 ) -1 ,0 ); +if ((tx_mask | rx_mask) & ~active_mask) { +dev_err (component->dev,"%s: Slot mask exceeds slot count\n" , + __func__); +return -EINVAL ; + } -if (rx_mask & ~0xff ) + tx_active =hweight32 (tx_mask); + rx_active =hweight32 (rx_mask); +if (tx_active !=0 && tx_active !=1 && tx_active !=2 && + tx_active !=8 ) { +dev_err (component->dev,"%s: Unsupported active TX slots (%u)!\n" , + __func__, tx_active); +return -EINVAL ; + } +if (rx_active !=0 && rx_active !=1 && rx_active !=2 && + rx_active !=8 ) { +dev_err (component->dev,"%s: Unsupported active RX slots (%u)!\n" , + __func__, rx_active);return -EINVAL ; + } + +dev_dbg (component->dev, +"%s: %d slots of %d bits, TX active: %u, RX active: %u\n" , + __func__, slots, slot_width, tx_active, rx_active); - rx_mask = rx_mask <<AB8500_AD_DATA0_OFFSET ; - slots_active =hweight32 (rx_mask); + ret =snd_soc_component_update_bits (component,AB8500_DIGIFCONF2 , + mask, conf2_val); +if (ret <0 ) +return ret; -dev_dbg (dai->component->dev,"%s: Slots, active, RX: %d\n" , __func__, - slots_active); + mask =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ) | +BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 ); + ret =snd_soc_component_update_bits (component,AB8500_DIGIFCONF1 , + mask, conf1_val); +if (ret <0 ) +return ret; -switch (slots_active) { -case 0 : -break ; -case 1 : - slot =ffs (rx_mask); -snd_soc_component_update_bits (component,AB8500_ADSLOTSEL (slot), -AB8500_MASK_SLOT (slot), -AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT3 , slot)); -break ; -case 2 : - slot =ffs (rx_mask); -snd_soc_component_update_bits (component, -AB8500_ADSLOTSEL (slot), -AB8500_MASK_SLOT (slot), -AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT3 , slot)); - slot =fls (rx_mask); -snd_soc_component_update_bits (component, -AB8500_ADSLOTSEL (slot), -AB8500_MASK_SLOT (slot), -AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT2 , slot)); -break ; -case 8 : -dev_dbg (dai->component->dev, -"%s: In 8-channel mode AD-to-slot mapping is set manually." , - __func__); -break ; -default : -dev_err (dai->component->dev, -"%s: Unsupported number of active RX-slots (%d)!\n" , - __func__, slots_active); -return -EINVAL ; + mask =AB8500_DASLOTCONFX_SLTODAX_MASK ; +if (tx_active ==1 || tx_active ==2 ) { + slot =__ffs (tx_mask) +AB8500_DA_DATA0_OFFSET ; + reg =AB8500_DASLOTCONF1 ; + ret =snd_soc_component_update_bits (component, reg, mask, slot); +if (ret <0 ) +return ret; + reg =AB8500_DASLOTCONF3 ; + ret =snd_soc_component_update_bits (component, reg, mask, slot); +if (ret <0 ) +return ret; + +if (tx_active ==2 ) + slot =__fls (tx_mask) +AB8500_DA_DATA0_OFFSET ; + reg =AB8500_DASLOTCONF2 ; + ret =snd_soc_component_update_bits (component, reg, mask, slot); +if (ret <0 ) +return ret; + reg =AB8500_DASLOTCONF4 ; + ret =snd_soc_component_update_bits (component, reg, mask, slot); +if (ret <0 ) +return ret; + }else if (tx_active ==8 ) { + channel =0 ; +for (slot =0 ; slot <8 ; slot++) { +if (!(tx_mask &BIT (slot))) +continue ; + reg =AB8500_DASLOTCONF1 + channel++; + value = slot +AB8500_DA_DATA0_OFFSET ; + ret =snd_soc_component_update_bits (component, reg, mask, value); +if (ret <0 ) +return ret; + } + } + +if (rx_active ==1 || rx_active ==2 ) { + slot =__ffs (rx_mask) +AB8500_AD_DATA0_OFFSET ; + value =AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT3 , + slot); + reg =AB8500_ADSLOTSEL (slot); + mask =AB8500_MASK_SLOT (slot); + ret =snd_soc_component_update_bits (component, reg, mask, value); +if (ret <0 ) +return ret; + +if (rx_active ==2 ) { + slot =__fls (rx_mask) +AB8500_AD_DATA0_OFFSET ; + value =AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT2 , + slot); + reg =AB8500_ADSLOTSEL (slot); + mask =AB8500_MASK_SLOT (slot); + ret =snd_soc_component_update_bits (component, reg, mask, value); +if (ret <0 ) +return ret; + } + }else if (rx_active ==8 ) { + channel =0 ; +for (slot =0 ; slot <8 ; slot++) { +if (!(rx_mask &BIT (slot))) +continue ; + ad_out =AB8500_AD_OUT1 + channel++; + value =AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (ad_out, slot); + reg =AB8500_ADSLOTSEL (slot); + mask =AB8500_MASK_SLOT (slot); + ret =snd_soc_component_update_bits (component, reg, mask, value); +if (ret <0 ) +return ret; + } }return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 672/1250 net: ethernet: oa_tc6: Interrupt is active low, level triggered.
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit b542d13fab0f556660ce5f10179fb3ae66bdb9a4 ]
According OPEN Alliance 10BASET1x MAC-PHY Serial Interface specification, interrupt is active low, level triggered.
Code used edge triggered interrupt which has the risk of losing an interrupt on instances like when interrupt is disabled. Level triggered interrupt won't be deasserted unless handler runs and clear the interrupting conditions.
Interrupt handler mechanism is changed to threaded irq from interrupt handler and kernel thread waiting on work queue. Threaded irq mechanism is best suited for level triggered interrupt as it disables the interrupt until handler is run in thread level, while giving us an ability to have interrupt context handler to signal the threaded irq handler.
Introduced a logic to disable the device interrupt on error. Error could be due in data chunk's header and footer or SPI interface itself. This will avoid having repeated interrupts, in case the driver couldn't recover from the error condition with the available recovery mechanism.
Stable-dep-of: 172c974113bf ("net: ethernet: oa_tc6: Improve the error recovery")
--- drivers/net/ethernet/oa_tc6.c | 126 ++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 50 deletions(-)
drivers/net/ethernet/oa_tc6.c +76 -50
@@ -7,6 +7,7 @@#include <linux/bitfield.h> #include <linux/iopoll.h> +#include <linux/interrupt.h> #include <linux/mdio.h> #include <linux/phy.h> #include <linux/oa_tc6.h>
@@ -44,6 +45,8 @@#define INT_MASK0_LOSS_OF_FRAME_ERR_MASK BIT(4)#define INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3)#define INT_MASK0_TX_PROTOCOL_ERR_MASK BIT(0) +#define INT_MASK0_ALL_INTERRUPTS (GENMASK(5, 0) | \ + GENMASK(12, 7))/* PHY Clause 22 registers base address and mask */ #define OA_TC6_PHY_STD_REG_ADDR_BASE 0xFF00
@@ -121,14 +124,13 @@ struct oa_tc6 {struct sk_buff *ongoing_tx_skb;struct sk_buff *waiting_tx_skb;struct sk_buff *rx_skb; -struct task_struct *spi_thread; -wait_queue_head_t spi_wq;u16 tx_skb_offset;u16 spi_data_tx_buf_offset;u16 tx_credits;u8 rx_chunks_available;bool rx_buf_overflow;bool int_flag; +bool disable_traffic; };enum oa_tc6_header_type {
@@ -669,6 +671,38 @@ static void oa_tc6_cleanup_ongoing_tx_skb(struct oa_tc6 *tc6) } } +static void oa_tc6_cleanup_waiting_tx_skb (struct oa_tc6 *tc6) +{ +if (tc6->waiting_tx_skb) { + tc6->netdev->stats.tx_dropped++; +kfree_skb (tc6->waiting_tx_skb); + tc6->waiting_tx_skb =NULL ; + } +} + +static void oa_tc6_free_pending_skbs (struct oa_tc6 *tc6) +{ +oa_tc6_cleanup_ongoing_tx_skb (tc6); +oa_tc6_cleanup_ongoing_rx_skb (tc6); +oa_tc6_cleanup_waiting_tx_skb (tc6); +} + +/* If the failure is at SPI interface level, masking and clearing +* the interrupt of the device won't work. Since SPI interrupt is +* disabled, it should stop the repeated interrupts. +*/ +static void oa_tc6_disable_traffic (struct oa_tc6 *tc6) +{ +u32 regval =INT_MASK0_ALL_INTERRUPTS ; + + tc6->disable_traffic = true; +oa_tc6_free_pending_skbs (tc6); +oa_tc6_write_register (tc6,OA_TC6_REG_INT_MASK0 , regval); +oa_tc6_read_register (tc6,OA_TC6_REG_STATUS0 , ®val); +oa_tc6_write_register (tc6,OA_TC6_REG_STATUS0 , regval); +dev_err (&tc6->spi->dev,"Device interrupt disabled to avoid interrupt storm" ); +} +static int oa_tc6_process_extended_status (struct oa_tc6 *tc6) {u32 value;
@@ -1116,29 +1150,29 @@ static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6)return 0 ; } -static int oa_tc6_spi_thread_handler (void *data) +static irqreturn_t oa_tc6_macphy_threaded_irq (int irq,void *data) {struct oa_tc6 *tc6 = data; -int ret; - -while (likely (!kthread_should_stop ())) { -/* This kthread will be waken up if there is a tx skb or mac-phy -* interrupt to perform spi transfer with tx chunks. -*/ -wait_event_interruptible (tc6->spi_wq, tc6->int_flag || - (tc6->waiting_tx_skb && - tc6->tx_credits) || -kthread_should_stop ()); - -if (kthread_should_stop ()) -break ; +int ret =0 ; - ret =oa_tc6_try_spi_transfer (tc6); -if (ret) -return ret; +/* It is possible that interrupt woke the thread before it is +* disabled. Until we come up with good recovery mechanism, +* no need to attempt spi transfer, once it fails. Pending skbs +* are already freed. +*/ +if (!tc6->disable_traffic) { +while (tc6->int_flag || + (tc6->waiting_tx_skb && tc6->tx_credits)) { + ret =oa_tc6_try_spi_transfer (tc6); +if (ret) { +disable_irq_nosync (tc6->spi->irq); +oa_tc6_disable_traffic (tc6); +break ; + } + } } -return 0 ; +return IRQ_HANDLED ; }static int oa_tc6_update_buffer_status_from_register (struct oa_tc6 *tc6)
@@ -1172,11 +1206,15 @@ static irqreturn_t oa_tc6_macphy_isr(int irq, void *data)* the previous rx footer. * - extended status event not reported in the previous rx footer. */ - tc6->int_flag = true; -/* Wake spi kthread to perform spi transfer */ -wake_up_interruptible (&tc6->spi_wq); - -return IRQ_HANDLED ; +if (tc6->disable_traffic) +disable_irq_nosync (tc6->spi->irq); +else + tc6->int_flag = true; +/* Wake IRQ thread to perform spi transfer . In case +* disable_traffic is set, threaded irq may run again +* one more time. +*/ +return IRQ_WAKE_THREAD ; }/**
@@ -1213,7 +1251,7 @@ EXPORT_SYMBOL_GPL(oa_tc6_zero_align_receive_frame_enable);*/ netdev_tx_t oa_tc6_start_xmit (struct oa_tc6 *tc6,struct sk_buff *skb) { -if (tc6->waiting_tx_skb) { +if (tc6->disable_traffic || tc6->waiting_tx_skb) {netif_stop_queue (tc6->netdev);return NETDEV_TX_BUSY ; }
@@ -1228,8 +1266,8 @@ netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb) tc6->waiting_tx_skb = skb;spin_unlock_bh (&tc6->tx_skb_lock); -/* Wake spi kthread to perform spi transfer */ -wake_up_interruptible (&tc6->spi_wq); +/* Wake the threaded IRQ to perform spi transfer. */ +irq_wake_thread (tc6->spi->irq, tc6);return NETDEV_TX_OK ; }
@@ -1322,24 +1360,15 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev) goto phy_exit; } -init_waitqueue_head (&tc6->spi_wq); - - tc6->spi_thread =kthread_run (oa_tc6_spi_thread_handler, tc6, -"oa-tc6-spi-thread" ); -if (IS_ERR (tc6->spi_thread)) { -dev_err (&tc6->spi->dev,"Failed to create SPI thread\n" ); - goto phy_exit; - } - -sched_set_fifo (tc6->spi_thread); - - ret =devm_request_irq (&tc6->spi->dev, tc6->spi->irq, oa_tc6_macphy_isr, -IRQF_TRIGGER_FALLING ,dev_name (&tc6->spi->dev), - tc6); + ret =devm_request_threaded_irq (&tc6->spi->dev, tc6->spi->irq, + oa_tc6_macphy_isr, + oa_tc6_macphy_threaded_irq, +IRQF_TRIGGER_LOW |IRQF_ONESHOT , +dev_name (&tc6->spi->dev), tc6);if (ret) {dev_err (&tc6->spi->dev,"Failed to request macphy isr %d\n" , ret); - goto kthread_stop; + goto phy_exit; }/* oa_tc6_sw_reset_macphy() function resets and clears the MAC-PHY reset
@@ -1349,12 +1378,10 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev)* 7.7 and 9.2.8.8 in the OPEN Alliance specification for more details. */ tc6->int_flag = true; -wake_up_interruptible (&tc6->spi_wq); +irq_wake_thread (tc6->spi->irq, tc6);return tc6; -kthread_stop: -kthread_stop (tc6->spi_thread); phy_exit:oa_tc6_phy_exit (tc6);return NULL ;
@@ -1367,11 +1394,10 @@ EXPORT_SYMBOL_GPL(oa_tc6_init);*/ void oa_tc6_exit (struct oa_tc6 *tc6) { + tc6->disable_traffic = true; +disable_irq (tc6->spi->irq);oa_tc6_phy_exit (tc6); -kthread_stop (tc6->spi_thread); -dev_kfree_skb_any (tc6->ongoing_tx_skb); -dev_kfree_skb_any (tc6->waiting_tx_skb); -dev_kfree_skb_any (tc6->rx_skb); +oa_tc6_free_pending_skbs (tc6); }EXPORT_SYMBOL_GPL (oa_tc6_exit);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 673/1250 net: ethernet: oa_tc6: Handle the OA TC6 SPI protected mode
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ciprian Regus <[email protected]>
[ Upstream commit 7d0e4c4b8c85d8ea2c77a90e1f7a7f74ce531e52 ]
Implement the OA TC6 standard defined protected mode for control (register access) transactions. In addition to the current register access formats the oa_tc6 driver handles, 1's complement values of the data field are included (by both the host and the MACPHY) in the SPI transfer frames. This feature acts as an integrity check.
Control write transactions look like this:
|<- 32 bits ->|<--- data_size --->|<- 32 bits ->|
MOSI: | ctrl header | reg write data | ignored |
MISO: | (discard) | echoed ctrl hdr | echoed data | data_size (LEN = number of registers to read in a sequence):
Unprotected: 32 x (LEN + 1) bits
Protected: 2 x 32 x (LEN + 1) bits
Control read transaction:
|<- 32 bits ->|<--- 32 bits --> |<- data_size ->|
MOSI: | ctrl header | ignored ... |
MISO: | (discard) | echoed ctrl hdr | reg read data | data_size (LEN = number of registers to read in a sequence):
Unprotected: 32 x (LEN + 1) bits
Protected: 2 x 32 x (LEN + 1) bits
Register data format ("reg write data" and "reg read data"):
Unprotected:
| W1 (normal) | W2 (normal) | ... | Wx (normal) |
Protected:
| W1 (normal) | W1 (complement) | ... | Wx (normal) | Wx (complement)|The protected mode state can be read from the bit 5 of CONFIG0 (0x4) register, and this setting is usually only configured during the MACPHY's reset (depending on the device it can be done by setting the state of a pin). We can read the protected mode configuration before any other register access and since the SPI transfer is initially sized for an unprotected read, the MACPHY's complement words are never clocked out and no checking is required. The data transactions (Ethernet frames) remain unchanged.
Stable-dep-of: 172c974113bf ("net: ethernet: oa_tc6: Improve the error recovery")
--- drivers/net/ethernet/oa_tc6.c | 93 ++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 17 deletions(-)
drivers/net/ethernet/oa_tc6.c +76 -17
@@ -25,6 +25,7 @@#define OA_TC6_REG_CONFIG0 0x0004#define CONFIG0_SYNC BIT(15)#define CONFIG0_ZARFE_ENABLE BIT(12) +#define CONFIG0_PROTE BIT(5)/* Status Register #0 */ #define OA_TC6_REG_STATUS0 0x0008
@@ -90,14 +91,17 @@#define OA_TC6_PHY_C45_AUTO_NEG_MMS5 5/* MMD 7 */ #define OA_TC6_PHY_C45_POWER_UNIT_MMS6 6/* MMD 13 */ +#define OA_TC6_CTRL_PROT_REPLY_SIZE 4#define OA_TC6_CTRL_HEADER_SIZE 4#define OA_TC6_CTRL_REG_VALUE_SIZE 4#define OA_TC6_CTRL_IGNORED_SIZE 4#define OA_TC6_CTRL_MAX_REGISTERS 128 -#define OA_TC6_CTRL_SPI_BUF_SIZE (OA_TC6_CTRL_HEADER_SIZE +\ - (OA_TC6_CTRL_MAX_REGISTERS *\ - OA_TC6_CTRL_REG_VALUE_SIZE) +\ - OA_TC6_CTRL_IGNORED_SIZE) +#define OA_TC6_CTRL_SPI_BUF_SIZE (OA_TC6_CTRL_HEADER_SIZE +\ + (OA_TC6_CTRL_MAX_REGISTERS *\ + (OA_TC6_CTRL_REG_VALUE_SIZE +\ + OA_TC6_CTRL_PROT_REPLY_SIZE)) +\ + OA_TC6_CTRL_IGNORED_SIZE) +#define OA_TC6_CHUNK_PAYLOAD_SIZE 64#define OA_TC6_DATA_HEADER_SIZE 4#define OA_TC6_CHUNK_SIZE (OA_TC6_DATA_HEADER_SIZE +\
@@ -131,6 +135,7 @@ struct oa_tc6 {bool rx_buf_overflow;bool int_flag;bool disable_traffic; +bool prot_ctrl; };enum oa_tc6_header_type {
@@ -214,25 +219,36 @@ static void oa_tc6_update_ctrl_write_data(struct oa_tc6 *tc6, u32 value[], {__be32 *tx_buf = tc6->spi_ctrl_tx_buf +OA_TC6_CTRL_HEADER_SIZE ; -for (int i =0 ; i < length; i++) +for (int i =0 ; i < length; i++) { *tx_buf++ =cpu_to_be32 (value[i]); +if (tc6->prot_ctrl) + *tx_buf++ =cpu_to_be32 (~value[i]); + } } -static u16 oa_tc6_calculate_ctrl_buf_size (u8 length) +static u16 oa_tc6_calculate_ctrl_buf_size (u8 length,bool ctrl_prot) { +u32 reply_size =OA_TC6_CTRL_REG_VALUE_SIZE ; + +if (ctrl_prot) + reply_size +=OA_TC6_CTRL_PROT_REPLY_SIZE ; +/* Control command consists 4 bytes header + 4 bytes register value for -* each register + 4 bytes ignored value. +* each register (+ 4 bytes for the register value complement in case +* protected mode is used) + 4 bytes ignored value. */ -return OA_TC6_CTRL_HEADER_SIZE +OA_TC6_CTRL_REG_VALUE_SIZE * length + +return OA_TC6_CTRL_HEADER_SIZE + reply_size * length +OA_TC6_CTRL_IGNORED_SIZE ; }static void oa_tc6_prepare_ctrl_spi_buf (struct oa_tc6 *tc6,u32 address,u32 value[],u8 length, -enum oa_tc6_register_op reg_op) +enum oa_tc6_register_op reg_op, +u16 buf_size) {__be32 *tx_buf = tc6->spi_ctrl_tx_buf; +memset (tx_buf,0 , buf_size); *tx_buf =oa_tc6_prepare_ctrl_header (address, length, reg_op);if (reg_op ==OA_TC6_CTRL_REG_WRITE )
@@ -255,10 +271,12 @@ static int oa_tc6_check_ctrl_write_reply(struct oa_tc6 *tc6, u8 size)return 0 ; } -static int oa_tc6_check_ctrl_read_reply (struct oa_tc6 *tc6,u8 size) +static int oa_tc6_check_ctrl_read_reply (struct oa_tc6 *tc6,u8 length) { -u32 *rx_buf = tc6->spi_ctrl_rx_buf +OA_TC6_CTRL_IGNORED_SIZE ; -u32 *tx_buf = tc6->spi_ctrl_tx_buf; +__be32 *rx_buf = tc6->spi_ctrl_rx_buf +OA_TC6_CTRL_IGNORED_SIZE ; +__be32 *tx_buf = tc6->spi_ctrl_tx_buf; +u32 complement; +u32 reply;/* The echoed control read header must match with the one that was * transmitted.
@@ -266,6 +284,20 @@ static int oa_tc6_check_ctrl_read_reply(struct oa_tc6 *tc6, u8 size)if (*tx_buf != *rx_buf)return -EPROTO ; +if (tc6->prot_ctrl) { +/* Skip past the echoed header to the value/complement pairs */ + rx_buf +=1 ; +for (int i =0 ; i < length; i++) { + reply =be32_to_cpu (rx_buf[0 ]); + complement =be32_to_cpu (rx_buf[1 ]); + +if (complement != ~reply) +return -EPROTO ; + + rx_buf +=2 ; + } + } +return 0 ; }
@@ -275,8 +307,13 @@ static void oa_tc6_copy_ctrl_read_data(struct oa_tc6 *tc6, u32 value[],__be32 *rx_buf = tc6->spi_ctrl_rx_buf +OA_TC6_CTRL_IGNORED_SIZE +OA_TC6_CTRL_HEADER_SIZE ; -for (int i =0 ; i < length; i++) +for (int i =0 ; i < length; i++) { value[i] =be32_to_cpu (*rx_buf++); + +/* skip complement word */ +if (tc6->prot_ctrl) + rx_buf++; + } } static int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 address, u32 value[],
@@ -285,10 +322,10 @@ static int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 address, u32 value[],u16 size;int ret; -/* Prepare control command and copy to SPI control buffer */ -oa_tc6_prepare_ctrl_spi_buf (tc6, address, value, length, reg_op); + size =oa_tc6_calculate_ctrl_buf_size (length, tc6->prot_ctrl); - size =oa_tc6_calculate_ctrl_buf_size (length); +/* Prepare control command and copy to SPI control buffer */ +oa_tc6_prepare_ctrl_spi_buf (tc6, address, value, length, reg_op, size);/* Perform SPI transfer */ ret =oa_tc6_spi_transfer (tc6,OA_TC6_CTRL_HEADER , size);
@@ -303,7 +340,7 @@ static int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 address, u32 value[],return oa_tc6_check_ctrl_write_reply (tc6, size);/* Check echoed/received control read command reply for errors */ - ret =oa_tc6_check_ctrl_read_reply (tc6, size); + ret =oa_tc6_check_ctrl_read_reply (tc6, length);if (ret)return ret;
@@ -1273,6 +1310,20 @@ netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb) }EXPORT_SYMBOL_GPL (oa_tc6_start_xmit); +static int oa_tc6_check_ctrl_protection (struct oa_tc6 *tc6) +{ +u32 regval; +int ret; + + ret =oa_tc6_read_register (tc6,OA_TC6_REG_CONFIG0 , ®val); +if (ret) +return ret; + + tc6->prot_ctrl =FIELD_GET (CONFIG0_PROTE , regval); + +return 0 ; +} +/** * oa_tc6_init - allocates and initializes oa_tc6 structure. * @spi: device with which data will be exchanged.
@@ -1325,6 +1376,14 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev)if (!tc6->spi_data_rx_buf)return NULL ; +/* Check the PROTE bit status so that we can reset the device */ + ret =oa_tc6_check_ctrl_protection (tc6); +if (ret) { +dev_err (&tc6->spi->dev, +"Failed to check the protection mode: %d\n" , ret); +return NULL ; + } + ret =oa_tc6_sw_reset_macphy (tc6);if (ret) { dev_err(&tc6->spi->dev,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 674/1250 net: ethernet: oa_tc6: Export standard defined registers
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ciprian Regus <[email protected]>
[ Upstream commit 9210d402bdf54240b8aec9815b2f9aad360fcb42 ]
Move defines for standard Open Alliance TC6 register addresses and subfields in the oa_tc6's header. As such, other ethernet drivers that rely on oa_tc6 can use them directly.
Stable-dep-of: 172c974113bf ("net: ethernet: oa_tc6: Improve the error recovery")
--- drivers/net/ethernet/oa_tc6.c | 50 ----------------------------------- include/linux/oa_tc6.h | 50 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 50 deletions(-)
drivers/net/ethernet/oa_tc6.c +0 -50
@@ -12,47 +12,6 @@#include <linux/phy.h> #include <linux/oa_tc6.h> -/* OPEN Alliance TC6 registers */ -/* Standard Capabilities Register */ -#define OA_TC6_REG_STDCAP 0x0002 -#define STDCAP_DIRECT_PHY_REG_ACCESS BIT(8) - -/* Reset Control and Status Register */ -#define OA_TC6_REG_RESET 0x0003 -#define RESET_SWRESET BIT(0)/* Software Reset */ - -/* Configuration Register #0 */ -#define OA_TC6_REG_CONFIG0 0x0004 -#define CONFIG0_SYNC BIT(15) -#define CONFIG0_ZARFE_ENABLE BIT(12) -#define CONFIG0_PROTE BIT(5) - -/* Status Register #0 */ -#define OA_TC6_REG_STATUS0 0x0008 -#define STATUS0_RESETC BIT(6)/* Reset Complete */ -#define STATUS0_HEADER_ERROR BIT(5) -#define STATUS0_LOSS_OF_FRAME_ERROR BIT(4) -#define STATUS0_RX_BUFFER_OVERFLOW_ERROR BIT(3) -#define STATUS0_TX_PROTOCOL_ERROR BIT(0) - -/* Buffer Status Register */ -#define OA_TC6_REG_BUFFER_STATUS 0x000B -#define BUFFER_STATUS_TX_CREDITS_AVAILABLE GENMASK(15, 8) -#define BUFFER_STATUS_RX_CHUNKS_AVAILABLE GENMASK(7, 0) - -/* Interrupt Mask Register #0 */ -#define OA_TC6_REG_INT_MASK0 0x000C -#define INT_MASK0_HEADER_ERR_MASK BIT(5) -#define INT_MASK0_LOSS_OF_FRAME_ERR_MASK BIT(4) -#define INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3) -#define INT_MASK0_TX_PROTOCOL_ERR_MASK BIT(0) -#define INT_MASK0_ALL_INTERRUPTS (GENMASK(5, 0) | \ - GENMASK(12, 7)) - -/* PHY Clause 22 registers base address and mask */ -#define OA_TC6_PHY_STD_REG_ADDR_BASE 0xFF00 -#define OA_TC6_PHY_STD_REG_ADDR_MASK 0x1F -/* Control command header */ #define OA_TC6_CTRL_HEADER_DATA_NOT_CTRL BIT(31)#define OA_TC6_CTRL_HEADER_WRITE_NOT_READ BIT(29)
@@ -82,15 +41,6 @@#define OA_TC6_DATA_FOOTER_END_BYTE_OFFSET GENMASK(13, 8)#define OA_TC6_DATA_FOOTER_TX_CREDITS GENMASK(5, 1) -/* PHY – Clause 45 registers memory map selector (MMS) as per table 6 in the -* OPEN Alliance specification. -*/ -#define OA_TC6_PHY_C45_PCS_MMS2 2/* MMD 3 */ -#define OA_TC6_PHY_C45_PMA_PMD_MMS3 3/* MMD 1 */ -#define OA_TC6_PHY_C45_VS_PLCA_MMS4 4/* MMD 31 */ -#define OA_TC6_PHY_C45_AUTO_NEG_MMS5 5/* MMD 7 */ -#define OA_TC6_PHY_C45_POWER_UNIT_MMS6 6/* MMD 13 */ -#define OA_TC6_CTRL_PROT_REPLY_SIZE 4#define OA_TC6_CTRL_HEADER_SIZE 4#define OA_TC6_CTRL_REG_VALUE_SIZE 4
include/linux/oa_tc6.h +50 -0
56 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 675/1250 net: ethernet: oa_tc6: Add the OA_TC6_ prefix to standard registers
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ciprian Regus <[email protected]>
[ Upstream commit 31bc75f17c1f5ff989fa896aae3e4e411d9b0b7a ]
The OA TC6 standard registers are currently exported in a header file. Add the OA_TC6_ prefix to the register address and subfield mask macros to avoid future naming conflicts.
Stable-dep-of: 172c974113bf ("net: ethernet: oa_tc6: Improve the error recovery")
--- drivers/net/ethernet/oa_tc6.c | 37 ++++++++++++++++---------------- include/linux/oa_tc6.h | 40 +++++++++++++++++------------------ 2 files changed, 39 insertions(+), 38 deletions(-)
drivers/net/ethernet/oa_tc6.c +19 -18
@@ -396,7 +396,7 @@ static int oa_tc6_check_phy_reg_direct_access_capability(struct oa_tc6 *tc6)if (ret)return ret; -if (!(regval &STDCAP_DIRECT_PHY_REG_ACCESS )) +if (!(regval &OA_TC6_STDCAP_DIRECT_PHY_REG_ACCESS ))return -ENODEV ;return 0 ;
@@ -589,7 +589,7 @@ static int oa_tc6_read_status0(struct oa_tc6 *tc6)static int oa_tc6_sw_reset_macphy (struct oa_tc6 *tc6) { -u32 regval =RESET_SWRESET ; +u32 regval =OA_TC6_RESET_SWRESET ;int ret; ret =oa_tc6_write_register (tc6,OA_TC6_REG_RESET , regval);
@@ -598,7 +598,7 @@ static int oa_tc6_sw_reset_macphy(struct oa_tc6 *tc6)/* Poll for soft reset complete for every 1ms until 1s timeout */ ret =readx_poll_timeout (oa_tc6_read_status0, tc6, regval, - regval &STATUS0_RESETC , + regval &OA_TC6_STATUS0_RESETC ,STATUS0_RESETC_POLL_DELAY ,STATUS0_RESETC_POLL_TIMEOUT );if (ret)
@@ -617,10 +617,10 @@ static int oa_tc6_unmask_macphy_error_interrupts(struct oa_tc6 *tc6)if (ret)return ret; - regval &= ~(INT_MASK0_TX_PROTOCOL_ERR_MASK | -INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK | -INT_MASK0_LOSS_OF_FRAME_ERR_MASK | -INT_MASK0_HEADER_ERR_MASK ); + regval &= ~(OA_TC6_INT_MASK0_TX_PROTOCOL_ERR_MASK | +OA_TC6_INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK | +OA_TC6_INT_MASK0_LOSS_OF_FRAME_ERR_MASK | +OA_TC6_INT_MASK0_HEADER_ERR_MASK );return oa_tc6_write_register (tc6,OA_TC6_REG_INT_MASK0 , regval); }
@@ -635,7 +635,7 @@ static int oa_tc6_enable_data_transfer(struct oa_tc6 *tc6)return ret;/* Enable configuration synchronization for data transfer */ - value |=CONFIG0_SYNC ; + value |=OA_TC6_CONFIG0_SYNC ;return oa_tc6_write_register (tc6,OA_TC6_REG_CONFIG0 , value); }
@@ -680,7 +680,7 @@ static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)*/ static void oa_tc6_disable_traffic (struct oa_tc6 *tc6) { -u32 regval =INT_MASK0_ALL_INTERRUPTS ; +u32 regval =OA_TC6_INT_MASK0_ALL_INTERRUPTS ; tc6->disable_traffic = true;oa_tc6_free_pending_skbs (tc6);
@@ -710,25 +710,25 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)return ret; } -if (FIELD_GET (STATUS0_RX_BUFFER_OVERFLOW_ERROR , value)) { +if (FIELD_GET (OA_TC6_STATUS0_RX_BUFFER_OVERFLOW_ERROR , value)) { tc6->rx_buf_overflow = true;oa_tc6_cleanup_ongoing_rx_skb (tc6);net_err_ratelimited ("%s: Receive buffer overflow error\n" , tc6->netdev->name);return -EAGAIN ; } -if (FIELD_GET (STATUS0_TX_PROTOCOL_ERROR , value)) { +if (FIELD_GET (OA_TC6_STATUS0_TX_PROTOCOL_ERROR , value)) {netdev_err (tc6->netdev,"Transmit protocol error\n" );return -ENODEV ; }/* TODO: Currently loss of frame and header errors are treated as * non-recoverable errors. They will be handled in the next version. */ -if (FIELD_GET (STATUS0_LOSS_OF_FRAME_ERROR , value)) { +if (FIELD_GET (OA_TC6_STATUS0_LOSS_OF_FRAME_ERROR , value)) {netdev_err (tc6->netdev,"Loss of frame error\n" );return -ENODEV ; } -if (FIELD_GET (STATUS0_HEADER_ERROR , value)) { +if (FIELD_GET (OA_TC6_STATUS0_HEADER_ERROR , value)) {netdev_err (tc6->netdev,"Header error\n" );return -ENODEV ; }
@@ -1175,9 +1175,10 @@ static int oa_tc6_update_buffer_status_from_register(struct oa_tc6 *tc6)if (ret)return ret; - tc6->tx_credits =FIELD_GET (BUFFER_STATUS_TX_CREDITS_AVAILABLE , value); - tc6->rx_chunks_available =FIELD_GET (BUFFER_STATUS_RX_CHUNKS_AVAILABLE , - value); + tc6->tx_credits =FIELD_GET (OA_TC6_BUFFER_STATUS_TX_CREDITS_AVAILABLE , + value); + tc6->rx_chunks_available = +FIELD_GET (OA_TC6_BUFFER_STATUS_RX_CHUNKS_AVAILABLE , value);return 0 ; }
@@ -1221,7 +1222,7 @@ int oa_tc6_zero_align_receive_frame_enable(struct oa_tc6 *tc6)return ret;/* Set Zero-Align Receive Frame Enable */ - regval |=CONFIG0_ZARFE_ENABLE ; + regval |=OA_TC6_CONFIG0_ZARFE_ENABLE ;return oa_tc6_write_register (tc6,OA_TC6_REG_CONFIG0 , regval); }
@@ -1269,7 +1270,7 @@ static int oa_tc6_check_ctrl_protection(struct oa_tc6 *tc6)if (ret)return ret; - tc6->prot_ctrl =FIELD_GET (CONFIG0_PROTE , regval); + tc6->prot_ctrl =FIELD_GET (OA_TC6_CONFIG0_PROTE , regval);return 0 ; }
include/linux/oa_tc6.h +20 -20
@@ -13,39 +13,39 @@/* OPEN Alliance TC6 registers */ /* Standard Capabilities Register */ #define OA_TC6_REG_STDCAP 0x0002 -#define STDCAP_DIRECT_PHY_REG_ACCESS BIT(8) +#define OA_TC6_STDCAP_DIRECT_PHY_REG_ACCESS BIT(8)/* Reset Control and Status Register */ #define OA_TC6_REG_RESET 0x0003 -#define RESET_SWRESET BIT(0)/* Software Reset */ +#define OA_TC6_RESET_SWRESET BIT(0)/* Software Reset */ /* Configuration Register #0 */ #define OA_TC6_REG_CONFIG0 0x0004 -#define CONFIG0_SYNC BIT(15) -#define CONFIG0_ZARFE_ENABLE BIT(12) -#define CONFIG0_PROTE BIT(5) +#define OA_TC6_CONFIG0_SYNC BIT(15) +#define OA_TC6_CONFIG0_ZARFE_ENABLE BIT(12) +#define OA_TC6_CONFIG0_PROTE BIT(5)/* Status Register #0 */ #define OA_TC6_REG_STATUS0 0x0008 -#define STATUS0_RESETC BIT(6)/* Reset Complete */ -#define STATUS0_HEADER_ERROR BIT(5) -#define STATUS0_LOSS_OF_FRAME_ERROR BIT(4) -#define STATUS0_RX_BUFFER_OVERFLOW_ERROR BIT(3) -#define STATUS0_TX_PROTOCOL_ERROR BIT(0) +#define OA_TC6_STATUS0_RESETC BIT(6)/* Reset Complete */ +#define OA_TC6_STATUS0_HEADER_ERROR BIT(5) +#define OA_TC6_STATUS0_LOSS_OF_FRAME_ERROR BIT(4) +#define OA_TC6_STATUS0_RX_BUFFER_OVERFLOW_ERROR BIT(3) +#define OA_TC6_STATUS0_TX_PROTOCOL_ERROR BIT(0)/* Buffer Status Register */ -#define OA_TC6_REG_BUFFER_STATUS 0x000B -#define BUFFER_STATUS_TX_CREDITS_AVAILABLE GENMASK(15, 8) -#define BUFFER_STATUS_RX_CHUNKS_AVAILABLE GENMASK(7, 0) +#define OA_TC6_REG_BUFFER_STATUS 0x000B +#define OA_TC6_BUFFER_STATUS_TX_CREDITS_AVAILABLE GENMASK(15, 8) +#define OA_TC6_BUFFER_STATUS_RX_CHUNKS_AVAILABLE GENMASK(7, 0)/* Interrupt Mask Register #0 */ -#define OA_TC6_REG_INT_MASK0 0x000C -#define INT_MASK0_HEADER_ERR_MASK BIT(5) -#define INT_MASK0_LOSS_OF_FRAME_ERR_MASK BIT(4) -#define INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3) -#define INT_MASK0_TX_PROTOCOL_ERR_MASK BIT(0) -#define INT_MASK0_ALL_INTERRUPTS (GENMASK(5, 0) | \ - GENMASK(12, 7)) +#define OA_TC6_REG_INT_MASK0 0x000C +#define OA_TC6_INT_MASK0_HEADER_ERR_MASK BIT(5) +#define OA_TC6_INT_MASK0_LOSS_OF_FRAME_ERR_MASK BIT(4) +#define OA_TC6_INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3) +#define OA_TC6_INT_MASK0_TX_PROTOCOL_ERR_MASK BIT(0) +#define OA_TC6_INT_MASK0_ALL_INTERRUPTS (GENMASK(5, 0) | \ + GENMASK(12, 7))/* PHY Clause 22 registers base address and mask */ #define OA_TC6_PHY_STD_REG_ADDR_BASE 0xFF00
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 676/1250 net: ethernet: oa_tc6: Protect skb pointer used by two different kernel instances
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit 5443d9c4f55d46634b95432e1e8a40b824019bbb ]
Threaded IRQ uses waiting_tx_skb. Transmit path also uses this pointer without any mutual exclusion protection. As a result, it might leak skb buffer, particularly if threaded IRQ sets disable_traffic true after start_xmit already checked and found that disable_traffic being false, if they happen to run on different cores.
On fatal error, where disable_traffic is set, transmit function drops the packet and return NETDEV_TX_OK. Due to this change, skb_linearize call is moved up to the beginning of the transmit function.
Since skb buffer may be freed from different contexts, dev_kfree_skb_any is used to free skb buffer now, replacing one of the kfree_skb call.
oa_tc6_exit disables the irq before setting disable_traffic true.
Stable-dep-of: 172c974113bf ("net: ethernet: oa_tc6: Improve the error recovery")
--- drivers/net/ethernet/oa_tc6.c | 109 ++++++++++++++++++++++++---------- 1 file changed, 76 insertions(+), 33 deletions(-)
drivers/net/ethernet/oa_tc6.c +76 -33
@@ -640,6 +640,26 @@ static int oa_tc6_enable_data_transfer(struct oa_tc6 *tc6)return oa_tc6_write_register (tc6,OA_TC6_REG_CONFIG0 , value); } +/* Called when a frame that is meant to be transmitted, is dropped. */ +static void oa_tc6_drop_tx_skb (struct oa_tc6 *tc6,struct sk_buff *skb) +{ +if (skb) { + tc6->netdev->stats.tx_dropped++; +dev_kfree_skb_any (skb); + } +} + +static struct sk_buff *oa_tc6_detach_waiting_tx_skb (struct oa_tc6 *tc6) +{ +struct sk_buff *skb; + +lockdep_assert_held (&tc6->tx_skb_lock); + skb = tc6->waiting_tx_skb; + tc6->waiting_tx_skb =NULL ; + +return skb; +} +static void oa_tc6_cleanup_ongoing_rx_skb (struct oa_tc6 *tc6) {if (tc6->rx_skb) {
@@ -651,26 +671,30 @@ static void oa_tc6_cleanup_ongoing_rx_skb(struct oa_tc6 *tc6)static void oa_tc6_cleanup_ongoing_tx_skb (struct oa_tc6 *tc6) { -if (tc6->ongoing_tx_skb) { - tc6->netdev->stats.tx_dropped++; -kfree_skb (tc6->ongoing_tx_skb); - tc6->ongoing_tx_skb =NULL ; - } +oa_tc6_drop_tx_skb (tc6, tc6->ongoing_tx_skb); + tc6->ongoing_tx_skb =NULL ; }static void oa_tc6_cleanup_waiting_tx_skb (struct oa_tc6 *tc6) { -if (tc6->waiting_tx_skb) { - tc6->netdev->stats.tx_dropped++; -kfree_skb (tc6->waiting_tx_skb); - tc6->waiting_tx_skb =NULL ; - } +struct sk_buff *skb; + +spin_lock_bh (&tc6->tx_skb_lock); + skb =oa_tc6_detach_waiting_tx_skb (tc6); +spin_unlock_bh (&tc6->tx_skb_lock); + +oa_tc6_drop_tx_skb (tc6, skb); } -static void oa_tc6_free_pending_skbs (struct oa_tc6 *tc6) +static void oa_tc6_free_ongoing_skbs (struct oa_tc6 *tc6) {oa_tc6_cleanup_ongoing_tx_skb (tc6);oa_tc6_cleanup_ongoing_rx_skb (tc6); +} + +static void oa_tc6_free_pending_skbs (struct oa_tc6 *tc6) +{ +oa_tc6_free_ongoing_skbs (tc6);oa_tc6_cleanup_waiting_tx_skb (tc6); }
@@ -681,9 +705,15 @@ static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)static void oa_tc6_disable_traffic (struct oa_tc6 *tc6) {u32 regval =OA_TC6_INT_MASK0_ALL_INTERRUPTS ; +struct sk_buff *skb; +spin_lock_bh (&tc6->tx_skb_lock); tc6->disable_traffic = true; -oa_tc6_free_pending_skbs (tc6); + skb =oa_tc6_detach_waiting_tx_skb (tc6); +spin_unlock_bh (&tc6->tx_skb_lock); + +oa_tc6_drop_tx_skb (tc6, skb); +oa_tc6_free_ongoing_skbs (tc6);oa_tc6_write_register (tc6,OA_TC6_REG_INT_MASK0 , regval);oa_tc6_read_register (tc6,OA_TC6_REG_STATUS0 , ®val);oa_tc6_write_register (tc6,OA_TC6_REG_STATUS0 , regval);
@@ -1124,8 +1154,7 @@ static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6)if (ret == -EAGAIN )continue ; -oa_tc6_cleanup_ongoing_tx_skb (tc6); -oa_tc6_cleanup_ongoing_rx_skb (tc6); +oa_tc6_free_ongoing_skbs (tc6);netdev_err (tc6->netdev,"Device error: %d\n" , ret);return ret; }
@@ -1147,15 +1176,20 @@ static irqreturn_t oa_tc6_macphy_threaded_irq(int irq, void *data)* no need to attempt spi transfer, once it fails. Pending skbs * are already freed. */ -if (!tc6->disable_traffic) { -while (tc6->int_flag || - (tc6->waiting_tx_skb && tc6->tx_credits)) { - ret =oa_tc6_try_spi_transfer (tc6); -if (ret) { -disable_irq_nosync (tc6->spi->irq); -oa_tc6_disable_traffic (tc6); -break ; - } +spin_lock_bh (&tc6->tx_skb_lock); +if (tc6->disable_traffic) { +spin_unlock_bh (&tc6->tx_skb_lock); +return IRQ_HANDLED ; + } +spin_unlock_bh (&tc6->tx_skb_lock); + +while (tc6->int_flag || + (tc6->waiting_tx_skb && tc6->tx_credits)) { + ret =oa_tc6_try_spi_transfer (tc6); +if (ret) { +disable_irq_nosync (tc6->spi->irq); +oa_tc6_disable_traffic (tc6); +break ; } }
@@ -1234,23 +1268,30 @@ EXPORT_SYMBOL_GPL(oa_tc6_zero_align_receive_frame_enable);* @tc6: oa_tc6 struct. * @skb: socket buffer in which the ethernet frame is stored. * -* Return: NETDEV_TX_OK if the transmit ethernet frame skb added in the tx_skb_q -* otherwise returns NETDEV_TX_BUSY. +* Return: NETDEV_TX_OK either on successful queueing of the packet for +* transmission, or on packet getting dropped. Packet can be dropped due to +* failure in linearizing the buffer or disable_traffic is set due to +* earlier fatal error. Returns NETDEV_TX_BUSY when there is no room +* to queue the packet. */ netdev_tx_t oa_tc6_start_xmit (struct oa_tc6 *tc6,struct sk_buff *skb) { -if (tc6->disable_traffic || tc6->waiting_tx_skb) { -netif_stop_queue (tc6->netdev); -return NETDEV_TX_BUSY ; - } -if (skb_linearize (skb)) { -dev_kfree_skb_any (skb); - tc6->netdev->stats.tx_dropped++; +oa_tc6_drop_tx_skb (tc6, skb);return NETDEV_TX_OK ; }spin_lock_bh (&tc6->tx_skb_lock); +if (tc6->waiting_tx_skb) { +netif_stop_queue (tc6->netdev); +spin_unlock_bh (&tc6->tx_skb_lock); +return NETDEV_TX_BUSY ; + } +if (tc6->disable_traffic) { +spin_unlock_bh (&tc6->tx_skb_lock); +oa_tc6_drop_tx_skb (tc6, skb); +return NETDEV_TX_OK ; + } tc6->waiting_tx_skb = skb;spin_unlock_bh (&tc6->tx_skb_lock);
@@ -1404,8 +1445,10 @@ EXPORT_SYMBOL_GPL(oa_tc6_init);*/ void oa_tc6_exit (struct oa_tc6 *tc6) { - tc6->disable_traffic = true;disable_irq (tc6->spi->irq); +spin_lock_bh (&tc6->tx_skb_lock); + tc6->disable_traffic = true; +spin_unlock_bh (&tc6->tx_skb_lock);oa_tc6_phy_exit (tc6);oa_tc6_free_pending_skbs (tc6); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 677/1250 net: ethernet: oa_tc6: Improve the error recovery
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit 172c974113bffe5723b80b1acac17593bb50513c ]
When oversubscribed traffic causes lot of buffer overflow errors, probably due to loss of data chunks, driver fails to find a data chunk with end_valid bit set, before it runs out of sk buffer space. As a result, assert is seen during skb_put.
Now, check is made if skb buffer has enough tailroom for the incoming data before accepting. If there is no room, current frame is abandoned and it will start looking for a data chunk with start_valid bit, that is a new frame.
SK buffer allocation error is considered as recoverable error.
rx_buf_overflow flag is too specific and no longer the only condition this flag is used for. Therefore it is renamed as wait_until_start_valid. This is more appropriate as this flag is used to look for the next data chunk with SV bit set, after failures like buffer overflow, buffer allocation failure, skb pointer validity besides buffer overflow error.
Not writing to status0 if it reads 0.
--- drivers/net/ethernet/oa_tc6.c | 143 +++++++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 35 deletions(-)
drivers/net/ethernet/oa_tc6.c +108 -35
@@ -82,7 +82,7 @@ struct oa_tc6 {u16 spi_data_tx_buf_offset;u16 tx_credits;u8 rx_chunks_available; -bool rx_buf_overflow; +bool wait_until_start_valid;bool int_flag;bool disable_traffic;bool prot_ctrl;
@@ -698,6 +698,12 @@ static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)oa_tc6_cleanup_waiting_tx_skb (tc6); } +static void oa_tc6_look_for_new_frame (struct oa_tc6 *tc6) +{ + tc6->wait_until_start_valid = true; +oa_tc6_cleanup_ongoing_rx_skb (tc6); +} +/* If the failure is at SPI interface level, masking and clearing * the interrupt of the device won't work. Since SPI interrupt is * disabled, it should stop the repeated interrupts.
@@ -732,6 +738,13 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)return ret; } +/* This function is called for each chunk received in a given SPI +* transaction. In case, extended status bit is set in more than +* one chunk, skip the write, if status0 is already cleared. +*/ +if (!value) +return 0 ; +/* Clear the error interrupts status */ ret =oa_tc6_write_register (tc6,OA_TC6_REG_STATUS0 , value);if (ret) {
@@ -741,8 +754,7 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6) }if (FIELD_GET (OA_TC6_STATUS0_RX_BUFFER_OVERFLOW_ERROR , value)) { - tc6->rx_buf_overflow = true; -oa_tc6_cleanup_ongoing_rx_skb (tc6); +oa_tc6_look_for_new_frame (tc6);net_err_ratelimited ("%s: Receive buffer overflow error\n" , tc6->netdev->name);return -EAGAIN ;
@@ -768,6 +780,8 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)static int oa_tc6_process_rx_chunk_footer (struct oa_tc6 *tc6,u32 footer) { +int ret =0 ; +/* Process rx chunk footer for the following, * 1. tx credits * 2. errors if any from MAC-PHY
@@ -778,9 +792,11 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer) footer);if (FIELD_GET (OA_TC6_DATA_FOOTER_EXTENDED_STS , footer)) { -int ret =oa_tc6_process_extended_status (tc6); - -if (ret) + ret =oa_tc6_process_extended_status (tc6); +/* EAGAIN error is recoverable. Move on to check +* HEADER and SYNC errors before returning. +*/ +if (ret && ret != -EAGAIN )return ret; }
@@ -798,7 +814,7 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer)return -ENODEV ; } -return 0 ; +return ret; }static void oa_tc6_submit_rx_skb (struct oa_tc6 *tc6)
@@ -823,13 +839,35 @@ static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6) tc6->rx_skb =NULL ; } -static void oa_tc6_update_rx_skb (struct oa_tc6 *tc6,u8 *payload,u8 length) +/* On oversubscribed traffic condition, particularly with overwhelming rx +* buffer overflow errors, there could be data chunk loss. If tail + length +* goes beyond end pointer, that is an indication that the data chunk with +* end_valid bit is lost. Time to look for a data chunk with start_valid bit. +* +* If rx_skb is NULL, it is time to start looking for data chunk with +* start_bit. +*/ +static int oa_tc6_update_rx_skb (struct oa_tc6 *tc6,u8 *payload,u8 length) { +if (!tc6->rx_skb || +skb_tailroom (tc6->rx_skb) < length) { +oa_tc6_look_for_new_frame (tc6); +return -EAGAIN ; + } +memcpy (skb_put (tc6->rx_skb, length), payload, length); +return 0 ; } +/* On overwhelming rx buffer overflow errors, due to data chunk loss, it is +* possible that we get two data chunks with start_valid bit set, without +* end_valid bit set in between. In this case, rx_skb would have a valid +* buffer pointer. We should release, if a valid pointer is found before +* allocating a new one. +*/ static int oa_tc6_allocate_rx_skb (struct oa_tc6 *tc6) { +oa_tc6_cleanup_ongoing_rx_skb (tc6); tc6->rx_skb =netdev_alloc_skb_ip_align (tc6->netdev, tc6->netdev->mtu +ETH_HLEN +ETH_FCS_LEN );if (!tc6->rx_skb) {
@@ -849,7 +887,9 @@ static int oa_tc6_prcs_complete_rx_frame(struct oa_tc6 *tc6, u8 *payload,if (ret)return ret; -oa_tc6_update_rx_skb (tc6, payload, size); + ret =oa_tc6_update_rx_skb (tc6, payload, size); +if (ret) +return ret;oa_tc6_submit_rx_skb (tc6);
@@ -864,22 +904,24 @@ static int oa_tc6_prcs_rx_frame_start(struct oa_tc6 *tc6, u8 *payload, u16 size)if (ret)return ret; -oa_tc6_update_rx_skb (tc6, payload, size); - -return 0 ; +return oa_tc6_update_rx_skb (tc6, payload, size); } -static void oa_tc6_prcs_rx_frame_end (struct oa_tc6 *tc6,u8 *payload,u16 size) +static int oa_tc6_prcs_rx_frame_end (struct oa_tc6 *tc6,u8 *payload,u16 size) { -oa_tc6_update_rx_skb (tc6, payload, size); +int ret; -oa_tc6_submit_rx_skb (tc6); + ret =oa_tc6_update_rx_skb (tc6, payload, size); +if (!ret) +oa_tc6_submit_rx_skb (tc6); +return ret; } -static void oa_tc6_prcs_ongoing_rx_frame (struct oa_tc6 *tc6,u8 *payload, -u32 footer) +static int oa_tc6_prcs_ongoing_rx_frame (struct oa_tc6 *tc6,u8 *payload, +u32 footer) { -oa_tc6_update_rx_skb (tc6, payload,OA_TC6_CHUNK_PAYLOAD_SIZE ); +return oa_tc6_update_rx_skb (tc6, payload, +OA_TC6_CHUNK_PAYLOAD_SIZE ); } static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
@@ -894,10 +936,10 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,u16 size;/* Restart the new rx frame after receiving rx buffer overflow error */ -if (start_valid && tc6->rx_buf_overflow) - tc6->rx_buf_overflow = false; +if (start_valid && tc6->wait_until_start_valid) + tc6->wait_until_start_valid = false; -if (tc6->rx_buf_overflow) +if (tc6->wait_until_start_valid)return 0 ;/* Process the chunk with complete rx frame */
@@ -919,8 +961,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,/* Process the chunk with only rx frame end */ if (end_valid && !start_valid) { size = end_byte_offset +1 ; -oa_tc6_prcs_rx_frame_end (tc6, data, size); -return 0 ; +return oa_tc6_prcs_rx_frame_end (tc6, data, size); }/* Process the chunk with previous rx frame end and next rx frame
@@ -934,6 +975,15 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,if (tc6->rx_skb) { size = end_byte_offset +1 ;oa_tc6_prcs_rx_frame_end (tc6, data, size); + +/* Return value from oa_tc6_prcs_rx_frame_end is not +* checked. If it returned an error, it is to make +* the code to look for new frame. At this stage, +* code below is going to process a new frame. So, +* error condition is set to false, in case it is +* set before proceeding. +*/ + tc6->wait_until_start_valid = false; } size =OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset; return oa_tc6_prcs_rx_frame_start(tc6,
@@ -942,9 +992,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data, }/* Process the chunk with ongoing rx frame data */ -oa_tc6_prcs_ongoing_rx_frame (tc6, data, footer); - -return 0 ; +return oa_tc6_prcs_ongoing_rx_frame (tc6, data, footer); }static u32 oa_tc6_get_rx_chunk_footer (struct oa_tc6 *tc6,u16 footer_offset)
@@ -960,8 +1008,9 @@ static u32 oa_tc6_get_rx_chunk_footer(struct oa_tc6 *tc6, u16 footer_offset)static int oa_tc6_process_spi_data_rx_buf (struct oa_tc6 *tc6,u16 length) {u16 no_of_rx_chunks = length /OA_TC6_CHUNK_SIZE ; +bool retry = false; +int ret =0 ;u32 footer; -int ret;/* All the rx chunks in the receive SPI data buffer are examined here */ for (int i =0 ; i < no_of_rx_chunks; i++) {
@@ -970,8 +1019,11 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length) OA_TC6_CHUNK_PAYLOAD_SIZE); ret =oa_tc6_process_rx_chunk_footer (tc6, footer); -if (ret) -return ret; +if (ret) { +if (ret != -EAGAIN ) +return ret; + retry = true; + }/* If there is a data valid chunks then process it for the * information needed to determine the validity and the location
@@ -983,12 +1035,35 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length) ret =oa_tc6_prcs_rx_chunk_payload (tc6, payload, footer); -if (ret) -return ret; +if (ret) { +if (ret != -ENOMEM && ret != -EAGAIN ) +return ret; + retry = true; + } } } -return 0 ; +/* Not bailing out on recoverable error codes, -EAGAIN and +* -ENOMEM. If subsequent loop iterations, if any, succeeds, +* error code would be overwritten. retry flag helps to +* make the caller to continue and retry. Since recovery +* action for -ENOMEM and -EAGAIN are same, we are returning +* one of the error codes, that is -EAGAIN. +* +* Successful recovery depends on how small the frames are, +* how many chunks, among the received chunks triggered the +* error, whether data is intact even with error conditions. +* As a result, there is no single, best method to recover +* most data when error conditions hit. We do our best by +* processing all the chunks with good "footer header" and +* "data valid" bit set. +*/ +if (retry) { + ret = -EAGAIN ; +oa_tc6_look_for_new_frame (tc6); + } + +return ret; } static __be32 oa_tc6_prepare_data_header(bool data_valid, bool start_valid,
@@ -1150,10 +1225,8 @@ static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6) } ret =oa_tc6_process_spi_data_rx_buf (tc6, spi_len); -if (ret) { -if (ret == -EAGAIN ) -continue ; +if (ret && ret != -EAGAIN ) {oa_tc6_free_ongoing_skbs (tc6);netdev_err (tc6->netdev,"Device error: %d\n" , ret);return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 678/1250 net: ethernet: oa_tc6: Disable tx queues on fatal error
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit 349c366365876b7f67120827a0deb44899f59303 ]
Previously, TX queue interface was stopped when disable_traffic flag was set, which would indicate fatal error. It is more appropriate to disable the queue as, unless driver is unloaded and reloaded, there is no recovery after disable_traffic is set.
Queues may be re-enabled inadvertently by other layers. Intention of disable_traffic is only to stop the traffic from flowing on fatal error.
--- drivers/net/ethernet/oa_tc6.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/net/ethernet/oa_tc6.c +4 -0
@@ -718,6 +718,10 @@ static void oa_tc6_disable_traffic(struct oa_tc6 *tc6) skb =oa_tc6_detach_waiting_tx_skb (tc6);spin_unlock_bh (&tc6->tx_skb_lock); +/* disable_traffic, when set, is a point of no return to +* working state. Keeping the TX queues disabled. +*/ +netif_tx_disable (tc6->netdev);oa_tc6_drop_tx_skb (tc6, skb);oa_tc6_free_ongoing_skbs (tc6);oa_tc6_write_register (tc6,OA_TC6_REG_INT_MASK0 , regval);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 679/1250 net: ethernet: oa_tc6: Fix for the wrong data type
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit 3cc2aa96b97184abd6fc106aac626ddf14389813 ]
Inadvertently bool data type is used where int is supposed to be used. This might turn a negative error code into true or false and sign of the return code would be lost.
--- drivers/net/ethernet/oa_tc6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/oa_tc6.c +1 -1
@@ -411,7 +411,7 @@ static int oa_tc6_mdiobus_read(struct mii_bus *bus, int addr, int regnum) {struct oa_tc6 *tc6 = bus->priv;u32 regval; -bool ret; +int ret; ret = oa_tc6_read_register(tc6, OA_TC6_PHY_STD_REG_ADDR_BASE | (regnum & OA_TC6_PHY_STD_REG_ADDR_MASK),
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 680/1250 net/sched: cls_u32: fix duplicate handle when node ID pool is exhausted
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit d7e7e98d23f42a92d9ab7e36302bd96bd9b33b5f ]
gen_new_kid() falls back to returning max (htid | 0xFFF) when both idr_alloc_u32() ranges are full, instead of reporting an error. u32_change() trusts that value and inserts a new knode with a handle that is already live in the hash table, breaking handle uniqueness within the table's node ID space.
The handle was never reserved in ht->handle_idr, so every later error path that does idr_remove(&ht->handle_idr, handle) removes the reservation of a different, live knode, which is then reused — one failed add compounds into further duplicates.
The 4095 limit is per (table, bucket) — ht->handle_idr is per hash table and the range is derived from htid (bucketid), so a table with divisor 256 can legitimately hold 256*4095 knodes.
The sibling helper gen_new_htid() has the same silent in-band failure:
it returns 0 when the tp_c handle pool (1..0x7FF) is full, and u32_init() publishes the root hash table with handle 0 without checking. Two root tables with handle 0 alias in u32_lookup_ht(), allowing cross-tcf_proto knode add/lookup/delete. Add the same exhaustion check that the divisor path already has.
Return an error so u32_change() fails with ENOSPC/ENOMEM when the node ID space is exhausted, and so u32_init() fails with -ENOMEM when the hash table ID space is exhausted. The extack message distinguishes pool exhaustion (-ENOSPC) from a transient allocation failure (-ENOMEM).
Conditions to recreate the bug:
- CONFIG_NET_SCHED=y, CONFIG_CLS_U32=y (or =m with module loaded)
- Create a clsact qdisc on a device, then add 4095 u32 filters with auto-generated handles to fill the node ID space for the root hash table (single bucket). The 4096th auto-handle filter add triggers the duplicate handle (fh 800::fff reused). Reachable at Level 2 (unshare -Urn, namespace-local CAP_NET_ADMIN).
- For gen_new_htid: create 2047 u32 proto entries on the same block to fill the tp_c handle pool, then create one more. The root table gets handle 0 and aliases with other handle-0 root tables.
--- net/sched/cls_u32.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-)
net/sched/cls_u32.c +26 -6
@@ -370,6 +370,10 @@ static int u32_init(struct tcf_proto *tp)refcount_set (&root_ht->refcnt,1 ); root_ht->handle = tp_c ?gen_new_htid (tp_c, root_ht) :id2handle (0 ); +if (root_ht->handle ==0 ) { +kfree (root_ht); +return -ENOMEM ; + } root_ht->prio = tp->prio; root_ht->is_root = true;idr_init (&root_ht->handle_idr);
@@ -695,21 +699,33 @@ static int u32_delete(struct tcf_proto *tp, void *arg, bool *last,return ret; } -static u32 gen_new_kid (struct tc_u_hnode *ht,u32 htid) +static u32 gen_new_kid (struct tc_u_hnode *ht,u32 htid,int *err) {u32 index = htid |0x800 ;u32 max = htid |0xFFF ; + *err =0 ; +if (idr_alloc_u32 (&ht->handle_idr,NULL , &index, max,GFP_KERNEL )) { index = htid +1 ; -if (idr_alloc_u32 (&ht->handle_idr,NULL , &index, max, -GFP_KERNEL )) - index = max; + *err =idr_alloc_u32 (&ht->handle_idr,NULL , &index, max, +GFP_KERNEL ); +if (*err) +return 0 ; }return index; } +static int u32_kid_extack (int err,struct netlink_ext_ack *extack) +{ +if (err == -ENOSPC ) +NL_SET_ERR_MSG_MOD (extack,"Hash table node ID pool exhausted" ); +else +NL_SET_ERR_MSG_MOD (extack,"Failed to allocate node ID" ); +return err; +} +static const struct nla_policy u32_policy[TCA_U32_MAX +1 ] = { [TCA_U32_CLASSID ] = { .type =NLA_U32 }, [TCA_U32_HASH ] = { .type =NLA_U32 },
@@ -1076,7 +1092,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,* handle which is used to uniquely identify the match entry. */ if (!TC_U32_NODE (handle)) { - handle =gen_new_kid (ht, htid); + handle =gen_new_kid (ht, htid, &err); +if (err) +return u32_kid_extack (err, extack); }else { handle = htid |TC_U32_NODE (handle); err = idr_alloc_u32(&ht->handle_idr, NULL, &handle,
@@ -1088,7 +1106,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,/* The user did not give us a handle; lets just generate one * from the table's pool of nodeids. */ - handle =gen_new_kid (ht, htid); + handle =gen_new_kid (ht, htid, &err); +if (err) +return u32_kid_extack (err, extack); }if (tb[TCA_U32_SEL ] ==NULL ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 681/1250 igmp: convert struct ip_sf_list to RCU
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 2987ee196c88dbde0463dc87d5fb209c684e34a2 ]
Commit 23d2b94043ca ("igmp: Add ip_mc_list lock in ip_check_mc_rcu") added spin_lock_bh(&im->lock) to ip_check_mc_rcu() to prevent a use-after-free while iterating im->sources during concurrent deletions.
However, ip_check_mc_rcu() is called from RCU read-side critical sections in packet receive and route lookup fast paths (e.g. __mkroute_output(), ip_route_input_rcu(), and __udp4_lib_rcv()).
When igmpv3_send_cr() or igmpv3_send_report() holds &pmc->lock and calls add_grec() -> igmpv3_newpack() -> ip_route_output_ports(), an XFRM policy matching a multicast destination triggers xfrm_tmpl_resolve_one() -> xfrm4_get_saddr() -> __mkroute_output() -> ip_check_mc_rcu(). This attempts to acquire &im->lock while &pmc->lock is already held on the same CPU, triggering a lockdep recursive locking warning / deadlock.
Fix this by converting IPv4 struct ip_sf_list to RCU, mirroring the IPv6 implementation in net/ipv6/mcast.c:
1. Add struct rcu_head to struct ip_sf_list and annotate sf_next, sources, and tomb as __rcu pointers.
2. Use rcu_assign_pointer() and kfree_rcu() for list updates and deletions.
3. Remove spin_lock_bh(&im->lock) from ip_check_mc_rcu() and traverse im->sources locklessly with for_each_psf_rcu(), reading and writing counter fields with READ_ONCE() and WRITE_ONCE().
Note: RCU conversion of /proc/net/mcfilter will be done in a separate patch.
--- include/linux/igmp.h | 7 +- net/ipv4/igmp.c | 210 +++++++++++++++++++++++++++---------------- 2 files changed, 135 insertions(+), 82 deletions(-)
include/linux/igmp.h +4 -3
@@ -57,20 +57,21 @@ struct ip_mc_socklist { };struct ip_sf_list { -struct ip_sf_list *sf_next; +struct ip_sf_list __rcu *sf_next;unsigned long sf_count[2 ];/* include/exclude counts */ __be32 sf_inaddr;unsigned char sf_gsresp;/* include in g & s response? */ unsigned char sf_oldin;/* change state */ unsigned char sf_crcount;/* retrans. left to send */ +struct rcu_head rcu; };struct ip_mc_list {struct in_device *interface;__be32 multiaddr;unsigned int sfmode; -struct ip_sf_list *sources; -struct ip_sf_list *tomb; +struct ip_sf_list __rcu *sources; +struct ip_sf_list __rcu *tomb;unsigned long sfcount[2 ];union {struct ip_mc_list *next;
net/ipv4/igmp.c +131 -79
466 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 682/1250 ppp: ppp_async: simplify tty disc_data access
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qingfang Deng <[email protected]>
[ Upstream commit 9feb069e5ed03582fbf6272539f1caa2a17dc6d5 ]
tty_ldisc_hangup() invokes the hangup callback while holding only a read lock on tty->ldisc_sem, so it can run concurrently with other line discipline callbacks. This currently forces async PPP to maintain separate lifetime protection around tty->disc_data.
Line discipline close is called under the write lock during hangup processing. Remove the hangup callback and rely on close for teardown, as done for SLIP by commit 23c53269f2ba ("slip: remove slip_hangup() to fix use-after-free in slip_receive_buf()"). This serializes teardown with all other line discipline operations.
disc_data_lock, refcount and completion are redundant with that serialization. Remove them and access tty->disc_data directly.
This also eliminates a lockdep warning reported by syzbot. The warning does not indicate a real deadlock because the write side runs only in process context with hardirqs disabled.
--- drivers/net/ppp/ppp_async.c | 82 ++++--------------------------------- 1 file changed, 7 insertions(+), 75 deletions(-)
drivers/net/ppp/ppp_async.c +7 -75
@@ -65,8 +65,6 @@ struct asyncppp {struct tasklet_struct tsk; -refcount_t refcnt; -struct completion dead;struct ppp_channel chan;/* interface to generic ppp layer */ unsigned char obuf[OBUFSIZE ]; };
@@ -116,38 +114,6 @@ static const struct ppp_channel_ops async_ops = {* Routines implementing the PPP line discipline. */ -/* -* We have a potential race on dereferencing tty->disc_data, -* because the tty layer provides no locking at all - thus one -* cpu could be running ppp_asynctty_receive while another -* calls ppp_asynctty_close, which zeroes tty->disc_data and -* frees the memory that ppp_asynctty_receive is using. The best -* way to fix this is to use a rwlock in the tty struct, but for now -* we use a single global rwlock for all ttys in ppp line discipline. -* -* FIXME: this is no longer true. The _close path for the ldisc is -* now guaranteed to be sane. -*/ -static DEFINE_RWLOCK (disc_data_lock); - -static struct asyncppp *ap_get (struct tty_struct *tty) -{ -struct asyncppp *ap; - -read_lock (&disc_data_lock); - ap = tty->disc_data; -if (ap !=NULL ) -refcount_inc (&ap->refcnt); -read_unlock (&disc_data_lock); -return ap; -} - -static void ap_put (struct asyncppp *ap) -{ -if (refcount_dec_and_test (&ap->refcnt)) -complete (&ap->dead); -} -/* * Called when a tty is put into PPP line discipline. Called in process * context.
@@ -182,9 +148,6 @@ ppp_asynctty_open(struct tty_struct *tty)skb_queue_head_init (&ap->rqueue);tasklet_setup (&ap->tsk, ppp_async_process); -refcount_set (&ap->refcnt,1 ); -init_completion (&ap->dead); - ap->chan.private = ap; ap->chan.ops = &async_ops; ap->chan.mtu =PPP_MRU ;
@@ -205,34 +168,18 @@ ppp_asynctty_open(struct tty_struct *tty) }/* -* Called when the tty is put into another line discipline -* or it hangs up. We have to wait for any cpu currently -* executing in any of the other ppp_asynctty_* routines to -* finish before we can call ppp_unregister_channel and free -* the asyncppp struct. This routine must be called from -* process context, not interrupt or softirq context. +* Called when the tty is put into another line discipline or it hangs up. +* This call is serialized against other ldisc functions. */ static void ppp_asynctty_close (struct tty_struct *tty) { -struct asyncppp *ap; +struct asyncppp *ap = tty->disc_data; -write_lock_irq (&disc_data_lock); - ap = tty->disc_data; - tty->disc_data =NULL ; -write_unlock_irq (&disc_data_lock);if (!ap)return ; -/* -* We have now ensured that nobody can start using ap from now -* on, but we have to wait for all existing users to finish. -* Note that ppp_unregister_channel ensures that no calls to -* our channel ops (i.e. ppp_async_send/ioctl) are in progress -* by the time it returns. -*/ -if (!refcount_dec_and_test (&ap->refcnt)) -wait_for_completion (&ap->dead); + tty->disc_data =NULL ;tasklet_kill (&ap->tsk);ppp_unregister_channel (&ap->chan);
@@ -242,17 +189,6 @@ ppp_asynctty_close(struct tty_struct *tty)kfree (ap); } -/* -* Called on tty hangup in process context. -* -* Wait for I/O to driver to complete and unregister PPP channel. -* This is already done by the close routine, so just call that. -*/ -static void ppp_asynctty_hangup (struct tty_struct *tty) -{ -ppp_asynctty_close (tty); -} -/* * Read does nothing - no data is ever available this way. * Pppd reads and writes packets via /dev/ppp instead.
@@ -283,7 +219,7 @@ ppp_asynctty_write(struct tty_struct *tty, struct file *file, const u8 *buf,static int ppp_asynctty_ioctl (struct tty_struct *tty,unsigned int cmd,unsigned long arg) { -struct asyncppp *ap =ap_get (tty); +struct asyncppp *ap = tty->disc_data;int err, val;int __user *p = (int __user *)arg;
@@ -324,7 +260,6 @@ ppp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) err =tty_mode_ioctl (tty, cmd, arg); } -ap_put (ap);return err; }
@@ -333,7 +268,7 @@ static void ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags, size_t count) { - struct asyncppp *ap = ap_get(tty); + struct asyncppp *ap = tty->disc_data;unsigned long flags;if (!ap)
@@ -343,21 +278,19 @@ ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,spin_unlock_irqrestore (&ap->recv_lock, flags);if (!skb_queue_empty (&ap->rqueue))tasklet_schedule (&ap->tsk); -ap_put (ap);tty_unthrottle (tty); }static void ppp_asynctty_wakeup (struct tty_struct *tty) { -struct asyncppp *ap =ap_get (tty); +struct asyncppp *ap = tty->disc_data;clear_bit (TTY_DO_WRITE_WAKEUP , &tty->flags);if (!ap)return ;set_bit (XMIT_WAKEUP , &ap->xmit_flags);tasklet_schedule (&ap->tsk); -ap_put (ap); }
@@ -367,7 +300,6 @@ static struct tty_ldisc_ops ppp_ldisc = { .name = "ppp", .open = ppp_asynctty_open, .close = ppp_asynctty_close, - .hangup = ppp_asynctty_hangup, .read = ppp_asynctty_read, .write = ppp_asynctty_write, .ioctl = ppp_asynctty_ioctl,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 683/1250 ppp: ppp_synctty: simplify tty disc_data access
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qingfang Deng <[email protected]>
[ Upstream commit d8d4d1cf40d541a5d7cc3b15d57e42d0815c7d53 ]
Apply the same simplification as the preceding ppp_async change.
--- drivers/net/ppp/ppp_synctty.c | 83 +++-------------------------------- 1 file changed, 7 insertions(+), 76 deletions(-)
drivers/net/ppp/ppp_synctty.c +7 -76
@@ -38,11 +38,9 @@#include <linux/ppp-ioctl.h> #include <linux/ppp_channel.h> #include <linux/spinlock.h> -#include <linux/completion.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/slab.h> -#include <linux/refcount.h> #include <linux/unaligned.h> #include <linux/uaccess.h>
@@ -69,8 +67,6 @@ struct syncppp {struct tasklet_struct tsk; -refcount_t refcnt; -struct completion dead_cmp;struct ppp_channel chan;/* interface to generic ppp layer */ };
@@ -118,37 +114,6 @@ ppp_print_buffer (const char *name, const __u8 *buf, int count)* Routines implementing the synchronous PPP line discipline. */ -/* -* We have a potential race on dereferencing tty->disc_data, -* because the tty layer provides no locking at all - thus one -* cpu could be running ppp_synctty_receive while another -* calls ppp_synctty_close, which zeroes tty->disc_data and -* frees the memory that ppp_synctty_receive is using. The best -* way to fix this is to use a rwlock in the tty struct, but for now -* we use a single global rwlock for all ttys in ppp line discipline. -* -* FIXME: Fixed in tty_io nowadays. -*/ -static DEFINE_RWLOCK (disc_data_lock); - -static struct syncppp *sp_get (struct tty_struct *tty) -{ -struct syncppp *ap; - -read_lock (&disc_data_lock); - ap = tty->disc_data; -if (ap !=NULL ) -refcount_inc (&ap->refcnt); -read_unlock (&disc_data_lock); -return ap; -} - -static void sp_put (struct syncppp *ap) -{ -if (refcount_dec_and_test (&ap->refcnt)) -complete (&ap->dead_cmp); -} -/* * Called when a tty is put into sync-PPP line discipline. */
@@ -179,9 +144,6 @@ ppp_sync_open(struct tty_struct *tty)skb_queue_head_init (&ap->rqueue);tasklet_setup (&ap->tsk, ppp_sync_process); -refcount_set (&ap->refcnt,1 ); -init_completion (&ap->dead_cmp); - ap->chan.private = ap; ap->chan.ops = &sync_ops; ap->chan.mtu =PPP_MRU ;
@@ -203,34 +165,18 @@ ppp_sync_open(struct tty_struct *tty) }/* -* Called when the tty is put into another line discipline -* or it hangs up. We have to wait for any cpu currently -* executing in any of the other ppp_synctty_* routines to -* finish before we can call ppp_unregister_channel and free -* the syncppp struct. This routine must be called from -* process context, not interrupt or softirq context. +* Called when the tty is put into another line discipline or it hangs up. +* This call is serialized against other ldisc functions. */ static void ppp_sync_close (struct tty_struct *tty) { -struct syncppp *ap; +struct syncppp *ap = tty->disc_data; -write_lock_irq (&disc_data_lock); - ap = tty->disc_data; - tty->disc_data =NULL ; -write_unlock_irq (&disc_data_lock);if (!ap)return ; -/* -* We have now ensured that nobody can start using ap from now -* on, but we have to wait for all existing users to finish. -* Note that ppp_unregister_channel ensures that no calls to -* our channel ops (i.e. ppp_sync_send/ioctl) are in progress -* by the time it returns. -*/ -if (!refcount_dec_and_test (&ap->refcnt)) -wait_for_completion (&ap->dead_cmp); + tty->disc_data =NULL ;tasklet_kill (&ap->tsk);ppp_unregister_channel (&ap->chan);
@@ -239,17 +185,6 @@ ppp_sync_close(struct tty_struct *tty)kfree (ap); } -/* -* Called on tty hangup in process context. -* -* Wait for I/O to driver to complete and unregister PPP channel. -* This is already done by the close routine, so just call that. -*/ -static void ppp_sync_hangup (struct tty_struct *tty) -{ -ppp_sync_close (tty); -} -/* * Read does nothing - no data is ever available this way. * Pppd reads and writes packets via /dev/ppp instead.
@@ -275,7 +210,7 @@ ppp_sync_write(struct tty_struct *tty, struct file *file, const u8 *buf,static int ppp_synctty_ioctl (struct tty_struct *tty,unsigned int cmd,unsigned long arg) { -struct syncppp *ap =sp_get (tty); +struct syncppp *ap = tty->disc_data;int __user *p = (int __user *)arg;int err, val;
@@ -316,7 +251,6 @@ ppp_synctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)break ; } -sp_put (ap);return err; }
@@ -325,7 +259,7 @@ static void ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags, size_t count) { - struct syncppp *ap = sp_get(tty); + struct syncppp *ap = tty->disc_data;unsigned long flags;if (!ap)
@@ -335,21 +269,19 @@ ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,spin_unlock_irqrestore (&ap->recv_lock, flags);if (!skb_queue_empty (&ap->rqueue))tasklet_schedule (&ap->tsk); -sp_put (ap);tty_unthrottle (tty); }static void ppp_sync_wakeup (struct tty_struct *tty) { -struct syncppp *ap =sp_get (tty); +struct syncppp *ap = tty->disc_data;clear_bit (TTY_DO_WRITE_WAKEUP , &tty->flags);if (!ap)return ;set_bit (XMIT_WAKEUP , &ap->xmit_flags);tasklet_schedule (&ap->tsk); -sp_put (ap); }
@@ -359,7 +291,6 @@ static struct tty_ldisc_ops ppp_sync_ldisc = { .name = "pppsync", .open = ppp_sync_open, .close = ppp_sync_close, - .hangup = ppp_sync_hangup, .read = ppp_sync_read, .write = ppp_sync_write, .ioctl = ppp_synctty_ioctl,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 684/1250 ipv6: mcast: fix RCU list diversion in ip6_mc_del1_src()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 93b49239840b91313adbd77b8b52993eff2d08c1 ]
When removing a source filter whose count reaches zero, ip6_mc_del1_src() unlinks psf from pmc->mca_sources. If the filter was previously active, the code moved psf directly into pmc->mca_tomb by updating psf->sf_next.
Because pmc->mca_sources is traversed locklessly under RCU (e.g. by ipv6_chk_mcast_addr()), mutating psf->sf_next before a grace period elapses diverts concurrent readers to the tombstone list. Consequently, readers miss remaining active sources in pmc->mca_sources and improperly examine deleted tombstone entries.
Fix this by allocating a new tombstone node for pmc->mca_tomb (as done in sf_setstate()) and retiring the original psf via kfree_rcu().
--- net/ipv6/mcast.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
net/ipv6/mcast.c +11 -7
@@ -2350,14 +2350,18 @@ static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,if (psf->sf_oldin && !(pmc->mca_flags &MAF_NOREPORT ) && !mld_in_v1_mode (idev)) { - psf->sf_crcount = idev->mc_qrv; -rcu_assign_pointer (psf->sf_next, -mc_dereference (pmc->mca_tomb, idev)); -rcu_assign_pointer (pmc->mca_tomb, psf); - rv =1 ; - }else { -kfree_rcu (psf, rcu); +struct ip6_sf_list *dpsf =kmalloc_obj (*dpsf); + +if (dpsf) { + *dpsf = *psf; + dpsf->sf_crcount = idev->mc_qrv; +rcu_assign_pointer (dpsf->sf_next, +mc_dereference (pmc->mca_tomb, idev)); +rcu_assign_pointer (pmc->mca_tomb, dpsf); + rv =1 ; + } } +kfree_rcu (psf, rcu); }return rv; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 685/1250 ipv6: mcast: use copy-on-write RCU updates in ip6_mc_source()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit c073d1b070f171d206b19c98d71739a97f15b3f1 ]
pmc->sflist is read locklessly under rcu_read_lock() by inet6_mc_check() during packet reception in the UDP and RAW multicast receive paths.
ip6_mc_source() mutated psl->sl_addr and psl->sl_count in-place when adding or removing a source filter. Additionally, when expanding the filter buffer, newpsl was published via rcu_assign_pointer() before writing the new source into the array.
Because 16-byte struct in6_addr writes are not atomic and array shifting is not synchronized with RCU readers, concurrent readers in inet6_mc_check() could read torn IPv6 addresses or observe duplicated/missed source entries.
Fix this by switching ip6_mc_source() to copy-on-write RCU updates:
allocate and fully populate newpsl before publishing it via rcu_assign_pointer(), and reclaim the old filter via kfree_rcu(), matching ip6_mc_msfilter().
Also remove the now unused IP6_SFBLOCK macro.
--- include/net/if_inet6.h | 2 - net/ipv6/mcast.c | 98 ++++++++++++++++++++++++------------------ 2 files changed, 56 insertions(+), 44 deletions(-)
include/net/if_inet6.h +0 -2
@@ -88,8 +88,6 @@ struct ip6_sf_socklist { struct in6_addr sl_addr[] __counted_by(sl_max); }; -#define IP6_SFBLOCK 10/* allocate this many at once */ -struct ipv6_mc_socklist {struct in6_addr addr;int ifindex;
net/ipv6/mcast.c +56 -42
139 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 686/1250 ip6_gre: check tunnel info before xmit in ip6gre_tunnel_xmit
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 97cc84dad1d7f68a36b71b69b361d88482707673 ]
Shuangpeng Bai reported a KASAN slab-use-after-free in ip6gre_tunnel_xmit().
The precise KASAN bug was caused by ip6_tnl_xmit() consuming the skb during headroom expansion and returning an error, while ip6gre_tunnel_xmit() still held the stale pointer and called skb_tunnel_info_txcheck(skb) at tx_err. That specific bug was fixed by commit 87f21b59ddc6 ("ip6_tunnel: use skb_cow_head() in ip6_tnl_xmit()").
However, calling skb_tunnel_info_txcheck(skb) at the tx_err label after the transmission attempt remains problematic:
Downstream helpers like ip6_tnl_xmit() call skb_scrub_packet(), which drops the skb's metadata_dst before transmission. If an error occurs later during transmit, inspecting skb at tx_err sees a scrubbed dst and misclassifies tx_errors vs tx_dropped.
Commit e5f7e211b6aa ("ip6gre: avoid tx_error when sending MLD/DAD on external tunnels") already handled this correctly in ip6erspan_tunnel_xmit() by checking and caching tun_info before transmit.
Align ip6gre_tunnel_xmit() with ip6erspan_tunnel_xmit() by caching tun_info before xmit and checking it at tx_err.
--- net/ipv6/ip6_gre.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
net/ipv6/ip6_gre.c +5 -1
@@ -877,6 +877,7 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)static netdev_tx_t ip6gre_tunnel_xmit (struct sk_buff *skb,struct net_device *dev) { +struct ip_tunnel_info *tun_info =NULL ;struct ip6_tnl *t =netdev_priv (dev);__be16 payload_protocol;int ret;
@@ -887,6 +888,9 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,if (!ip6_tnl_xmit_ctl (t, &t->parms.laddr, &t->parms.raddr)) goto tx_err; +if (t->parms.collect_md) + tun_info =skb_tunnel_info_txcheck (skb); + payload_protocol =skb_protocol (skb, true);switch (payload_protocol) {case htons (ETH_P_IP ):
@@ -906,7 +910,7 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,return NETDEV_TX_OK ; tx_err: -if (!t->parms.collect_md || !IS_ERR (skb_tunnel_info_txcheck (skb))) +if (!IS_ERR (tun_info))DEV_STATS_INC (dev, tx_errors);DEV_STATS_INC (dev, tx_dropped);kfree_skb (skb);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 687/1250 tipc: fix NULL deref in tipc_named_node_up() on empty publication list
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tung Nguyen <[email protected]>
[ Upstream commit b3b76e9f4f2476f1135b2ba7743a821db4a0df4b ]
User-space applications can bind a large number of service addresses to one or more sockets. Each binding of a local-scope service address inserts one entry (publication) into the TIPC name table. If the number of these publications exceeds TIPC_MAX_PUBL (65535), protocol service types (such as node state and link state) are no longer inserted into the name table. This causes two issues:
1. User-space applications subscribing to node or link up/down events stop receiving notifications.
2. A NULL pointer dereference can occur:
BUG: kernel NULL pointer dereference, address: 00000000000000d0 ... CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 7.2.0-rc4-default+ #5 PREEMPT(full) ... RIP: 0010:tipc_named_node_up (./include/linux/skbuff.h:2251 net/tipc/name_distr.c:195 net/tipc/name_distr.c:221) ... Call Trace: <IRQ> tipc_node_write_unlock (net/tipc/node.c:428) tipc_rcv (net/tipc/node.c:934 net/tipc/node.c:2189) tipc_udp_recv (net/tipc/udp_media.c:389)
Thread 1 (tipc_net_finalize) | Thread 2 (named_distribute)
-----------------------------|-----------------------------
| ...
| list_for_each_entry(publ, pls, binding_node) {
| ...
| __skb_queue_tail(list, skb);
| ...
| }
| ...
| hdr = buf_msg(skb_peek_tail(list));
... |
tipc_nametbl_publish(); | If 'tipc_nametbl_publish()' (Thread 1) fails because the number of local publications reaches TIPC_MAX_PUBL, list (Thread 2) will be empty. As a result, NULL is passed to 'buf_msg()', leading to a NULL pointer dereference.
Fix these issues by allowing protocol service types (node state, link state, and topology server) to be inserted into the name table unconditionally. This ensures that users subscribing to these types always receive notifications. In addition, the maximum number of local user publications is reduced to (TIPC_MAX_PUBL - 1). This ensures that the maximum bulk size calculated in tipc_link_set_queue_limits() remains valid.
--- net/tipc/name_table.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-)
net/tipc/name_table.c +26 -4
@@ -763,21 +763,40 @@ struct publication *tipc_nametbl_publish(struct net *net, struct tipc_uaddr *ua, struct tipc_socket_addr *sk, u32 key) {struct name_table *nt =tipc_name_table (net); +u32 max_user_pub =TIPC_MAX_PUBL -1 ;struct tipc_net *tn =tipc_net (net);struct publication *p =NULL ;struct sk_buff *skb =NULL ; +bool protocol_type = false;u32 rc_dests; +if (ua->sr.type ==TIPC_NODE_STATE || ua->sr.type ==TIPC_LINK_STATE || + ua->sr.type ==TIPC_TOP_SRV ) + protocol_type = true; +spin_lock_bh (&tn->nametbl_lock); +if (protocol_type) + goto insert; -if (nt->local_publ_count >=TIPC_MAX_PUBL ) { -pr_warn ("Bind failed, max limit %u reached\n" ,TIPC_MAX_PUBL ); +/* Reserve one entry for node state service type because it has cluster +* scope and it is distributed in bulk. So, the maximum number of user's +* publications is (TIPC_MAX_PUBL - 1). +*/ +if (nt->local_publ_count >= max_user_pub) { +pr_warn ("Bind failed, max limit %u reached\n" , max_user_pub); goto exit; } +insert: p =tipc_nametbl_insert_publ (net, ua, sk, key);if (p) { - nt->local_publ_count++; +/* Not count node state, link state and topology server types +* so that maximum nt->local_publ_count does not prevent +* protocol service types from being inserted into the name +* table. +*/ +if (!protocol_type) + nt->local_publ_count++; skb =tipc_named_publish (net, p); } rc_dests = nt->rc_dests;
@@ -810,7 +829,10 @@ void tipc_nametbl_withdraw(struct net *net, struct tipc_uaddr *ua, p =tipc_nametbl_remove_publ (net, ua, sk, key);if (p) { - nt->local_publ_count--; +if (p->sr.type !=TIPC_NODE_STATE && + p->sr.type !=TIPC_LINK_STATE && + p->sr.type !=TIPC_TOP_SRV ) + nt->local_publ_count--; skb =tipc_named_withdraw (net, p);list_del_init (&p->binding_sock);kfree_rcu (p, rcu);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 688/1250 tipc: Dont send random pad bytes in RESET/ACTIVATE messages
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Laight <[email protected]>
[ Upstream commit 81c600c26302a27852ed8b19c5f2f647ea3555c9 ]
The interface name is passed in a fixed length (TIPC_MAX_IF_NAME) buffer. Replace the strcpy(data, l->if_name) with memcpy() so that the pad bytes are actually written (l->if_name[] is zero padded) rather than sending random bytes from the skb to the remote system.
Replace two other strcpy() with strscpy().
--- net/tipc/link.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
net/tipc/link.c +3 -3
@@ -504,7 +504,7 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id,snprintf (l->name,sizeof (l->name),"%s:%s-%s:unknown" , self_str, if_name, peer_str); -strcpy (l->if_name, if_name); +strscpy (l->if_name, if_name); l->addr = peer; l->peer_caps = peer_caps; l->net = net;
@@ -574,7 +574,7 @@ bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, u8 *peer_id,snprintf (l->name,sizeof (l->name),"%s:%s" , tipc_bclink_name, peer_str); }else { -strcpy (l->name, tipc_bclink_name); +strscpy (l->name, tipc_bclink_name); }trace_tipc_link_reset (l,TIPC_DUMP_ALL ,"bclink created!" );tipc_link_reset (l);
@@ -1898,7 +1898,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,msg_set_dest_session (hdr, l->peer_session); }msg_set_max_pkt (hdr, l->advertised_mtu); -strcpy (data, l->if_name); +memcpy (data, l->if_name,TIPC_MAX_IF_NAME );msg_set_size (hdr,INT_H_SIZE +TIPC_MAX_IF_NAME );skb_trim (skb,INT_H_SIZE +TIPC_MAX_IF_NAME ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 689/1250 ipv6: sr: restore network header before routing and forwarding
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 975b5b067f525a1b1338c4a3bee1c46545801518 ]
ipv6_srh_rcv() runs with skb->data at the Segment Routing Header (SRH) while skb_network_header() points at the IPv6 header.
When segments_left > 0, ipv6_srh_rcv() previously restored the skb->data position by pushing sizeof(struct ipv6hdr), assuming the SRH immediately followed the fixed IPv6 header. If another extension header (such as a Hop-by-Hop options header) precedes the SRH, skb_network_offset() remained negative.
This led to two problems:
1. During ip6_route_input(), fib6_rules_early_flow_dissect() invokes __skb_flow_dissect() which passes the negative skb_network_offset() to flow dissection, breaking BPF and C flow dissector logic.
2. If forwarded via ip6_forward() or redirected via act_mirred, downstream handlers (like sch_fragment() or neighbour output) pass the negative offset as an unsigned length, triggering OOB memcpy or buffer overflows.
Fix this by pushing -skb_network_offset(skb) before routing, ensuring skb_network_offset(skb) is 0 for route lookup / flow dissection as well as downstream forwarding. On the loopback path, pull skb_transport_offset(skb) to restore skb->data to the SRH before looping back.
--- net/ipv6/exthdrs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/ipv6/exthdrs.c +2 -2
@@ -448,7 +448,7 @@ static int ipv6_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev) hdr->segments_left--; addr = hdr->segments + hdr->segments_left; -skb_push (skb,sizeof (struct ipv6hdr )); +skb_push (skb, -skb_network_offset (skb));if (skb->ip_summed ==CHECKSUM_COMPLETE )seg6_update_csum (skb);
@@ -472,7 +472,7 @@ static int ipv6_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev) }ipv6_hdr (skb)->hop_limit--; -skb_pull (skb,sizeof (struct ipv6hdr )); +skb_pull (skb,skb_transport_offset (skb)); goto looped_back; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 690/1250 af_packet: Dont cast tpacket_hdr.tp_len to int in tpacket_parse_header().
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 73e594c19b4f815d8343461cec7074c4713bbde7 ]
syzbot reported BUG() in sock_sendmsg_nosec(). [0]
The problem is that tpacket_parse_header() casts user-provided tpacket_hdr.tp_len, which is u32, to int.
If the length is larger than INT_MAX, the following condition in tpacket_parse_header() passes,
if (unlikely(tp_len > size_max))
and any negative value can be returned to the caller, up to sock_sendmsg_nosec().
The repro set tpacket_hdr.tp_len to 0xfffffdef, which is cast to -EIOCBQUEUED (-529), triggering BUG() in sock_sendmsg_nosec().
*(uint64_t*)0x200000000008 = 0xfffffdef; ... syscall(__NR_write, /*fd=*/r[0], /*buf=*/0x200000000000ul, /*count=*/1ul);
Let's define the local tp_len as u32 in tpacket_parse_header().
[0]:
kernel BUG at net/socket.c:803!
Oops: invalid opcode: 0000 [#1] SMP KASAN PTI CPU: 0 UID: 0 PID: 5628 Comm: syz-executor176 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/24/2026 RIP: 0010:sock_sendmsg_nosec+0x145/0x180 net/socket.c:803 Code: 06 67 48 0f b9 3a eb 95 e8 e8 3a 22 f8 48 89 df 4c 89 f6 4c 89 e2 4d 89 fb 2e e8 32 a5 5c 16 e9 51 ff ff ff e8 cc 3a 22 f8 90 <0f> 0b e8 c4 3a 22 f8 48 83 c3 18 48 89 d8 48 c1 e8 03 42 80 3c 28 RSP: 0018:ffffc90003aefb48 EFLAGS: 00010293 RAX: ffffffff89a578d4 RBX: ffff8880764c67c0 RCX: ffff88807fb23e80 RDX: 0000000000000000 RSI: 00000000fffffdef RDI: 00000000fffffdef RBP: 00000000fffffdef R08: ffffc90003aef747 R09: 1ffff9200075dee8 R10: dffffc0000000000 R11: fffff5200075dee9 R12: 0000000000000001 R13: dffffc0000000000 R14: ffffc90003aefbc0 R15: ffffffff8aac4310 FS: 000055559101b400(0000) GS:ffff888124ce0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000200000000210 CR3: 0000000073dca000 CR4: 00000000003526f0 Call Trace: <TASK> __sock_sendmsg net/socket.c:815 [inline] sock_write_iter+0x2de/0x3e0 net/socket.c:1266 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x612/0xba0 fs/read_write.c:687 ksys_write+0x150/0x270 fs/read_write.c:739 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline] do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f173130ecb9 Code: c0 79 93 eb d5 48 8d 7c 1d 00 eb 99 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 d8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007ffd67e44248 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 0000200000000000 RCX: 00007f173130ecb9 RDX: 0000000000000001 RSI: 0000200000000000 RDI: 0000000000000003 RBP: 0000000000000001 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 00007ffd67e44388 R13: 0000000000000002 R14: 00002000000000c0 R15: 0000000000000002 </TASK>
--- net/packet/af_packet.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
net/packet/af_packet.c +3 -2
@@ -2674,7 +2674,8 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame, int size_max, void **data) { union tpacket_uhdr ph; -int tp_len, off; +u32 tp_len; +int off; ph.raw = frame;
@@ -2694,7 +2695,7 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame,break ; }if (unlikely (tp_len > size_max)) { -pr_err ("packet size is too long (%d > %d)\n" , tp_len, size_max); +pr_err ("packet size is too long (%u > %d)\n" , tp_len, size_max);return -EMSGSIZE ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 691/1250 staging: fbtft: make dirty_lock IRQ-safe
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Su <[email protected]>
[ Upstream commit f576944a59f31bcffff121117ebf452c5dd162b7 ]
fbtft_mkdirty() can be reached from the fbcon rendering path while processing printk() in hardirq context. Meanwhile, dirty_lock is also taken by fbtft_deferred_io() in workqueue context with local interrupts enabled.
Lockdep reports a possible IRQ lock inversion involving dirty_lock and console_owner. A hardirq can interrupt a CPU holding dirty_lock and enter the console rendering path, which can attempt to acquire dirty_lock again.
The following lockdep report was observed on an RK3566 system with CONFIG_PROVE_LOCKING enabled:
WARNING: possible irq lock inversion dependency detected swapper/2/0 just changed the state of lock:
(console_owner){-...}-{0:0}
but this lock took another, HARDIRQ-unsafe lock in the past:
(&par->dirty_lock){+.+.}-{2:2}
CPU0 CPU1
---- ----
lock(&par->dirty_lock);
local_irq_disable();
lock(console_owner);
lock(&par->dirty_lock);
<Interrupt>
lock(console_owner);*** DEADLOCK ***
Use spin_lock_irqsave() for fbtft_mkdirty() and spin_lock_irq() for fbtft_deferred_io(). They only access the dirty line range, so the IRQ-off regions remain short.
--- drivers/staging/fbtft/fbtft-core.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/staging/fbtft/fbtft-core.c +5 -4
@@ -299,6 +299,7 @@ static void fbtft_mkdirty(struct fb_info *info, int y, int height) {struct fbtft_par *par = info->par;struct fb_deferred_io *fbdefio = info->fbdefio; +unsigned long flags;/* special case, needed ? */ if (y ==-1 ) {
@@ -307,12 +308,12 @@ static void fbtft_mkdirty(struct fb_info *info, int y, int height) }/* Mark display lines/area as dirty */ -spin_lock (&par->dirty_lock); +spin_lock_irqsave (&par->dirty_lock, flags);if (y < par->dirty_lines_start) par->dirty_lines_start = y;if (y + height -1 > par->dirty_lines_end) par->dirty_lines_end = y + height -1 ; -spin_unlock (&par->dirty_lock); +spin_unlock_irqrestore (&par->dirty_lock, flags);/* Schedule deferred_io to update display (no-op if already on queue)*/ schedule_delayed_work (&info->deferred_work, fbdefio->delay);
@@ -325,13 +326,13 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagereflisstruct fb_deferred_io_pageref *pageref;unsigned int y_low =0 , y_high =0 ; -spin_lock (&par->dirty_lock); +spin_lock_irq (&par->dirty_lock); dirty_lines_start = par->dirty_lines_start; dirty_lines_end = par->dirty_lines_end;/* set display line markers as clean */ par->dirty_lines_start = par->info->var.yres -1 ; par->dirty_lines_end =0 ; -spin_unlock (&par->dirty_lock); +spin_unlock_irq (&par->dirty_lock);/* Mark display lines as dirty */ list_for_each_entry (pageref, pagereflist, list) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 692/1250 ALSA: hda: restore MFG widget enumeration after core split
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
[ Upstream commit 32d7226e6105c257ef7b3d0ec819f11a81f53b6d ]
Before commit 7639a06c23c7 ("ALSA: hda - Move a part of hda_codec stuff into hdac_device"), widget enumeration selected the function group with
codec->afg ? codec->afg : codec->mfg
and read subordinate nodes from that group.
The core split moved this logic into snd_hdac_refresh_widgets(), but hard-coded codec->afg there. For an MFG-only codec, codec->afg is zero, so the Root Node is queried and codec->start_nid/num_nodes are populated from the function-group range instead of the MFG's subordinate nodes.
Restore the pre-split AFG-or-MFG selection.
--- sound/hda/core/device.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
sound/hda/core/device.c +3 -2
@@ -404,6 +404,7 @@ static void setup_fg_nodes(struct hdac_device *codec)*/ int snd_hdac_refresh_widgets (struct hdac_device *codec) { +hda_nid_t fg = codec->afg ? codec->afg : codec->mfg;hda_nid_t start_nid;int nums, err =0 ;
@@ -412,10 +413,10 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec)* widgets array. */ guard (mutex)(&codec->widget_lock); - nums =snd_hdac_get_sub_nodes (codec, codec->afg, &start_nid); + nums =snd_hdac_get_sub_nodes (codec, fg, &start_nid);if (!start_nid || nums <=0 || nums >=0xff ) {dev_err (&codec->dev,"cannot read sub nodes for FG 0x%02x\n" , - codec->afg); + fg);return -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 693/1250 s390/boot: Fix physical memory search range
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasily Gorbik <[email protected]>
[ Upstream commit a0c798ed4103316c23938bdf625af364fbd38016 ]
search_mem_end() calculates the number of 1MB blocks with a signed int literal. CONFIG_MAX_PHYSMEM_BITS values of 51 and above either overflow the signed int or shift beyond its width. This produces an invalid search range when the binary-search memory detection fallback is used.
Use an unsigned long literal so the full supported physical address range is represented.
--- arch/s390/boot/physmem_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/s390/boot/physmem_info.c +1 -1
@@ -141,7 +141,7 @@ static int tprot(unsigned long addr)static unsigned long search_mem_end (void ) { -unsigned long range =1 << (MAX_PHYSMEM_BITS -20 );/* in 1MB blocks */ +unsigned long range =1UL << (MAX_PHYSMEM_BITS -20 );/* in 1MB blocks */ unsigned long offset =0 ;unsigned long pivot;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 694/1250 s390/boot: Avoid IPL parameter append past command line
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasily Gorbik <[email protected]>
[ Upstream commit d76181dfabdaa720703167393704efacba343442 ]
A command line may occupy all but the terminating byte of COMMAND_LINE_SIZE. In that case append_ipl_block_parm() passes a zero size to the IPL parameter conversion helpers and points the destination one byte past early_command_line. The helpers subtract one from the unsigned size and write the converted parameter outside the command line buffer.
Convert the IPL parameter in the command line parsing buffer first. A parameter beginning with '=' can then replace the existing command line regardless of its length, while other parameters are appended only when space remains.
--- arch/s390/boot/ipl_parm.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-)
arch/s390/boot/ipl_parm.c +12 -14
@@ -22,6 +22,7 @@ struct parmarea parmarea __section(".parmarea") = { };char __bootdata (early_command_line )[COMMAND_LINE_SIZE ]; +static char command_line_buf[COMMAND_LINE_SIZE ];unsigned int __bootdata_preserved (zlib_dfltcc_support ) =ZLIB_DFLTCC_FULL ;struct ipl_parameter_block __bootdata_preserved (ipl_block );
@@ -134,31 +135,29 @@ static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,static void append_ipl_block_parm (void ) { -char *parm, *delim; -size_t len, rc =0 ; +size_t len, extra =0 ; +char *delim; len =strlen (early_command_line); - - delim = early_command_line + len;/* '\0' character position */ - parm = early_command_line + len +1 ;/* append right after '\0' */ + delim = early_command_line + len;/* '\0' character position */ switch (ipl_block.pb0_hdr.pbt) {case IPL_PBT_CCW : - rc =ipl_block_get_ascii_vmparm ( - parm,COMMAND_LINE_SIZE - len -1 , &ipl_block); + extra =ipl_block_get_ascii_vmparm (command_line_buf,sizeof (command_line_buf), &ipl_block);break ;case IPL_PBT_FCP :case IPL_PBT_NVME :case IPL_PBT_ECKD : - rc =ipl_block_get_ascii_scpdata ( - parm,COMMAND_LINE_SIZE - len -1 , &ipl_block); + extra =ipl_block_get_ascii_scpdata (command_line_buf,sizeof (command_line_buf), &ipl_block);break ; } -if (rc) { -if (*parm =='=' ) -memmove (early_command_line, parm +1 , rc); -else +if (extra) { +if (command_line_buf[0 ] =='=' ) { +memmove (early_command_line, command_line_buf +1 , extra); + }else if (len <COMMAND_LINE_SIZE -2 ) { *delim =' ' ;/* replace '\0' with space */ +sized_strscpy (delim +1 , command_line_buf,COMMAND_LINE_SIZE - len -1 ); + } } }
@@ -244,7 +243,6 @@ static void modify_fac_list(char *str)check_cleared_facilities (); } -static char command_line_buf[COMMAND_LINE_SIZE ];void parse_boot_command_line (void ) {char *param, *val;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 695/1250 ASoC: fsl_micfil: balance mclk enable/disable
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ștefan Ghețu <[email protected]>
[ Upstream commit d3dbccfe6afa7b9a6a7ed65cfaa76a47fa050a56 ]
hw_params() enables mclk unconditionally and hw_free() disables it unconditionally, but the PCM core does not guarantee 1:1 pairing:hw_free() can run without hw_params(), and hw_params() can be called multiple times from the SETUP state. This triggers an "already disabled" WARN() in the first case and leaks an enable reference in the second, leaving the clock ungateable.
Guard both sides with the existing mclk_flag, as fsl_sai.c does with mclk_streams.
--- sound/soc/fsl/fsl_micfil.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-)
sound/soc/fsl/fsl_micfil.c +13 -8
@@ -808,12 +808,17 @@ static int fsl_micfil_reparent_rootclk(struct fsl_micfil *micfil, unsigned int s/* Get root clock */ clk = micfil->mclk; -/* Disable clock first, for it was enabled by pm_runtime */ +/* Reparent root clock to the PLL matching this sample rate */ fsl_asoc_reparent_pll_clocks (dev, clk, micfil->pll8k_clk, micfil->pll11k_clk, ratio); - ret =clk_prepare_enable (clk); -if (ret) -return ret; + +/* Enable only once; hw_params can be called multiple times */ +if (!micfil->mclk_flag) { + ret =clk_prepare_enable (clk); +if (ret) +return ret; + micfil->mclk_flag = true; + }return 0 ; }
@@ -846,8 +851,6 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,if (ret)return ret; - micfil->mclk_flag = true; -/* floor(K * CLKDIV) */ switch (micfil->quality) {case QUALITY_HIGH :
@@ -923,8 +926,10 @@ static int fsl_micfil_hw_free(struct snd_pcm_substream *substream, {struct fsl_micfil *micfil =snd_soc_dai_get_drvdata (dai); -clk_disable_unprepare (micfil->mclk); - micfil->mclk_flag = false; +if (micfil->mclk_flag) { +clk_disable_unprepare (micfil->mclk); + micfil->mclk_flag = false; + }return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 696/1250 ASoC: amd: renoir: fix disable_pdm_interrupts() to clear mask bits
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: wangdicheng <[email protected]>
[ Upstream commit 0c06c4ce0206290c9a934a1e7196aaa86adfe018 ]
disable_pdm_interrupts() uses |= ~PDM_DMA_INTR_MASK which sets all bits except the PDM DMA interrupt bit instead of clearing only the PDM DMA interrupt bit. Use &= ~PDM_DMA_INTR_MASK to clear only the target bit.
--- sound/soc/amd/renoir/acp3x-pdm-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/amd/renoir/acp3x-pdm-dma.c +1 -1
@@ -104,7 +104,7 @@ static void disable_pdm_interrupts(void __iomem *acp_base)u32 ext_int_ctrl; ext_int_ctrl =rn_readl (acp_base +ACP_EXTERNAL_INTR_CNTL ); - ext_int_ctrl |= ~PDM_DMA_INTR_MASK ; + ext_int_ctrl &= ~PDM_DMA_INTR_MASK ;rn_writel (ext_int_ctrl, acp_base +ACP_EXTERNAL_INTR_CNTL ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 697/1250 ASoC: amd: yc: fix memory leak in acp6x_pdm_dma_close()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: wangdicheng <[email protected]>
[ Upstream commit 1b67e0d3b9691d7b6b74e18960ddd2be24f9dc9d ]
acp6x_pdm_dma_close() does not free the runtime->private_data buffer allocated in acp6x_pdm_dma_open(). Add the missing kfree.
--- sound/soc/amd/yc/acp6x-pdm-dma.c | 2 ++ 1 file changed, 2 insertions(+)
sound/soc/amd/yc/acp6x-pdm-dma.c +2 -0
@@ -275,9 +275,11 @@ static int acp6x_pdm_dma_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) {struct pdm_dev_data *adata =dev_get_drvdata (component->dev); +struct snd_pcm_runtime *runtime = substream->runtime;acp6x_disable_pdm_interrupts (adata->acp6x_base); adata->capture_stream =NULL ; +kfree (runtime->private_data);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 698/1250 drm/xe/oa: Remove sysfs entry on idr_alloc failure in xe_oa_add_config_ioctl()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lu Yao <[email protected]>
[ Upstream commit 3663c8d1f31e65771bd73ee3259f35fd397f9933 ]
If idr_alloc() fails after create_dynamic_oa_sysfs_entry() has succeeded, the error path frees the OA config without removing the metrics sysfs group.
Remove the sysfs group before releasing the config, and fix up the misleading error message copied from the sysfs creation failure path.
--- drivers/gpu/drm/xe/xe_oa.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/gpu/drm/xe/xe_oa.c +4 -2
@@ -2418,9 +2418,9 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fi oa_config->id =idr_alloc (&oa->metrics_idr, oa_config,1 ,0 ,GFP_KERNEL );if (oa_config->id <0 ) { -drm_dbg (&oa->xe->drm,"Failed to create sysfs entry for OA config\n" ); +drm_dbg (&oa->xe->drm,"Failed to allocate id for OA config\n" ); err = oa_config->id; - goto sysfs_err; + goto id_alloc_err; } id = oa_config->id;
@@ -2431,6 +2431,8 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fireturn id; +id_alloc_err: +sysfs_remove_group (oa->metrics_kobj, &oa_config->sysfs_metric); sysfs_err:mutex_unlock (&oa->metrics_lock); reg_err:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 699/1250 ALSA: dummy: Report a change when one capture switch channel moves
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
[ Upstream commit 83162eeaf78c71ff6f6fa31dc95e3b6e90ee593f ]
The snd_dummy_capsrc_put() builds its change flag with &&, so it reports a change only when both channels move at once. Writing a single channel stores the new value and returns 0, the control core then sends no SNDRV_CTL_EVENT_MASK_VALUE, and a second reader keeps showing the old setting until it polls again.
The volume put a few lines above compares the same pair of channels with ||.
The mixer selftest already reports this. With snd-dummy loaded it fails event_missing on all five capture switches:
# CD Capture Switch.1 orig 0 read 1, is_volatile 0 not ok 13 event_missing.Dummy.9
--- sound/drivers/dummy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/drivers/dummy.c +1 -1
@@ -782,7 +782,7 @@ static int snd_dummy_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el left = ucontrol->value.integer.value[0 ] &1 ; right = ucontrol->value.integer.value[1 ] &1 ;guard (spinlock_irq)(&dummy->mixer_lock); - change = dummy->capture_source[addr][0 ] != left && + change = dummy->capture_source[addr][0 ] != left || dummy->capture_source[addr][1 ] != right; dummy->capture_source[addr][0 ] = left; dummy->capture_source[addr][1 ] = right;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 700/1250 btrfs: detach failed sprout device from transaction update list
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guanghui Yang <[email protected]>
[ Upstream commit c93b3c43df561cd9f592cee20ae058b563f9e5b6 ]
When creating the first metadata chunk for a sprout filesystem, create_chunk() adds the new device to the transaction dev_update_list through device->post_commit_list.
If the subsequent system chunk creation fails, btrfs_init_new_device() aborts the transaction and releases the device while post_commit_list is still linked. This triggers a warning in btrfs_free_device() and leaves the transaction list referencing freed memory.
Detach the device while holding chunk_mutex before releasing it.
--- fs/btrfs/volumes.c | 2 ++ 1 file changed, 2 insertions(+)
fs/btrfs/volumes.c +2 -0
@@ -3034,6 +3034,8 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_pathbtrfs_sysfs_remove_device (device);mutex_lock (&fs_info->fs_devices->device_list_mutex);mutex_lock (&fs_info->chunk_mutex); +if (!list_empty (&device->post_commit_list)) +list_del_init (&device->post_commit_list);list_del_rcu (&device->dev_list);list_del (&device->dev_alloc_list); fs_info->fs_devices->num_devices--;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 701/1250 btrfs: restore active device pointers after failed sprout
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guanghui Yang <[email protected]>
[ Upstream commit e0b54613aabeb8e9da597f23b90c6a03d0981986 ]
btrfs_init_new_device() switches latest_dev and possibly s_bdev from the seed device to the new sprout device before creating the first writable chunks.
If chunk creation or the subsequent sprout setup fails, the error path releases the new device without switching those pointers back. btrfs_show_devname() can then dereference the freed latest_dev and crash.
Restore the active device pointers to the latest seed device before removing and releasing the failed sprout device.
--- fs/btrfs/volumes.c | 2 ++ 1 file changed, 2 insertions(+)
fs/btrfs/volumes.c +2 -0
@@ -3033,6 +3033,8 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path error_sysfs:btrfs_sysfs_remove_device (device);mutex_lock (&fs_info->fs_devices->device_list_mutex); +if (seeding_dev) +btrfs_assign_next_active_device (device, seed_devices->latest_dev);mutex_lock (&fs_info->chunk_mutex);if (!list_empty (&device->post_commit_list))list_del_init (&device->post_commit_list);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 702/1250 bonding: alb: fix uninitialized transport header access in alb_determine_nd()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 70f3995830d3f1e79faa14eb0605914f778feca9 ]
alb_determine_nd() uses icmp6_hdr(skb) to inspect ICMPv6 headers. However, in xmit paths (e.g. packets sent via AF_PACKET / raw sockets or forwarded packets), skb->transport_header is not guaranteed to be initialized. While pskb_network_may_pull() ensures the packet data is linear starting from the network header, it does not set or adjust the transport header offset.
Dereferencing icmp6_hdr(skb) can therefore access out-of-bounds memory.
Fetch the icmp6hdr directly after ipv6hdr following pskb_network_may_pull(), and reload ipv6hdr in case pskb_may_pull() reallocated skb->head. Also remove the unused bond argument from alb_determine_nd().
--- drivers/net/bonding/bond_alb.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
drivers/net/bonding/bond_alb.c +7 -6
@@ -1281,10 +1281,10 @@ static int alb_set_mac_address(struct bonding *bond, void *addr) }/* determine if the packet is NA or NS */ -static bool alb_determine_nd (struct sk_buff *skb,struct bonding *bond) +static bool alb_determine_nd (struct sk_buff *skb) { -struct ipv6hdr *ip6hdr; -struct icmp6hdr *hdr; +const struct ipv6hdr *ip6hdr; +const struct icmp6hdr *hdr;if (!pskb_network_may_pull (skb,sizeof (*ip6hdr)))return true;
@@ -1296,7 +1296,8 @@ static bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond)if (!pskb_network_may_pull (skb,sizeof (*ip6hdr) +sizeof (*hdr)))return true; - hdr =icmp6_hdr (skb); + ip6hdr =ipv6_hdr (skb); + hdr = (const struct icmp6hdr *)(ip6hdr +1 );return hdr->icmp6_type ==NDISC_NEIGHBOUR_ADVERTISEMENT || hdr->icmp6_type ==NDISC_NEIGHBOUR_SOLICITATION ; }
@@ -1381,7 +1382,7 @@ struct slave *bond_xmit_tlb_slave_get(struct bonding *bond,if (!is_multicast_ether_addr (eth_data->h_dest)) {switch (skb->protocol) {case htons (ETH_P_IPV6 ): -if (alb_determine_nd (skb, bond)) +if (alb_determine_nd (skb))break ; fallthrough;case htons (ETH_P_IP ):
@@ -1467,7 +1468,7 @@ struct slave *bond_xmit_alb_slave_get(struct bonding *bond,break ; } -if (alb_determine_nd (skb, bond)) { +if (alb_determine_nd (skb)) { do_tx_balance = false;break ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 703/1250 perf: RISC-V: check cpu_hw_evt before dereference in overflow IRQ
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xixin Liu <[email protected]>
[ Upstream commit f643f520c4c6998fa27dce90dd3b3ff6414e0bae ]
The overflow IRQ handler dereferences cpu_hw_evt before the null check. Move the check first. Defensive only; the cookie is valid on the normal path today.
--- drivers/perf/riscv_pmu_sbi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/perf/riscv_pmu_sbi.c +3 -1
@@ -1051,11 +1051,13 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)u64 overflowed_ctrs =0 ;struct cpu_hw_events *cpu_hw_evt = dev;u64 start_clock =sched_clock (); -struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr; +struct riscv_pmu_snapshot_data *sdata;if (WARN_ON_ONCE (!cpu_hw_evt))return IRQ_NONE ; + sdata = cpu_hw_evt->snapshot_addr; +/* Firmware counter don't support overflow yet */ fidx =find_first_bit (cpu_hw_evt->used_hw_ctrs,RISCV_MAX_COUNTERS );if (fidx ==RISCV_MAX_COUNTERS ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 704/1250 scsi: mpt3sas: Avoid out-of-bounds cpumask_of_node() call in _base_assign_reply_queues()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivy Lopez <[email protected]>
[ Upstream commit e0d26fe176a8db6ccad4ab38c5bab29391c1946b ]
dev_to_node() can return NUMA_NO_NODE (-1) on systems without NUMA topology information for the PCI device, such as single-socket boards that don't expose device-to-node affinity. Passing -1 directly into cpumask_of_node() indexes node_to_cpumask_map[-1], an out-of-bounds array read caught by UBSAN:
UBSAN: array-index-out-of-bounds in arch/x86/include/asm/topology.h:72:28 index -1 is out of range for type 'cpumask *[1024]'
Fall back to cpu_online_mask when no NUMA node is available, rather than assuming dev_to_node() always returns a valid node index.
--- drivers/scsi/mpt3sas/mpt3sas_base.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/scsi/mpt3sas/mpt3sas_base.c +4 -1
@@ -3238,7 +3238,10 @@ _base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc)* corresponding to high iops queues. */ if (ioc->high_iops_queues) { - mask =cpumask_of_node (dev_to_node (&ioc->pdev->dev)); +int node =dev_to_node (&ioc->pdev->dev); + + mask = (node ==NUMA_NO_NODE ) ? + cpu_online_mask :cpumask_of_node (node);for (index =0 ; index < ioc->high_iops_queues; index++) { irq =pci_irq_vector (ioc->pdev, index);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 705/1250 sched: Make class_schedulers avoid pushing current, and get rid of proxy_tag_curr()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: John Stultz <[email protected]>
[ Upstream commit e0ca8991b2de6c9dfe6fcd8a0364951b2bd56797 ]
With proxy-execution, the scheduler selects the donor, but for blocked donors, we end up running the lock owner.
This caused some complexity, because the class schedulers make sure to remove the task they pick from their pushable task lists, which prevents the donor from being migrated, but there wasn't then anything to prevent rq->curr from being migrated if rq->curr != rq->donor.
This was sort of hacked around by calling proxy_tag_curr() on the rq->curr task if we were running something other then the donor. proxy_tag_curr() did a dequeue/enqueue pair on the rq->curr task, allowing the class schedulers to remove it from their pushable list.
The dequeue/enqueue pair was wasteful, and additonally K Prateek highlighted that we didn't properly undo things when we stopped proxying, leaving the lock owner off the pushable list.
After some alternative approaches were considered, Peter suggested just having the RT/DL classes just avoid migrating when task_on_cpu().
So rework pick_next_pushable_dl_task() and the rt pick_next_pushable_task() functions so that they skip over the first pushable task if it is on_cpu.
Then just drop all of the proxy_tag_curr() logic.
Stable-dep-of: dae5c0292080 ("sched/rt,dl: Skip migrate-disabled tasks when picking a push candidate")
--- kernel/sched/core.c | 24 ------------------------ kernel/sched/deadline.c | 18 ++++++++++++++++-- kernel/sched/rt.c | 15 ++++++++++++--- 3 files changed, 28 insertions(+), 29 deletions(-)
kernel/sched/core.c +0 -24
@@ -6758,23 +6758,6 @@ find_proxy_task(struct rq *rq, struct task_struct *donor, struct rq_flags *rf) }#endif /* SCHED_PROXY_EXEC */ -static inline void proxy_tag_curr (struct rq *rq,struct task_struct *owner) -{ -if (!sched_proxy_exec ()) -return ; -/* -* pick_next_task() calls set_next_task() on the chosen task -* at some point, which ensures it is not push/pullable. -* However, the chosen/donor task *and* the mutex owner form an -* atomic pair wrt push/pull. -* -* Make sure owner we run is not pushable. Unfortunately we can -* only deal with that by means of a dequeue/enqueue cycle. :-/ -*/ -dequeue_task (rq, owner,DEQUEUE_NOCLOCK |DEQUEUE_SAVE ); -enqueue_task (rq, owner,ENQUEUE_NOCLOCK |ENQUEUE_RESTORE ); -} -/* * __schedule() is the main scheduler function. *
@@ -6925,9 +6908,6 @@ static void __sched notrace __schedule(int sched_mode)*/ RCU_INIT_POINTER (rq->curr, next); -if (!task_current_donor (rq, next)) -proxy_tag_curr (rq, next); -/* * The membarrier system call requires each architecture * to have a full memory barrier after updating
@@ -6962,10 +6942,6 @@ static void __sched notrace __schedule(int sched_mode)/* Also unlocks the rq: */ rq =context_switch (rq, prev, next, &rf); }else { -/* In case next was already curr but just got blocked_donor */ -if (!task_current_donor (rq, next)) -proxy_tag_curr (rq, next); -rq_unpin_lock (rq, &rf);__balance_callbacks (rq);raw_spin_rq_unlock_irq (rq);
kernel/sched/deadline.c +16 -2
@@ -2768,12 +2768,26 @@ static int find_later_rq(struct task_struct *task)static struct task_struct *pick_next_pushable_dl_task (struct rq *rq) { -struct task_struct *p; +struct task_struct *i, *p =NULL ; +struct rb_node *next_node;if (!has_pushable_dl_tasks (rq))return NULL ; - p =__node_2_pdl (rb_first_cached (&rq->dl.pushable_dl_tasks_root)); + next_node =rb_first_cached (&rq->dl.pushable_dl_tasks_root); +while (next_node) { + i =__node_2_pdl (next_node); +/* make sure task isn't on_cpu (possible with proxy-exec) */ +if (!task_on_cpu (rq, i)) { + p = i; +break ; + } + + next_node =rb_next (next_node); + } + +if (!p) +return NULL ;WARN_ON_ONCE (rq->cpu !=task_cpu (p));WARN_ON_ONCE (task_current (rq, p));
kernel/sched/rt.c +12 -3
@@ -1853,13 +1853,22 @@ static int find_lowest_rq(struct task_struct *task)static struct task_struct *pick_next_pushable_task (struct rq *rq) { -struct task_struct *p; +struct plist_head *head = &rq->rt.pushable_tasks; +struct task_struct *i, *p =NULL ;if (!has_pushable_tasks (rq))return NULL ; - p =plist_first_entry (&rq->rt.pushable_tasks, - struct task_struct, pushable_tasks); +plist_for_each_entry (i, head, pushable_tasks) { +/* make sure task isn't on_cpu (possible with proxy-exec) */ +if (!task_on_cpu (rq, i)) { + p = i; +break ; + } + } + +if (!p) +return NULL ;BUG_ON (rq->cpu !=task_cpu (p));BUG_ON (task_current (rq, p));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 706/1250 sched/rt,dl: Skip migrate-disabled tasks when picking a push candidate
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Seiji Nishikawa <[email protected]>
[ Upstream commit dae5c0292080dd7b9c7d784268dcf443f1f3d15e ]
A migrate_disable()'d RT task cannot be moved to another CPU, but the scheduler still keeps such a task on that CPU's pushable list (rq->rt.pushable_tasks) and still marks the runqueue RT-overloaded (rq->rt.overloaded = 1). So the RT balancer keeps treating this CPU as having a task to move away, and keeps trying to move the task, but the push can never succeed. When the head is pinned, push_rt_task() does not give up either. It falls back to pushing rq->curr instead, using the per-CPU stopper, as added by commit a7c81556ec4d ("sched: Fix migrate_disable() vs rt/dl balancing").
The CPU spends tens of milliseconds in this retry loop. The core is isolated for real-time work, but during the loop nearly half of its time is consumed by pushes that cannot succeed.
An ftrace capture of the affected CPU, with sched_switch enabled and commit 94894c9c477e ("sched/rt: Skip currently executing CPU in rto_next_cpu()") applied, shows where the CPU time went. Two SCHED_FIFO tasks at equal priority shared the CPU, taskA migrate_disable()'d and queued, taskB as rq->curr. In one 89 ms window, taskB got only 52 ms of CPU. The other 37 ms went to the stopper thread.
The scheduler kept trying to push taskA, the pinned head of the pushable list, fell back to pushing taskB instead, and woke the stopper 5204 times. Every one of those pushes failed and no task was moved. taskA stayed runnable and queued the whole time, and never ran.
Pushing taskB fails on a re-check. find_lock_lowest_rq() drops the rq lock to take the target rq lock, then checks again with "task != pick_next_pushable_task(rq)".
The task being pushed is taskB, but the pick returns taskA, the head of the pushable list. taskB is rq->curr, and set_next_task_rt() removes the running task from that list, so taskB can never be the head. The check expects a candidate taken from the pushable list, but the fallback pushes rq->curr, which is never on that list. So the check fails every time.
.--> push-IPI arrives
| |
| v
| pushable head = taskA -> pinned, cannot be pushed
| |
| v
| so push taskB instead -> wake migration/N, a stop-class
| | thread, so it preempts taskB
| v
| re-check compares taskB against the pushable head,
| which is still taskA -> give up
| |
| v
| nothing moved, taskA still queued, rq still overloaded
| |
'----------'
repeats every ~17 us, 5204 times, for 89 msThe loop cannot stop itself. Every round leaves the runqueue exactly as it was, so the next push-IPI does the same thing. In the capture it ended only when taskB went to sleep on its own. taskA was then picked locally and left the pushable list.
CPU time per task in the window, from sched_switch:
taskB 51.95 ms real work
migration/N 37.18 ms nothing moved
taskA 0.00 ms queued the whole time, never pickedidle 0.01 ms
Counts over the same window:
7667 push-IPIs handled on this CPU
17481 pick_next_pushable_task() returned taskA, still pinned
5204 find_lock_lowest_rq() gave up on the re-check
1 push that actually completed
0 migrations of taskA The CPU times and the window length come from the standard sched_switch tracepoint. The counts needed tracepoints added inside the RT balancer for this investigation.
The self-IPI path is closed by the rto_next_cpu() fix above, and that part works. But the runqueue is still marked overloaded, because the pinned task is still advertised as pushable. Other CPUs now send the push-IPIs during their own RT balancing, and the same loop runs again. Closing the self-IPI path did not stop a pinned task from triggering push balancing.
A pinned task should never have been returned as a push candidate in the first place. A migrate_disable()'d task cannot be migrated, so it belongs in the same skip that was added for on_cpu tasks by commit e0ca8991b2de ("sched: Make class_schedulers avoid pushing current, and get rid of proxy_tag_curr()"). Add is_migration_disabled() to the skip condition in pick_next_pushable_task() and pick_next_pushable_dl_task().
With the skip in place, if the pinned task is the only extra runnable task the helpers return NULL, push_rt_task() and push_dl_task() give up early, and no stopper is woken. The pinned task then runs locally once curr yields. If a task that really can be migrated is queued behind the pinned head, it is now picked and pushed for real.
This makes the fallback that pushes rq->curr unreachable when the pushable head is migrate-disabled. Nothing is lost, because that path was always stopped by the re-check described above. In the capture it ran 5204 times and moved nothing.
--- kernel/sched/deadline.c | 4 ++-- kernel/sched/rt.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-)
kernel/sched/deadline.c +2 -2
@@ -2777,8 +2777,8 @@ static struct task_struct *pick_next_pushable_dl_task(struct rq *rq) next_node =rb_first_cached (&rq->dl.pushable_dl_tasks_root);while (next_node) { i =__node_2_pdl (next_node); -/* make sure task isn't on_cpu (possible with proxy-exec) */ -if (!task_on_cpu (rq, i)) { +/* skip tasks that cannot be migrated */ +if (!task_on_cpu (rq, i) && !is_migration_disabled (i)) { p = i;break ; }
kernel/sched/rt.c +2 -2
@@ -1860,8 +1860,8 @@ static struct task_struct *pick_next_pushable_task(struct rq *rq)return NULL ;plist_for_each_entry (i, head, pushable_tasks) { -/* make sure task isn't on_cpu (possible with proxy-exec) */ -if (!task_on_cpu (rq, i)) { +/* skip tasks that cannot be migrated */ +if (!task_on_cpu (rq, i) && !is_migration_disabled (i)) { p = i;break ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 707/1250 x86/itmt: Dont make ITMT enablement depend on debugfs
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mario Limonciello <[email protected]>
[ Upstream commit eaece4849991d62fcd6f46637c55dcce00e25d70 ]
sched_set_itmt_support() treats debugfs file creation failures as fatal. When CONFIG_DEBUG_FS is disabled, debugfs stubs return ERR_PTR(-ENODEV), causing ITMT to be silently disabled.
debugfs is a debug-only facility; its return values should be ignored. Drop the fatal error handling and enable ITMT unconditionally.
--- arch/x86/kernel/itmt.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
arch/x86/kernel/itmt.c +2 -6
@@ -110,18 +110,14 @@ int sched_set_itmt_support(void) arch_debugfs_dir, &sysctl_sched_itmt_enabled, &dfs_sched_itmt_fops); -if (IS_ERR_OR_NULL (dfs_sched_itmt)) { +if (IS_ERR (dfs_sched_itmt)) dfs_sched_itmt =NULL ; -return -ENOMEM ; - } dfs_sched_core_prio =debugfs_create_file ("sched_core_priority" ,0644 , arch_debugfs_dir,NULL , &sched_core_priority_fops); -if (IS_ERR_OR_NULL (dfs_sched_core_prio)) { +if (IS_ERR (dfs_sched_core_prio)) dfs_sched_core_prio =NULL ; -return -ENOMEM ; - } sched_itmt_capable = true;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 708/1250 perf/core: Skip empty AUX records with only format flags
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Leo Yan <[email protected]>
[ Upstream commit 8a7f5b5e860b5c113ca99acd5b1e9074f5c5af3c ]
perf_aux_output_end() emits a PERF_RECORD_AUX when the recorded size is nonzero or when any flag other than PERF_AUX_FLAG_OVERWRITE is set.
PMU format flags describe how an AUX payload is encoded. TRBE driver sets PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW for raw trace buffers, causing an AUX record to be emitted even when no trace data.
This is noticeable when tracing a task with strace. Ptrace stops repeatedly end empty AUX transactions, producing many zero-sized PERF_RECORD_AUX records. For example:
perf record -e cs_etm//u -m,128M -- strace ls
perf script -D 2>&1 |
awk '/PERF_RECORD_AUX offset/ {
for (i = 1; i <= NF; i++)
if ($i == "size:" && $(i + 1) == "0")
count++
}
END { print count }'
165This recording contains 165 zero-sized AUX records which provide no useful information to userspace.
Ignore PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK, together with PERF_AUX_FLAG_OVERWRITE, when deciding whether an empty AUX record is useful. Zero-sized records carrying TRUNCATED, PARTIAL or COLLISION are still emitted.
--- kernel/events/ring_buffer.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
kernel/events/ring_buffer.c +7 -2
@@ -509,7 +509,10 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size)/* * Only send RECORD_AUX if we have something useful to communicate * -* Note: the OVERWRITE records by themselves are not considered +* PMU_FORMAT bits identify the PMU type rather than an AUX event +* has occurred, so ignore them for zero-sized records. +* +* The OVERWRITE records by themselves are not considered * useful, as they don't communicate any *new* information, * aside from the short-lived offset, that becomes history at * the next event sched-in and therefore isn't useful.
@@ -518,7 +521,9 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size)* offset. So, from now on we don't output AUX records that * have *only* OVERWRITE flag set. */ -if (size || (handle->aux_flags & ~(u64 )PERF_AUX_FLAG_OVERWRITE )) +if (size || + (handle->aux_flags & ~(u64)(PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK | +PERF_AUX_FLAG_OVERWRITE )))perf_event_aux_event (handle->event, aux_head, size, handle->aux_flags);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 709/1250 locking/lockdep: Invalidate stale class_cache entries for zapped classes
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 02c6be7d675b21d81f0ba3a524346850a8c0e3bf ]
syzbot reported a lockdep splat hitting DEBUG_LOCKS_WARN_ON(1) in hlock_class() due to an invalid class_idx:
WARNING: kernel/locking/lockdep.c:238 at __lock_acquire+0x382/0x2cf0 kernel/locking/lockdep.c:5203 Workqueue: wg-crypt-wg0 wg_packet_tx_worker RIP: 0010:hlock_class kernel/locking/lockdep.c:238 [inline] RIP: 0010:check_wait_context kernel/locking/lockdep.c:4870 [inline] RIP: 0010:__lock_acquire+0x389/0x2cf0 kernel/locking/lockdep.c:5203 Call Trace: <IRQ> lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5886 _raw_spin_lock+0x2e/0x40 kernel/locking/spinlock.c:173 tcp_tsq_handler+0x29/0x200 net/ipv4/tcp_output.c:1291 tcp_tsq_workfn+0x384/0x410 net/ipv4/tcp_output.c:1325 ...
When a lock class is zapped (e.g. during module unload or key unregistration), zap_class() clears the class's bit in lock_classes_in_use and removes it from the class hash table. However, existing lockdep_map instances embedded in data structures may still retain a pointer to the zapped class in their class_cache[] array.
When __lock_acquire() subsequently runs on such a lock, it finds lock->class_cache[subclass] != NULL, skipping register_lock_class() and assigning hlock->class_idx to the index of the zapped class. When check_wait_context() or hlock_class() inspects the held_lock, it finds !test_bit(class_idx, lock_classes_in_use) and warns. Furthermore, if the zapped slot is subsequently re-allocated to an unrelated lock key, the stale class_cache entry would erroneously match the unrelated class (ABA issue).
Add lock_class_cache_is_valid() to validate that the cached class is within lock_classes bounds, still allocated in lock_classes_in_use (using uninstrumented arch_test_bit() in __always_inline context so it is safe in noinstr contexts like match_held_lock()), and that class->key matches the expected subkey (taking lockdep_set_subclass() overrides into account). Also use READ_ONCE()/WRITE_ONCE() when accessing class_cache[]. If the entry is invalid or stale, fall back to register_lock_class() / look_up_lock_class().
--- kernel/locking/lockdep.c | 50 +++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 8 deletions(-)
kernel/locking/lockdep.c +42 -8
@@ -947,6 +947,34 @@ look_up_lock_class(const struct lockdep_map *lock, unsigned int subclass)return NULL ; } +static __always_inline bool lock_class_cache_is_valid(const struct lockdep_map *lock, +const struct lock_class *class, +unsigned int subclass) +{ +unsigned int class_subclass; + +if (!class) +return false; + +if (unlikely (class < lock_classes || class >= lock_classes +MAX_LOCKDEP_KEYS )) +return false; + +if (unlikely (!arch_test_bit (class - lock_classes, lock_classes_in_use))) +return false; + +if (unlikely (!lock->key)) +return false; + + class_subclass = subclass ? subclass : class->subclass; +if (unlikely (class_subclass >=MAX_LOCKDEP_SUBCLASSES )) +return false; + +if (unlikely (READ_ONCE (class->key) != lock->key->subkeys + class_subclass)) +return false; + +return true; +} +/* * Static locks do not have their class-keys yet - for them the key is * the lock object itself. If the lock is in the per cpu area, the
@@ -1379,9 +1407,9 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force) out_set_class_cache:if (!subclass || force) - lock->class_cache[0 ] = class; +WRITE_ONCE (lock->class_cache[0 ], class);else if (subclass <NR_LOCKDEP_CACHING_CLASSES ) - lock->class_cache[subclass] = class; +WRITE_ONCE (lock->class_cache[subclass], class);/* * Hash collision, did we smoke some? We found a class with a matching
@@ -4941,7 +4969,7 @@ void lockdep_init_map_type(struct lockdep_map *lock, const char *name,int i;for (i =0 ; i <NR_LOCKDEP_CACHING_CLASSES ; i++) - lock->class_cache[i] =NULL ; +WRITE_ONCE (lock->class_cache[i],NULL );#ifdef CONFIG_LOCK_STAT lock->cpu =raw_smp_processor_id ();
@@ -5006,12 +5034,15 @@ EXPORT_SYMBOL_GPL(__lockdep_no_track__);void lockdep_set_lock_cmp_fn (struct lockdep_map *lock,lock_cmp_fn cmp_fn,lock_print_fn print_fn) { -struct lock_class *class = lock->class_cache[0 ]; +struct lock_class *class =READ_ONCE (lock->class_cache[0 ]);unsigned long flags;raw_local_irq_save (flags);lockdep_recursion_inc (); +if (!lock_class_cache_is_valid (lock, class,0 )) + class =NULL ; +if (!class) class =register_lock_class (lock,0 ,0 );
@@ -5103,8 +5134,11 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,if (DEBUG_LOCKS_WARN_ON (subclass >=MAX_LOCKDEP_SUBCLASSES ))return 0 ; -if (subclass <NR_LOCKDEP_CACHING_CLASSES ) - class = lock->class_cache[subclass]; +if (subclass <NR_LOCKDEP_CACHING_CLASSES ) { + class =READ_ONCE (lock->class_cache[subclass]); +if (!lock_class_cache_is_valid (lock, class, subclass)) + class =NULL ; + }/* * Not cached? */
@@ -5307,9 +5341,9 @@ static noinstr int match_held_lock(const struct held_lock *hlock,return 1 ;if (hlock->references) { -const struct lock_class *class = lock->class_cache[0 ]; +const struct lock_class *class =READ_ONCE (lock->class_cache[0 ]); -if (!class) +if (!lock_class_cache_is_valid (lock, class,0 )) class =look_up_lock_class (lock,0 );/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 710/1250 octeontx2-af: Fix limiting SRIOV VF count logic
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sunil Goutham <[email protected]>
[ Upstream commit f695390ea63941a9e412bf1f3afe65ab245fc681 ]
When RVU PF0/AF's VFs are SDP instead of LBK, limiting the VF count based on the LBK channel count is incorrect.
Apply LBK channel-based VF limits only when the VF device ID matches the LBK RVU AFVF device.
--- .../net/ethernet/marvell/octeontx2/af/rvu.c | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-)
drivers/net/ethernet/marvell/octeontx2/af/rvu.c +19 -14
@@ -3377,6 +3377,8 @@ int rvu_get_num_lbk_chans(void)return ret; } +#define PCI_DEVID_OCTEONTX2_RVU_AFVF 0xA0F8 +static int rvu_enable_sriov (struct rvu *rvu) {struct pci_dev *pdev = rvu->pdev;
@@ -3395,24 +3397,27 @@ static int rvu_enable_sriov(struct rvu *rvu)return 0 ;pci_read_config_word (pdev, pos +PCI_SRIOV_VF_DID , &rvu->vf_devid); - chans =rvu_get_num_lbk_chans (); -if (chans <0 ) -return chans; - vfs =pci_sriov_get_totalvfs (pdev); - -/* Limit VFs in case we have more VFs than LBK channels available. */ -if (vfs > chans) - vfs = chans; -if (!vfs)return 0 ; -/* LBK channel number 63 is used for switching packets between -* CGX mapped VFs. Hence limit LBK pairs till 62 only. -*/ -if (vfs >62 ) - vfs =62 ; +if (rvu->vf_devid ==PCI_DEVID_OCTEONTX2_RVU_AFVF ) { + chans =rvu_get_num_lbk_chans (); +if (chans <0 ) +return chans; + +/* The last LBK channel is reserved for switching packets between +* CGX mapped VFs. Also, since LBK VFs work in pairs, limit VF +* count to available LBK channels minus 2. +*/ + vfs =min (vfs, chans -2 ); + +if (vfs <=0 ) { +dev_warn (&pdev->dev, +"Skipping SRIOV enablement, not enough LBK channels available\n" ); +return 0 ; + } + }/* Save VFs number for reference in VF interrupts handlers. * Since interrupts might start arriving during SRIOV enablement
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 711/1250 ALSA: ump: do not touch legacy_rmidi before it exists
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qingyu Zhang <[email protected]>
[ Upstream commit adeee7187694719890aaffdc14b7e89cfd736f1d ]
snd_ump_parse_endpoint() sets ump->parsed on every exit, including error, before the caller attaches the legacy rawmidi device. ump_handle_ep_name_msg() then treats parsed as "legacy_rmidi is live" and calls ump_legacy_set_rawmidi_name(), which snprintf()s into ump->legacy_rmidi->name. If a UMP packet arrives in that window (IRQ path from snd_ump_receive), legacy_rmidi is still NULL (KASAN null-ptr-deref in snprintf).
Guard the legacy helpers. parsed only means endpoint info was parsed, not that legacy_rmidi exists.
--- sound/core/ump.c | 4 ++++ 1 file changed, 4 insertions(+)
sound/core/ump.c +4 -0
@@ -1333,6 +1333,8 @@ static void update_legacy_names(struct snd_ump_endpoint *ump) {struct snd_rawmidi *rmidi = ump->legacy_rmidi; +if (!rmidi) +return ;update_legacy_substreams (ump, rmidi,SNDRV_RAWMIDI_STREAM_INPUT );update_legacy_substreams (ump, rmidi,SNDRV_RAWMIDI_STREAM_OUTPUT ); }
@@ -1341,6 +1343,8 @@ static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump) {struct snd_rawmidi *rmidi = ump->legacy_rmidi; +if (!rmidi) +return ;snprintf (rmidi->name,sizeof (rmidi->name),"%.68s (MIDI 1.0)" , ump->core.name); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 712/1250 printk/nbcon: Change nbcon_irq_work to IRQ_WORK_LAZY
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: John Ogness <[email protected]>
[ Upstream commit 560f4deda32785e260056200f8bb911c475c5b88 ]
Change the nbcon_irq_work to be IRQ_WORK_LAZY, thus not raising an IRQ upon irq_work queuing. The irq_work is then handled on the next kernel tick. This additional delay is acceptable because nbcon_irq_work is only responsible for non-emergency deferred printing, which is delayed anyway. This has the benefit of not needing to raise an IRQ for each printk() call.
On a side note, the Tegra20 and Tegra30 platforms can hang if an irq_work IRQ is raised while entering cpuidle states. This problem was reproducible by calling printk() while entering cpuidle. So this change also provides a workaround for these platforms (as long as they are not running tickless).
--- kernel/printk/nbcon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/printk/nbcon.c +1 -1
@@ -1705,7 +1705,7 @@ bool nbcon_alloc(struct console *con)return false;rcuwait_init (&con->rcuwait); -init_irq_work (&con->irq_work, nbcon_irq_work); + con->irq_work =IRQ_WORK_INIT_LAZY (nbcon_irq_work);atomic_long_set (&ACCESS_PRIVATE (con, nbcon_prev_seq),-1UL );nbcon_state_set (con, &state);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 713/1250 ASoC: ux500: Fix MSP stream lifecycle handling
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit c37ba8fe00f264eee2fd18b0bff7c5f188136c51 ]
The trigger stop path drops the direction busy flag even though ALSA still owns the stream until shutdown. A later trigger cannot reliably restart it, shutdown may leave the block configured, and a second stream may overwrite shared duplex configuration.
Keep configured and running directions as separate state. Program shared settings only for the first direction, require a compatible configuration for the other half of a duplex stream, and enable the frame generator only while a provider stream is running. Also fix the RX-disable direction test and preserve the other direction multichannel setup.
--- sound/soc/ux500/ux500_msp_dai.c | 8 +- sound/soc/ux500/ux500_msp_i2s.c | 183 ++++++++++++++++++++++++-------- sound/soc/ux500/ux500_msp_i2s.h | 4 + 3 files changed, 149 insertions(+), 46 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +6 -2
@@ -34,8 +34,10 @@ static int setup_pcm_multichan(struct snd_soc_dai *dai,if (drvdata->slots >1 ) { msp_config->multichannel_configured =1 ; - multi->tx_multichannel_enable = true; - multi->rx_multichannel_enable = true; + multi->tx_multichannel_enable = + msp_config->direction &MSP_DIR_TX ; + multi->rx_multichannel_enable = + msp_config->direction &MSP_DIR_RX ; multi->rx_comparison_enable_mode =MSP_COMPARISON_DISABLED ; multi->tx_channel_0_enable = drvdata->tx_mask;
@@ -192,6 +194,7 @@ static int setup_clocking(struct snd_soc_dai *dai,case SND_SOC_DAIFMT_BC_FC :dev_dbg (dai->dev,"%s: Codec is master.\n" , __func__); + msp_config->clock_provider = false; msp_config->iodelay =0x20 ; msp_config->rx_fsync_sel =0 ; msp_config->tx_fsync_sel =1 <<TFSSEL_SHIFT ;
@@ -204,6 +207,7 @@ static int setup_clocking(struct snd_soc_dai *dai,case SND_SOC_DAIFMT_BP_FP :dev_dbg (dai->dev,"%s: Codec is slave.\n" , __func__); + msp_config->clock_provider = true; msp_config->tx_clk_sel =TX_CLK_SEL_SRG ; msp_config->tx_fsync_sel =TX_SYNC_SRG_PROG ; msp_config->rx_clk_sel =RX_CLK_SEL_SRG ;
sound/soc/ux500/ux500_msp_i2s.c +139 -44
284 lines not on this page · raw message
sound/soc/ux500/ux500_msp_i2s.h +4 -0
@@ -460,6 +460,7 @@ struct ux500_msp_config {enum msp_data_size data_size;unsigned int def_elem_len;unsigned int iodelay; +bool clock_provider; };struct ux500_msp {
@@ -470,8 +471,11 @@ struct ux500_msp {enum msp_state msp_state;int def_elem_len;unsigned int dir_busy; +unsigned int dir_running;int loopback_enable;unsigned int f_bitclk; +bool clock_provider; +struct ux500_msp_config config; }; int ux500_msp_i2s_init_msp(struct platform_device *pdev,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 714/1250 ASoC: ux500: Propagate MSP setup errors
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 3415421a2b0bc4e32bb5a9df24ed7863512d47a7 ]
The prepare callback continues with a partly initialized configuration when format setup fails. Probe likewise tests the allocated pointer instead of the return value, so an MMIO resource or mapping failure can be ignored after allocation succeeds.
Return configuration failures from prepare and test the MSP initialization result directly.
--- sound/soc/ux500/ux500_msp_dai.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +4 -2
@@ -468,7 +468,9 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,dev_dbg (dai->dev,"%s: MSP %d (%s): Enter (rate = %d).\n" , __func__, dai->id,snd_pcm_stream_str (substream), runtime->rate); -setup_msp_config (substream, dai, &msp_config); + ret =setup_msp_config (substream, dai, &msp_config); +if (ret) +return ret; ret =ux500_msp_i2s_open (drvdata->msp, &msp_config);if (ret <0 ) {
@@ -764,7 +766,7 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) } ret =ux500_msp_i2s_init_msp (pdev, &drvdata->msp); -if (!drvdata->msp) { +if (ret) {dev_err (&pdev->dev,"%s: ERROR: Failed to init MSP-struct (%d)!" , __func__, ret);
Signature · 1 line
2.53.0