6.6.145-rc1 review
Subsystems (7)
Greg Kroah-Hartman
maintainerPATCH 853/1266 netfilter: xt_connmark: reject invalid shift parameters
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wyatt Feng <[email protected]>
[ Upstream commit 1b47026fb4b35bac850ad6e8a4ad7fc018e09ebc ]
Revision 2 of the CONNMARK target accepts user-controlled shift parameters and applies them to 32-bit mark values in connmark_tg_shift().
A shift_bits value of 32 or more triggers an undefined-shift bug when the rule is evaluated. Invalid shift_dir values are also accepted and silently fall back to the left-shift path.
Reject invalid revision-2 shift parameters in connmark_tg_check() so malformed rules fail at installation time, before they can reach the packet path.
--- net/netfilter/xt_connmark.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
net/netfilter/xt_connmark.c +12 -2
@@ -112,6 +112,16 @@ static int connmark_tg_check(const struct xt_tgchk_param *par)return ret; } +static int connmark_tg_check_v2 (const struct xt_tgchk_param *par) +{ +const struct xt_connmark_tginfo2 *info = par->targinfo; + +if (info->shift_dir >D_SHIFT_RIGHT || info->shift_bits >=32 ) +return -EINVAL ; + +return connmark_tg_check (par); +} +static void connmark_tg_destroy (const struct xt_tgdtor_param *par) {nf_ct_netns_put (par->net, par->family);
@@ -162,7 +172,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = { .name = "CONNMARK", .revision = 2, .family = NFPROTO_IPV4, - .checkentry = connmark_tg_check, + .checkentry = connmark_tg_check_v2, .target = connmark_tg_v2, .targetsize = sizeof(struct xt_connmark_tginfo2), .destroy = connmark_tg_destroy,
@@ -183,7 +193,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = { .name = "CONNMARK", .revision = 2, .family = NFPROTO_IPV6, - .checkentry = connmark_tg_check, + .checkentry = connmark_tg_check_v2, .target = connmark_tg_v2, .targetsize = sizeof(struct xt_connmark_tginfo2), .destroy = connmark_tg_destroy,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 854/1266 net/mlx5e: Fix HV VHCA stats zero-sized buffer allocation
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Feng Liu <[email protected]>
[ Upstream commit 25f6b929c7e379cbea7cb8caa67b49b2d1efae17 ]
mlx5e_hv_vhca_stats_create() is called from mlx5e_nic_enable(), before mlx5e_open(). At that point priv->stats_nch is still zero, because it is only ever incremented in mlx5e_channel_stats_alloc(), which is reached only from mlx5e_open_channel().
mlx5e_hv_vhca_stats_buf_size() therefore returns 0, and kvzalloc(0, GFP_KERNEL) returns ZERO_SIZE_PTR ((void *)16) rather than NULL. The "if (!buf)" guard does not catch this, and mlx5e_hv_vhca_stats_create() completes "successfully" with priv->stats_agent.buf set to ZERO_SIZE_PTR.
Once channels are opened (priv->stats_nch > 0) and the hypervisor enables stats reporting, mlx5e_hv_vhca_stats_work() recomputes buf_len using the new non-zero stats_nch and calls memset(buf, 0, buf_len) on ZERO_SIZE_PTR, faulting at address 0x10.
Allocate the buffer based on priv->max_nch, which is set in mlx5e_priv_init() and is the upper bound on stats_nch:
- Add a separate helper mlx5e_hv_vhca_stats_buf_max_size() that returns sizeof(per_ring_stats) * max(max_nch, stats_nch), and use it for the kvzalloc() in mlx5e_hv_vhca_stats_create().
- Keep mlx5e_hv_vhca_stats_buf_size() (which returns based on stats_nch) for the worker's active payload size, so the wire format (block->rings = stats_nch) and the amount of data filled by mlx5e_hv_vhca_fill_stats() are unchanged.
The max(max_nch, stats_nch) guard handles the rare case where mlx5e_attach_netdev() recomputes max_nch downward across a detach/resume cycle while priv->stats_nch persists (mlx5e_detach_netdev does not call mlx5e_priv_cleanup, so stats_nch is only reset when the netdev is destroyed). Without the guard, the worker could compute buf_len from stats_nch and overrun the smaller buffer allocated based on the reduced max_nch.
Allocating a non-zero buffer also makes the kvzalloc() failure path in mlx5e_hv_vhca_stats_create() reachable for the first time: it returns early without (re)creating the agent. Clear priv->stats_agent.{agent,buf} in mlx5e_hv_vhca_stats_destroy() after freeing them, so that if a later create() bails out on this path, a subsequent teardown does not double-free the stale agent/buffer left from a previous enable/disable cycle.
This mirrors the existing mlx5e pattern of preallocating arrays of size max_nch (e.g. priv->channel_stats) and lazily populating entries up to stats_nch on demand.
--- .../net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +9 -1
@@ -54,6 +54,12 @@ static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv) priv->stats_nch); } +static int mlx5e_hv_vhca_stats_buf_max_size (struct mlx5e_priv *priv) +{ +return (sizeof (struct mlx5e_hv_vhca_per_ring_stats ) * +max (priv->max_nch, priv->stats_nch)); +} +static void mlx5e_hv_vhca_stats_work (struct work_struct *work) {struct mlx5e_hv_vhca_stats_agent *sagent;
@@ -122,7 +128,7 @@ static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent)void mlx5e_hv_vhca_stats_create (struct mlx5e_priv *priv) { -int buf_len =mlx5e_hv_vhca_stats_buf_size (priv); +int buf_len =mlx5e_hv_vhca_stats_buf_max_size (priv);struct mlx5_hv_vhca_agent *agent; priv->stats_agent.buf =kvzalloc (buf_len,GFP_KERNEL );
@@ -155,5 +161,7 @@ void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv)return ;mlx5_hv_vhca_agent_destroy (priv->stats_agent.agent); + priv->stats_agent.agent =NULL ;kvfree (priv->stats_agent.buf); + priv->stats_agent.buf =NULL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 855/1266 net/mlx5e: Fix HV VHCA stats agent registration race
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Feng Liu <[email protected]>
[ Upstream commit 89b25b5f46f488ea3b29b3444864c76944c9075b ]
mlx5e_hv_vhca_stats_create() registers the stats agent through mlx5_hv_vhca_agent_create(). The helper publishes the agent in hv_vhca->agents[type] under agents_lock and immediately schedules an asynchronous control invalidation on the HV VHCA workqueue before returning to mlx5e.
The asynchronous invalidation invokes the control agent's invalidate callback, which reads the hypervisor control block and forwards the command to mlx5e_hv_vhca_stats_control(). That callback may either:
- call cancel_delayed_work_sync(&priv->stats_agent.work), or
- call queue_delayed_work(priv->wq, &sagent->work, sagent->delay).
However, the delayed_work and priv->stats_agent.agent are only initialized after mlx5_hv_vhca_agent_create() returns to mlx5e:
agent = mlx5_hv_vhca_agent_create(...); /* publish + invalidate */
...
priv->stats_agent.agent = agent; /* too late */
INIT_DELAYED_WORK(&priv->stats_agent.work, ...); /* too late */If the asynchronous control path runs before the two assignments above, it can:
- Operate on an uninitialized delayed_work whose timer.function is NULL. queue_delayed_work() calls add_timer() unconditionally, so when the timer expires the timer softirq invokes a NULL function pointer.
- Re-initialize the timer later through INIT_DELAYED_WORK() while the timer is already enqueued in the timer wheel, corrupting the hlist (entry.pprev cleared while the previous bucket node still points at this entry).
- When the worker eventually runs, mlx5e_hv_vhca_stats_work() reads
sagent->agent (NULL) and dereferences it inside
mlx5_hv_vhca_agent_write().Fix this by:
- Initializing priv->stats_agent.work before invoking mlx5_hv_vhca_agent_create(), so the work is always in a valid state when the control callback observes it.
- Adding a struct mlx5_hv_vhca_agent **ctx_update out-parameter to mlx5_hv_vhca_agent_create(). The helper writes the agent pointer to *ctx_update before publishing into hv_vhca->agents[] and triggering the agents_update flow, so any callback subsequently invoked from that flow already sees a valid priv->stats_agent.agent. This avoids having the control callback participate in agent initialization.
While at it, access priv->stats_agent.agent with READ_ONCE()/WRITE_ONCE() for the cross-CPU access with the worker, and clear priv->stats_agent.buf on the agent_create() failure path.
--- .../mellanox/mlx5/core/en/hv_vhca_stats.c | 21 +++++++++++-------- .../ethernet/mellanox/mlx5/core/lib/hv_vhca.c | 8 +++++-- .../ethernet/mellanox/mlx5/core/lib/hv_vhca.h | 6 ++++-- 3 files changed, 22 insertions(+), 13 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +12 -9
@@ -73,7 +73,7 @@ static void mlx5e_hv_vhca_stats_work(struct work_struct *work) sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work); priv = container_of(sagent, struct mlx5e_priv, stats_agent); buf_len =mlx5e_hv_vhca_stats_buf_size (priv); - agent = sagent->agent; + agent =READ_ONCE (sagent->agent); buf = sagent->buf;memset (buf,0 , buf_len);
@@ -135,11 +135,14 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)if (!priv->stats_agent.buf)return ; +INIT_DELAYED_WORK (&priv->stats_agent.work, mlx5e_hv_vhca_stats_work); + agent =mlx5_hv_vhca_agent_create (priv->mdev->hv_vhca,MLX5_HV_VHCA_AGENT_STATS , mlx5e_hv_vhca_stats_control,NULL , mlx5e_hv_vhca_stats_cleanup, - priv); + priv, + &priv->stats_agent.agent);if (IS_ERR_OR_NULL (agent)) {if (IS_ERR (agent))
@@ -148,20 +151,20 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) PTR_ERR(agent));kvfree (priv->stats_agent.buf); -return ; + priv->stats_agent.buf =NULL ; } - - priv->stats_agent.agent = agent; -INIT_DELAYED_WORK (&priv->stats_agent.work, mlx5e_hv_vhca_stats_work); }void mlx5e_hv_vhca_stats_destroy (struct mlx5e_priv *priv) { -if (IS_ERR_OR_NULL (priv->stats_agent.agent)) +struct mlx5_hv_vhca_agent *agent; + + agent =READ_ONCE (priv->stats_agent.agent); +if (IS_ERR_OR_NULL (agent))return ; -mlx5_hv_vhca_agent_destroy (priv->stats_agent.agent); - priv->stats_agent.agent =NULL ; +mlx5_hv_vhca_agent_destroy (agent); +WRITE_ONCE (priv->stats_agent.agent,NULL );kvfree (priv->stats_agent.buf); priv->stats_agent.buf =NULL ; }
drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c +6 -2
@@ -190,7 +190,7 @@ mlx5_hv_vhca_control_agent_create(struct mlx5_hv_vhca *hv_vhca)return mlx5_hv_vhca_agent_create (hv_vhca,MLX5_HV_VHCA_AGENT_CONTROL ,NULL , mlx5_hv_vhca_control_agent_invalidate, -NULL ,NULL ); +NULL ,NULL ,NULL ); }static void mlx5_hv_vhca_control_agent_destroy (struct mlx5_hv_vhca_agent *agent)
@@ -256,7 +256,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,void (*invalidate)(struct mlx5_hv_vhca_agent *,u64 block_mask), void (*cleaup)(struct mlx5_hv_vhca_agent *agent), - void *priv) + void *priv, + struct mlx5_hv_vhca_agent **ctx_update) { struct mlx5_hv_vhca_agent *agent;
@@ -284,6 +285,9 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, agent->invalidate = invalidate; agent->cleanup = cleaup; +if (ctx_update) +WRITE_ONCE (*ctx_update, agent); +mutex_lock (&hv_vhca->agents_lock); hv_vhca->agents[type] = agent;mutex_unlock (&hv_vhca->agents_lock);
drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h +4 -2
@@ -43,7 +43,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,void (*invalidate)(struct mlx5_hv_vhca_agent *,u64 block_mask), void (*cleanup)(struct mlx5_hv_vhca_agent *agent), - void *context); + void *context, + struct mlx5_hv_vhca_agent **ctx_update);void mlx5_hv_vhca_agent_destroy (struct mlx5_hv_vhca_agent *agent); int mlx5_hv_vhca_agent_write(struct mlx5_hv_vhca_agent *agent,
@@ -84,7 +85,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,void (*invalidate)(struct mlx5_hv_vhca_agent *,u64 block_mask), void (*cleanup)(struct mlx5_hv_vhca_agent *agent), - void *context) + void *context, + struct mlx5_hv_vhca_agent **ctx_update) { return NULL; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 856/1266 net: microchip: vcap: fix races on the shared Super VCAP block
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jens Emil Schulz Østergaard <[email protected]>
[ Upstream commit d7a8d500d7e42837bd8dce40cb52c97c6e8706a9 ]
The VCAP instances on a chip are not independent, yet they are locked independently. On sparx5 and lan969x the IS0 and IS2 instances are backed by the same Super VCAP hardware block and share its cache and command registers: every access drives the shared VCAP_SUPER_CTRL register and moves data through the shared cache registers.
Accessing one instance therefore races with accessing another. The per-instance admin->lock cannot prevent this, as each instance takes a different lock.
The locking issue is mostly disguised by the fact that the core usage of the vcap api runs under rtnl. However, the full rule dump in debugfs decodes rules straight from hardware (a READ command followed by a cache read) and runs outside rtnl, so it races a concurrent tc-flower rule write to another Super VCAP instance.
Besides corrupting the dump, the read repopulates the shared cache between the writers cache fill and its write command, so the writer commits the wrong data and corrupts the hardware entry.
Introduce vcap_lock() and vcap_unlock() helpers and route every rule lock site in the VCAP API and its debugfs code through them. Replace the per-instance admin->lock with a single mutex in struct vcap_control that serializes access to all instances. The helpers reach it through a new admin->vctrl back-pointer, and the clients initialise and destroy the control lock instead of a per-instance one.
No path holds more than one instance lock, so collapsing them onto a single mutex cannot self-deadlock.
--- .../microchip/lan966x/lan966x_vcap_impl.c | 5 +- .../microchip/sparx5/sparx5_vcap_impl.c | 5 +- .../net/ethernet/microchip/vcap/vcap_api.c | 72 +++++++++++-------- .../net/ethernet/microchip/vcap/vcap_api.h | 3 +- .../microchip/vcap/vcap_api_debugfs.c | 8 +-- .../microchip/vcap/vcap_api_debugfs_kunit.c | 3 +- .../ethernet/microchip/vcap/vcap_api_kunit.c | 3 +- .../microchip/vcap/vcap_api_private.h | 3 + 8 files changed, 60 insertions(+), 42 deletions(-)
drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c +3 -2
@@ -601,7 +601,6 @@ static void lan966x_vcap_admin_free(struct vcap_admin *admin)kfree (admin->cache.keystream);kfree (admin->cache.maskstream);kfree (admin->cache.actionstream); -mutex_destroy (&admin->lock);kfree (admin); }
@@ -615,7 +614,7 @@ lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl,if (!admin)return ERR_PTR (-ENOMEM ); -mutex_init (&admin->lock); + admin->vctrl = ctrl;INIT_LIST_HEAD (&admin->list);INIT_LIST_HEAD (&admin->rules);INIT_LIST_HEAD (&admin->enabled);
@@ -721,6 +720,7 @@ int lan966x_vcap_init(struct lan966x *lan966x) ctrl->ops = &lan966x_vcap_ops;INIT_LIST_HEAD (&ctrl->list); +mutex_init (&ctrl->lock);for (int i =0 ; i <ARRAY_SIZE (lan966x_vcap_inst_cfg); ++i) { cfg = &lan966x_vcap_inst_cfg[i];
@@ -780,5 +780,6 @@ void lan966x_vcap_deinit(struct lan966x *lan966x)lan966x_vcap_admin_free (admin); } +mutex_destroy (&ctrl->lock);kfree (ctrl); }
drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +3 -2
@@ -1941,7 +1941,6 @@ static void sparx5_vcap_admin_free(struct vcap_admin *admin) {if (!admin)return ; -mutex_destroy (&admin->lock);kfree (admin->cache.keystream);kfree (admin->cache.maskstream);kfree (admin->cache.actionstream);
@@ -1961,7 +1960,7 @@ sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl,INIT_LIST_HEAD (&admin->list);INIT_LIST_HEAD (&admin->rules);INIT_LIST_HEAD (&admin->enabled); -mutex_init (&admin->lock); + admin->vctrl = ctrl; admin->vtype = cfg->vtype; admin->vinst = cfg->vinst; admin->ingress = cfg->ingress;
@@ -2069,6 +2068,7 @@ int sparx5_vcap_init(struct sparx5 *sparx5) ctrl->ops = &sparx5_vcap_ops;INIT_LIST_HEAD (&ctrl->list); +mutex_init (&ctrl->lock);for (idx =0 ; idx <ARRAY_SIZE (sparx5_vcap_inst_cfg); ++idx) { cfg = &sparx5_vcap_inst_cfg[idx]; admin =sparx5_vcap_admin_alloc (sparx5, ctrl, cfg);
@@ -2107,5 +2107,6 @@ void sparx5_vcap_destroy(struct sparx5 *sparx5)list_del (&admin->list);sparx5_vcap_admin_free (admin); } +mutex_destroy (&ctrl->lock);kfree (ctrl); }
drivers/net/ethernet/microchip/vcap/vcap_api.c +41 -31
235 lines not on this page · raw message
drivers/net/ethernet/microchip/vcap/vcap_api.h +2 -1
@@ -164,7 +164,7 @@ struct vcap_admin {struct list_head list;/* for insertion in vcap_control */ struct list_head rules;/* list of rules */ struct list_head enabled;/* list of enabled ports */ -struct mutex lock;/* control access to rules */ +struct vcap_control *vctrl;/* the control instance owning this vcap */ enum vcap_type vtype;/* type of vcap */ int vinst;/* instance number within the same type */ int first_cid;/* first chain id in this vcap */
@@ -275,6 +275,7 @@ struct vcap_control {const struct vcap_info *vcaps;/* client supplied vcap models */ const struct vcap_statistics *stats;/* client supplied vcap stats */ struct list_head list;/* list of vcap instances */ + struct mutex lock; /* serialize access to all vcap instances */ };#endif /* __VCAP_API__ */
drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c +4 -4
@@ -410,9 +410,9 @@ static int vcap_debugfs_show(struct seq_file *m, void *unused) };int ret; -mutex_lock (&info->admin->lock); +vcap_lock (info->admin); ret =vcap_show_admin (info->vctrl, info->admin, &out); -mutex_unlock (&info->admin->lock); +vcap_unlock (info->admin);return ret; }DEFINE_SHOW_ATTRIBUTE (vcap_debugfs);
@@ -427,9 +427,9 @@ static int vcap_raw_debugfs_show(struct seq_file *m, void *unused) };int ret; -mutex_lock (&info->admin->lock); +vcap_lock (info->admin); ret =vcap_show_admin_raw (info->vctrl, info->admin, &out); -mutex_unlock (&info->admin->lock); +vcap_unlock (info->admin);return ret; }DEFINE_SHOW_ATTRIBUTE (vcap_raw_debugfs);
drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c +2 -1
@@ -243,10 +243,11 @@ static void vcap_test_api_init(struct vcap_admin *admin) {/* Initialize the shared objects */ INIT_LIST_HEAD (&test_vctrl.list); +mutex_init (&test_vctrl.lock);INIT_LIST_HEAD (&admin->list);INIT_LIST_HEAD (&admin->rules);INIT_LIST_HEAD (&admin->enabled); -mutex_init (&admin->lock); + admin->vctrl = &test_vctrl;list_add_tail (&admin->list, &test_vctrl.list);memset (test_updateaddr,0 ,sizeof (test_updateaddr)); test_updateaddridx =0 ;
drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +2 -1
@@ -233,10 +233,11 @@ static void vcap_test_api_init(struct vcap_admin *admin) {/* Initialize the shared objects */ INIT_LIST_HEAD (&test_vctrl.list); +mutex_init (&test_vctrl.lock);INIT_LIST_HEAD (&admin->list);INIT_LIST_HEAD (&admin->rules);INIT_LIST_HEAD (&admin->enabled); -mutex_init (&admin->lock); + admin->vctrl = &test_vctrl;list_add_tail (&admin->list, &test_vctrl.list);memset (test_updateaddr,0 ,sizeof (test_updateaddr)); test_updateaddridx =0 ;
drivers/net/ethernet/microchip/vcap/vcap_api_private.h +3 -0
@@ -50,6 +50,9 @@ struct vcap_stream_iter {/* Check that the control has a valid set of callbacks */ int vcap_api_check (struct vcap_control *ctrl); +/* Serialize access to the vcap instances of a control */ +void vcap_lock (struct vcap_admin *admin); +void vcap_unlock (struct vcap_admin *admin);/* Erase the VCAP cache area used or encoding and decoding */ void vcap_erase_cache (struct vcap_rule_internal *ri);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 857/1266 qede: fix off-by-one in BD ring consumption on build_skb failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shigeru Yoshida <[email protected]>
[ Upstream commit a0a558ca7e75b49e71f8c545c30e8c005e6e4e2f ]
qede_rx_build_skb() and qede_tpa_rx_build_skb() do not check for a NULL return from qede_build_skb(). When it returns NULL under memory pressure, the functions still consume a BD from the ring before returning NULL. The callers then recycle additional BDs, resulting in one extra BD being consumed (off-by-one). This desynchronizes the BD ring, which can corrupt DMA page reference counts and lead to SLUB freelist corruption.
Commit 4e910dbe3650 ("qede: confirm skb is allocated before using") added a NULL check inside qede_build_skb() to prevent a NULL pointer dereference, but did not address the missing NULL checks in the callers, making this off-by-one reachable.
Fix this by adding NULL checks for the return value of qede_build_skb() in both qede_rx_build_skb() and qede_tpa_rx_build_skb(), returning NULL immediately before any BD ring manipulation.
--- drivers/net/ethernet/qlogic/qede/qede_fp.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/net/ethernet/qlogic/qede/qede_fp.c +5 -0
@@ -765,6 +765,9 @@ qede_tpa_rx_build_skb(struct qede_dev *edev,struct sk_buff *skb; skb =qede_build_skb (rxq, bd, len, pad); +if (unlikely (!skb)) +return NULL ; + bd->page_offset += rxq->rx_buf_seg_size;if (bd->page_offset ==PAGE_SIZE ) {
@@ -812,6 +815,8 @@ qede_rx_build_skb(struct qede_dev *edev, } skb =qede_build_skb (rxq, bd, len, pad); +if (unlikely (!skb)) +return NULL ;if (unlikely (qede_realloc_rx_buffer (rxq, bd))) {/* Incr page ref count to reuse on allocation failure so
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 858/1266 net: qualcomm: rmnet: validate MAP frame length before ingress parsing
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
[ Upstream commit f0f1887a9e30712a1df03e152dce6fb91344b1f3 ]
When ingress deaggregation is disabled, rmnet_map_ingress_handler() passes the skb straight to __rmnet_map_ingress_handler(), skipping the length validation that rmnet_map_deaggregate() performs on the aggregated path. The parser then dereferences the MAP header and csum header/trailer based on the on-wire pkt_len without checking skb->len, so a short frame is read out of bounds:
BUG: KASAN: slab-out-of-bounds in rmnet_map_checksum_downlink_packet Read of size 1 at addr ffff88801118ed00 by task exploit/147 Call Trace: ... rmnet_map_checksum_downlink_packet (drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c:413) __rmnet_map_ingress_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:96) rmnet_rx_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:129) __netif_receive_skb_core.constprop.0 (net/core/dev.c:6089) netif_receive_skb (net/core/dev.c:6460) tun_get_user (drivers/net/tun.c:1955) tun_chr_write_iter (drivers/net/tun.c:2001) vfs_write (fs/read_write.c:688) ksys_write (fs/read_write.c:740) do_syscall_64 (arch/x86/entry/syscall_64.c:94) ...
Factor that validation out of rmnet_map_deaggregate() into rmnet_map_validate_packet_len() and run it on the no-aggregation path too. The MAP header is bounds-checked first, since this path can receive a frame shorter than the header.
--- .../ethernet/qualcomm/rmnet/rmnet_handlers.c | 5 +- .../net/ethernet/qualcomm/rmnet/rmnet_map.h | 1 + .../ethernet/qualcomm/rmnet/rmnet_map_data.c | 72 ++++++++++--------- 3 files changed, 45 insertions(+), 33 deletions(-)
drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c +4 -1
@@ -126,7 +126,10 @@ rmnet_map_ingress_handler(struct sk_buff *skb,consume_skb (skb); }else { -__rmnet_map_ingress_handler (skb, port); +if (rmnet_map_validate_packet_len (skb, port)) +__rmnet_map_ingress_handler (skb, port); +else +kfree_skb (skb); } }
drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h +1 -0
@@ -59,5 +59,6 @@ void rmnet_map_tx_aggregate_init(struct rmnet_port *port);void rmnet_map_tx_aggregate_exit (struct rmnet_port *port);void rmnet_map_update_ul_agg_config (struct rmnet_port *port,u32 size,u32 count,u32 time); +u32 rmnet_map_validate_packet_len (struct sk_buff *skb,struct rmnet_port *port);#endif /* _RMNET_MAP_H_ */
drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +40 -32
94 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 859/1266 net/sched: act_pedit: fix TOCTOU heap OOB write in tc offload
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 8b519cbcabe836a441369fbec1a8a6518a709251 ]
There is a TOCTOU race condition in flower lockless approach between sizing a flow_rule buffer and filling it. [email protected] reports:
The cls_flower classifier operates with TCF_PROTO_OPS_DOIT_UNLOCKED (fl_change runs without RTNL), while RTM_NEWACTION holds RTNL, so the independent locking domains make the race reachable in practice. KASAN confirms:
BUG: KASAN: slab-out-of-bounds in tcf_pedit_offload_act_setup+0x81b/0x930 Write of size 4 at addr ffff888001f27520 by task poc-toctou/312 The buggy address is located 0 bytes to the right of allocated 288-byte region [ffff888001f27400, ffff888001f27520) (cache kmalloc-512)
Note: The result is a heap OOB write attacker-controlled content into the adjacent slab object (requires CAP_NET_ADMIN).
The fix introduces reading tcfp_nkeys under act->tcfa_lock in all places using a new tcf_pedit_nkeys_locked() which replaces the old tcf_pedit_nkeys(). Additionally we close the remaining TOCTOU window between the sizing read and the fill reads by more careful accounting.
Rather than silently truncating the key count, which leads to incorrect action semantics offloaded to hardware and secondary OOB writes if the remaining capacity is zero or consumed by prior actions, we enforce remaining capacity checks and return -ENOSPC if the required space exceeds the remaining capacity.
--- include/net/tc_act/tc_pedit.h | 18 ++++++++---------- net/sched/act_api.c | 13 +++++++++---- net/sched/act_pedit.c | 13 +++++++++++-- net/sched/cls_api.c | 22 +++++++++++++++++----- 4 files changed, 45 insertions(+), 21 deletions(-)
include/net/tc_act/tc_pedit.h +8 -10
@@ -36,17 +36,15 @@ static inline bool is_tcf_pedit(const struct tc_action *a)return false; } -static inline int tcf_pedit_nkeys (const struct tc_action *a) +/* Must be called with act->tcfa_lock held to ensure consistency of parallel +* reads of the same action's pedit keys (e.g. flow_offload count vs fill). +* Note, this is only used for pedit offload. +*/ +static inline int tcf_pedit_nkeys_locked (const struct tc_action *a) { -struct tcf_pedit_parms *parms; -int nkeys; - -rcu_read_lock (); - parms =to_pedit_parms (a); - nkeys = parms->tcfp_nkeys; -rcu_read_unlock (); - -return nkeys; +lockdep_assert_held (&a->tcfa_lock); +return rcu_dereference_protected (to_pedit (a)->parms, +lockdep_is_held (&a->tcfa_lock))->tcfp_nkeys; }static inline u32 tcf_pedit_htype (const struct tc_action *a,int index)
net/sched/act_api.c +9 -4
@@ -148,10 +148,15 @@ static void offload_action_hw_count_dec(struct tc_action *act,static unsigned int tcf_offload_act_num_actions_single (struct tc_action *act) { -if (is_tcf_pedit (act)) -return tcf_pedit_nkeys (act); -else -return 1 ; +unsigned int count; + +if (is_tcf_pedit (act)) { +spin_lock_bh (&act->tcfa_lock); + count =tcf_pedit_nkeys_locked (act); +spin_unlock_bh (&act->tcfa_lock); +return count; + } +return 1 ; }static bool tc_act_skip_hw (u32 flags)
net/sched/act_pedit.c +11 -2
@@ -563,9 +563,18 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data, {if (bind) {struct flow_action_entry *entry = entry_data; +int nkeys =tcf_pedit_nkeys_locked (act);int k; -for (k =0 ; k <tcf_pedit_nkeys (act); k++) { +/* If the required keys exceed the remaining capacity return +* -ENOSPC to abort the offload and fallback to software. +*/ +if (nkeys > *index_inc) { +NL_SET_ERR_MSG_MOD (extack,"Not enough space to offload all pedit keys" ); +return -ENOSPC ; + } + +for (k =0 ; k < nkeys; k++) {switch (tcf_pedit_cmd (act, k)) {case TCA_PEDIT_KEY_EX_CMD_SET : entry->id =FLOW_ACTION_MANGLE ;
@@ -602,7 +611,7 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data,return -EOPNOTSUPP ; } -for (k =1 ; k <tcf_pedit_nkeys (act); k++) { +for (k =1 ; k <tcf_pedit_nkeys_locked (act); k++) {if (cmd !=tcf_pedit_cmd (act, k)) {NL_SET_ERR_MSG_MOD (extack,"Unsupported pedit command offload" );return -EOPNOTSUPP ;
net/sched/cls_api.c +17 -5
@@ -3808,12 +3808,21 @@ int tc_setup_action(struct flow_action *flow_action, entry = &flow_action->entries[j];spin_lock_bh (&act->tcfa_lock); + +/* Abort the offload if we have exhausted the allocated capacity */ +if (j >= flow_action->num_entries) { +NL_SET_ERR_MSG_MOD (extack,"Flow action buffer overflow" ); + err = -ENOSPC ; + goto err_out_locked; + } + err =tcf_act_get_user_cookie (entry, act);if (err) goto err_out_locked; - index =0 ; - err =tc_setup_offload_act (act, entry, &index, extack); + index = flow_action->num_entries - j; + err =tc_setup_offload_act (act, entry, &index, + extack);if (err) goto err_out_locked;
@@ -3867,10 +3876,13 @@ unsigned int tcf_exts_num_actions(struct tcf_exts *exts)int i;tcf_exts_for_each_action (i, act, exts) { -if (is_tcf_pedit (act)) - num_acts +=tcf_pedit_nkeys (act); -else +if (is_tcf_pedit (act)) { +spin_lock_bh (&act->tcfa_lock); + num_acts +=tcf_pedit_nkeys_locked (act); +spin_unlock_bh (&act->tcfa_lock); + }else { num_acts++; + } }return num_acts; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 860/1266 net/smc: fix UAF in smc_cdc_rx_handler() by pinning the socket
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
[ Upstream commit 9d160b35cc34a2ba8229d07651468a7848325135 ]
smc_cdc_rx_handler() looks up the connection by token under the link group's conns_lock, drops the lock, and then dereferences conn and the smc_sock derived from it, ending in sock_hold(&smc->sk) inside smc_cdc_msg_recv(). No reference is held across the lock release.
The only reference pinning the socket while the connection is discoverable in the link group is taken in smc_lgr_register_conn() (sock_hold) and dropped in __smc_lgr_unregister_conn() (sock_put), both under conns_lock. Once the handler drops conns_lock, a concurrent close() -> smc_release() -> smc_conn_free() -> smc_lgr_unregister_conn() can drop that reference and free the smc_sock, so the handler's later sock_hold() runs on freed memory:
WARNING: lib/refcount.c:25 at refcount_warn_saturate Workqueue: rxe_wq do_work refcount_warn_saturate (lib/refcount.c:25) smc_cdc_msg_recv (net/smc/smc_cdc.c:430) smc_cdc_rx_handler (net/smc/smc_cdc.c:502) smc_wr_rx_tasklet_fn (net/smc/smc_wr.c:445) tasklet_action_common (kernel/softirq.c:938) handle_softirqs (kernel/softirq.c:622) Kernel panic - not syncing: panic_on_warn set
Only SMC-R is affected. The SMC-D receive tasklet is stopped by tasklet_kill(&conn->rx_tsklet) in smc_conn_free() before the connection is unregistered, so it cannot run concurrently with the free.
Take the socket reference while still holding conns_lock, so the registration reference can no longer be the last one, and drop it once the handler is done.
--- net/smc/smc_cdc.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-)
net/smc/smc_cdc.c +10 -5
@@ -438,9 +438,9 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf) {struct smc_link *link = (struct smc_link *)wc->qp->qp_context;struct smc_cdc_msg *cdc = buf; +struct smc_sock *smc =NULL ;struct smc_connection *conn;struct smc_link_group *lgr; -struct smc_sock *smc;if (wc->byte_len < offsetof(struct smc_cdc_msg , reserved))return ;/* short message */
@@ -451,21 +451,26 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf) lgr =smc_get_lgr (link);read_lock_bh (&lgr->conns_lock); conn =smc_lgr_find_conn (ntohl (cdc->token), lgr); -read_unlock_bh (&lgr->conns_lock); -if (!conn || conn->out_of_sync) +if (!conn || conn->out_of_sync) { +read_unlock_bh (&lgr->conns_lock);return ; + } smc = container_of(conn, struct smc_sock, conn); +sock_hold (&smc->sk); +read_unlock_bh (&lgr->conns_lock);if (cdc->prod_flags.failover_validation) {smc_cdc_msg_validate (smc, cdc, link); -return ; + goto out; }if (smc_cdc_before (ntohs (cdc->seqno), conn->local_rx_ctrl.seqno))/* received seqno is old */ -return ; + goto out;smc_cdc_msg_recv (smc, cdc); +out: +sock_put (&smc->sk); }static struct smc_wr_rx_handler smc_cdc_rx_handlers[] = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 861/1266 amt: fix size calculation in amt_get_size()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 9e05e91a9a847ed57926414bd7c2c5e54d6c56c6 ]
amt_get_size() incorrectly used sizeof(struct iphdr) for the sizes of IFLA_AMT_DISCOVERY_IP, IFLA_AMT_REMOTE_IP, and IFLA_AMT_LOCAL_IP. These attributes contain IPv4 addresses (__be32), not full IP headers.
Replace sizeof(struct iphdr) with sizeof(__be32) to avoid over-allocating netlink message space.
--- drivers/net/amt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/net/amt.c +3 -3
@@ -3303,9 +3303,9 @@ static size_t amt_get_size(const struct net_device *dev)nla_total_size (sizeof (__u16)) +/* IFLA_AMT_GATEWAY_PORT */ nla_total_size (sizeof (__u32)) +/* IFLA_AMT_LINK */ nla_total_size (sizeof (__u32)) +/* IFLA_MAX_TUNNELS */ -nla_total_size (sizeof (struct iphdr )) +/* IFLA_AMT_DISCOVERY_IP */ -nla_total_size (sizeof (struct iphdr )) +/* IFLA_AMT_REMOTE_IP */ -nla_total_size (sizeof (struct iphdr ));/* IFLA_AMT_LOCAL_IP */ +nla_total_size (sizeof (__be32)) +/* IFLA_AMT_DISCOVERY_IP */ +nla_total_size (sizeof (__be32)) +/* IFLA_AMT_REMOTE_IP */ +nla_total_size (sizeof (__be32));/* IFLA_AMT_LOCAL_IP */ }static int amt_fill_info (struct sk_buff *skb,const struct net_device *dev)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 862/1266 Bluetooth: 6lowpan: hold L2CAP conn across debugfs control
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang <[email protected]>
[ Upstream commit 518aa9505fa10ea5662349e5d2efd8c9e32a820b ]
get_l2cap_conn() looks up an LE hci_conn under hdev protection, but then drops that protection before reading hcon->l2cap_data and before lowpan_control_write() later dereferences conn->hcon. A disconnect or device close can tear down the same L2CAP connection in that window.
The buggy scenario involves two paths, with each column showing the order within that path:
6LoWPAN control write: HCI disconnect/device close:
1. get_l2cap_conn() finds hcon 1. hci_disconn_cfm() dispatches
and hcon->l2cap_data. the L2CAP disconnect callback.
2. get_l2cap_conn() drops hdev 2. l2cap_conn_del() clears
protection and returns conn. hcon->l2cap_data and drops the
L2CAP connection reference.
3. lowpan_control_write() reads 3. hci_conn_del() removes and drops
conn->hcon. the HCI connection.Take a reference to the L2CAP connection with l2cap_conn_hold_unless_zero() while hdev is still locked, and drop that reference after the debugfs command's last use of conn. This mirrors the existing L2CAP ACL receive-side handoff and keeps the connection dereferenceable after leaving hdev protection. Export the existing helper so the bluetooth_6lowpan module can use the same lifetime primitive.
Validation reproduced this kernel report:
BUG: KASAN: slab-use-after-free in lowpan_control_write+0x374/0x520 The buggy address belongs to the object at ffff888111b9d000 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 0 bytes inside of freed 1024-byte region [ffff888111b9d000, ffff888111b9d400) Read of size 8 Call trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x5f0 lowpan_control_write+0x374/0x520 (net/bluetooth/6lowpan.c:1131) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x19f/0x330 kasan_report+0xe0/0x110 __debugfs_file_get+0xf7/0x400 full_proxy_write+0x9e/0xd0 vfs_write+0x1b0/0x810 ksys_write+0xd2/0x170 dnotify_flush+0x32/0x220 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 __kasan_kmalloc+0xaa/0xb0 l2cap_conn_add+0x45/0x520 l2cap_chan_connect+0xac6/0xd90 l2cap_sock_connect+0x216/0x350 __sys_connect+0x101/0x130 __x64_sys_connect+0x40/0x50 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 hci_conn_hash_flush+0xc0/0x140 hci_dev_close_sync+0x41a/0xb00 hci_dev_close+0x12f/0x160 hci_sock_ioctl+0x157/0x570 sock_do_ioctl+0xf7/0x210 sock_ioctl+0x32f/0x490 __x64_sys_ioctl+0xc7/0x110 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f kasan_record_aux_stack+0xa7/0xc0 insert_work+0x32/0x100 __queue_work+0x262/0xa60 queue_work_on+0xad/0xb0 l2cap_connect_cfm+0x4ef/0x670 hci_le_remote_feat_complete_evt+0x247/0x430 hci_event_packet+0x360/0x6f0 hci_rx_work+0x2ae/0x7a0 process_one_work+0x4fd/0xbc0 worker_thread+0x2d8/0x570 kthread+0x1ad/0x1f0 ret_from_fork+0x3c9/0x540 ret_from_fork_asm+0x1a/0x30
--- net/bluetooth/6lowpan.c | 21 +++++++++++++++------ net/bluetooth/l2cap_core.c | 1 + 2 files changed, 16 insertions(+), 6 deletions(-)
net/bluetooth/6lowpan.c +15 -6
@@ -1006,16 +1006,19 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,hci_dev_lock (hdev); hcon =hci_conn_hash_lookup_le (hdev, addr, le_addr_type); -hci_dev_unlock (hdev); -hci_dev_put (hdev); - -if (!hcon) +if (!hcon) { +hci_dev_unlock (hdev); +hci_dev_put (hdev);return -ENOENT ; + } - *conn = (struct l2cap_conn *)hcon->l2cap_data; + *conn =l2cap_conn_hold_unless_zero (hcon->l2cap_data);BT_DBG ("conn %p dst %pMR type %u" , *conn, &hcon->dst, hcon->dst_type); +hci_dev_unlock (hdev); +hci_dev_put (hdev); +return 0 ; }
@@ -1158,18 +1161,22 @@ static ssize_t lowpan_control_write(struct file *fp,if (conn) {struct lowpan_peer *peer; -if (!is_bt_6lowpan (conn->hcon)) +if (!is_bt_6lowpan (conn->hcon)) { +l2cap_conn_put (conn);return -EINVAL ; + } peer =lookup_peer (conn);if (peer) {BT_DBG ("6LoWPAN connection already exists" ); +l2cap_conn_put (conn);return -EALREADY ; }BT_DBG ("conn %p dst %pMR type %d user %u" , conn, &conn->hcon->dst, conn->hcon->dst_type, addr_type); +l2cap_conn_put (conn); } ret =bt_6lowpan_connect (&addr, addr_type);
@@ -1185,6 +1192,8 @@ static ssize_t lowpan_control_write(struct file *fp,return ret; ret =bt_6lowpan_disconnect (conn, addr_type); +if (conn) +l2cap_conn_put (conn);if (ret <0 )return ret;
net/bluetooth/l2cap_core.c +1 -0
@@ -7645,6 +7645,7 @@ struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c)return c; } +EXPORT_SYMBOL (l2cap_conn_hold_unless_zero);void l2cap_recv_acldata (struct hci_conn *hcon,struct sk_buff *skb,u16 flags) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 863/1266 Bluetooth: MGMT: Fix adv monitor add failure cleanup
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang <[email protected]>
[ Upstream commit 384a4b2fef9ffe5e270ee5558975c0504881c5fb ]
hci_add_adv_monitor() publishes a new adv_monitor in hdev->adv_monitors_idr before the powered MSFT setup step. The MSFT offload add path can then fail either locally before the controller add command completes, or in the MSFT add callback. In the current queued management add flow, hci_cmd_sync_work() still invokes mgmt_add_adv_patterns_monitor_complete() with the original pending command after msft_add_monitor_pattern() returns.
The buggy scenario involves two paths, with each column showing the order within that path:
MSFT add handling MGMT completion 1. insert monitor and handle 1. receive sync error 2. send MSFT add command 2. call add-monitor completion 3. callback sees bad response 3. load cmd->user_data 4. callback frees monitor 4. read monitor->handle
Local MSFT setup failures have the other half of the same ownership bug:
they return an error after the IDR insertion, but no later code removes the failed monitor from the IDR.
Keep ownership with the pending management command until its completion. For normal management adds, the MSFT add callback now records successful controller state and returns errors to its caller. The management completion frees the monitor on non-success after copying the response handle, while resume/reregister callback-error cleanup remains in the MSFT callback. The success path keeps the existing bookkeeping.
Validation reproduced this kernel report:
BUG: KASAN: slab-use-after-free in mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] Call Trace: <TASK> dump_stack_lvl+0x66/0xa0 print_report+0xce/0x5f0 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x19f/0x330 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] kasan_report+0xe0/0x110 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 ? 0xffffffffc00d00da ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] ? hci_cmd_sync_work+0x1ab/0x210 [bluetooth] hci_cmd_sync_work+0x1c0/0x210 [bluetooth] ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] process_one_work+0x4fd/0xbc0 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_hci_cmd_sync_work+0x10/0x10 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x2d8/0x570 ? __pfx_worker_thread+0x10/0x10 kthread+0x1ad/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x3c9/0x540 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x2e9/0x730 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 </TASK> Allocated by task 471 on cpu 3 at 285.205389s: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 __kasan_kmalloc+0xaa/0xb0 add_adv_patterns_monitor_rssi+0xd5/0x230 [bluetooth] hci_sock_sendmsg+0x96b/0xf80 [bluetooth] __sys_sendto+0x2bc/0x2d0 __x64_sys_sendto+0x76/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 454 on cpu 2 at 285.217112s: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 msft_add_monitor_sync+0x54a/0x570 [bluetooth] hci_add_adv_monitor+0x133/0x180 [bluetooth] hci_cmd_sync_work+0x187/0x210 [bluetooth] process_one_work+0x4fd/0xbc0 worker_thread+0x2d8/0x570 kthread+0x1ad/0x1f0 ret_from_fork+0x3c9/0x540 ret_from_fork_asm+0x1a/0x30
--- net/bluetooth/mgmt.c | 2 ++ net/bluetooth/msft.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-)
net/bluetooth/mgmt.c +2 -0
@@ -5347,6 +5347,8 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,if (monitor->state ==ADV_MONITOR_STATE_NOT_REGISTERED ) monitor->state =ADV_MONITOR_STATE_REGISTERED ;hci_update_passive_scan (hdev); + }else { +hci_free_adv_monitor (hdev, monitor); } mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode,
net/bluetooth/msft.c +1 -1
@@ -292,7 +292,7 @@ static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode, monitor->state =ADV_MONITOR_STATE_OFFLOADED ; unlock: -if (status) +if (status && msft->resuming)hci_free_adv_monitor (hdev, monitor);hci_dev_unlock (hdev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 864/1266 Bluetooth: ISO: exclude RFU bits from ISO_SDU_Length
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <[email protected]>
[ Upstream commit fd076d8deeab6f9f18ef13400f89e1f550df665b ]
slen contains ISO_SDU_Length (12 bits), RFU (2 bits), Packet_Status_Flags (2 bits).
Exclude the RFU bits from hci_iso_data_len. Also add masks to the pack macro.
--- include/net/bluetooth/hci.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
include/net/bluetooth/hci.h +3 -2
@@ -2914,8 +2914,9 @@ static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb)#define hci_iso_flags_pack (pb, ts) ((pb & 0x03) | ((ts & 0x01) << 2))/* ISO data length and flags pack/unpack */ -#define hci_iso_data_len_pack (h, f) ((__u16) ((h) | ((f) << 14))) -#define hci_iso_data_len (h) ((h) & 0x3fff) +#define hci_iso_data_len_pack (h, f) ((__u16) (((h) & 0x0fff) | \ + (((f) & 0x3) << 14))) +#define hci_iso_data_len (h) ((h) & 0x0fff)#define hci_iso_data_flags (h) ((h) >> 14)/* codec transport types */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 865/1266 Bluetooth: bpa10x: avoid OOB read of revision string in bpa10x_setup()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit dd068ef044128db655f48323a4acfd5907e04903 ]
bpa10x_setup() sends the vendor command 0xfc0e and passes the response to bt_dev_info() and hci_set_fw_info() as a "%s" string starting at skb->data + 1, without checking the length:
bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); hci_set_fw_info(hdev, "%s", skb->data + 1);
A device that returns a one-byte response (status only) leaves skb->data + 1 past the end of the data, and the %s walk reads adjacent slab memory until it meets a NUL. The same happens when the payload is not NUL-terminated within skb->len. The out-of-bounds bytes end up in the kernel log and the firmware-info debugfs file.
Print the revision string with a bounded "%.*s" limited to skb->len - 1 instead. This keeps the string readable for well-behaved devices while never reading past the received data, and does not fail setup, so a device returning a short or unterminated response keeps working.
--- drivers/bluetooth/bpa10x.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/bluetooth/bpa10x.c +6 -2
@@ -254,9 +254,13 @@ static int bpa10x_setup(struct hci_dev *hdev)if (IS_ERR (skb))return PTR_ERR (skb); -bt_dev_info (hdev,"%s" , (char *)(skb->data +1 )); +/* Bounded print: the device controls skb->len. */ +if (skb->len >1 ) { +int len = skb->len -1 ; -hci_set_fw_info (hdev,"%s" , skb->data +1 ); +bt_dev_info (hdev,"%.*s" , len, (char *)(skb->data +1 )); +hci_set_fw_info (hdev,"%.*s" , len, skb->data +1 ); + }kfree_skb (skb);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 866/1266 ring-buffer: Fix event length with forced 8-byte alignment
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Wang <[email protected]>
[ Upstream commit c37e0a4b79a6bbb96ce5ffe279d7c001e20529e0 ]
When RB_FORCE_8BYTE_ALIGNMENT is true, rb_calculate_event_length() reserves the space of event->array[0] for placing the data length and rb_update_event() stores the data length in event->array[0] accordingly. As a result the whole event length will add extra 4 bytes for sizeof(event.array[0]) unconditionally.
But ring_buffer_event_length() only subtracts the sizeof(event->array[0]) for events larger than RB_MAX_SMALL_DATA + sizeof(event->array[0]). As a result, small events on architectures with RB_FORCE_8BYTE_ALIGNMENT=true report a data length that is 4 bytes larger than expected.
To fix it, add the RB_FORCE_8BYTE_ALIGNMENT as a condition to subtract the size of that length field whenever RB_FORCE_8BYTE_ALIGNMENT is true.
This issue is observed in a riscv64 kernel with CONFIG_HAVE_64BIT_ALIGNED_ACCESS set to y, when we run ftrace selftest trace_marker_raw.tc, we get the weird log: for cases where the id is 1..100, the number of data field is 8*N, but once id exceeds 100, the number of data field becomes 8*N+4:
# 1 buf: 58 00 00 00 80 5e d1 63 (number of data field is 8*1)
...
# a buf: 58 ... (number of data field is 8*2) ... # 64 buf: 58 ... (number of data field is 8*13) # 65 buf: 58 ... (number of data field is 8*13+4)
After applying this change, the number of data field keeps being 8*N+4 consistently.
--- kernel/trace/ring_buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/trace/ring_buffer.c +2 -1
@@ -255,7 +255,8 @@ unsigned ring_buffer_event_length(struct ring_buffer_event *event)if (event->type_len >RINGBUF_TYPE_DATA_TYPE_LEN_MAX )return length; length -=RB_EVNT_HDR_SIZE ; -if (length >RB_MAX_SMALL_DATA +sizeof (event->array[0 ])) +if (length >RB_MAX_SMALL_DATA +sizeof (event->array[0 ]) || +RB_FORCE_8BYTE_ALIGNMENT ) length -=sizeof (event->array[0 ]);return length; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 867/1266 net/tls: Consume empty data records in tls_sw_read_sock()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 3be28e2c9cd0230cb51fd4967df095273afd3848 ]
A peer may send a zero-length TLS application_data record; TLS 1.3 explicitly permits these as a traffic-analysis countermeasure (RFC 8446, Section 5.1). After decryption such a record has full_len ==
0. tls_sw_read_sock() hands it to the read_actor, which has no payload to consume and returns zero. The loop treats a zero return as backpressure (used <= 0), requeues the skb at the head of rx_list, and stops. rx_list is serviced head-first on the next call, so the empty record is dequeued, fails the same way, and is requeued again; every later record on the connection is blocked behind it.
tls_sw_recvmsg() does not stall on this: a zero-length data record copies nothing and falls through to consume_skb(). Mirror that in the read_sock() path by recognizing an empty data record before the actor runs, consuming it, and continuing.
--- net/tls/tls_sw.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
net/tls/tls_sw.c +11 -0
@@ -2392,6 +2392,17 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, goto read_sock_requeue; } +/* An empty data record (legal in TLS 1.3) gives a zero +* read_actor return, indistinguishable from the consumer +* stalling; the used <= 0 path would requeue it at the +* head of rx_list and block all later records. Consume it +* here instead. +*/ +if (rxm->full_len ==0 ) { +consume_skb (skb); +continue ; + } + used =read_actor (desc, skb, rxm->offset, rxm->full_len);if (used <=0 ) {if (!copied)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 868/1266 net: usb: lan78xx: move functions to avoid forward definitions
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oleksij Rempel <[email protected]>
[ Upstream commit 39aa1d620d10cdd276f4728da50f136dbe939643 ]
Move following functions to avoid forward declarations in the code:
- lan78xx_start_hw()
- lan78xx_stop_hw()
- lan78xx_flush_fifo()
- lan78xx_start_tx_path()
- lan78xx_stop_tx_path()
- lan78xx_flush_tx_fifo()
- lan78xx_start_rx_path()
- lan78xx_stop_rx_path()
- lan78xx_flush_rx_fifo()
These functions will be used in an upcoming PHYlink migration patch.
No modifications to the functionality of the code are made.
Stable-dep-of: 60444706aa17 ("net: usb: lan78xx: disable VLAN filter in promiscuous mode")
--- drivers/net/usb/lan78xx.c | 300 +++++++++++++++++++------------------- 1 file changed, 150 insertions(+), 150 deletions(-)
drivers/net/usb/lan78xx.c +150 -150
@@ -817,6 +817,156 @@ static void lan78xx_update_stats(struct lan78xx_net *dev)usb_autopm_put_interface (dev->intf); } +static int lan78xx_start_hw (struct lan78xx_net *dev,u32 reg,u32 hw_enable) +{ +return lan78xx_update_reg (dev, reg, hw_enable, hw_enable); +} + +static int lan78xx_stop_hw (struct lan78xx_net *dev,u32 reg,u32 hw_enabled, +u32 hw_disabled) +{ +unsigned long timeout; +bool stopped = true; +int ret; +u32 buf; + +/* Stop the h/w block (if not already stopped) */ + + ret =lan78xx_read_reg (dev, reg, &buf); +if (ret <0 ) +return ret; + +if (buf & hw_enabled) { + buf &= ~hw_enabled; + + ret =lan78xx_write_reg (dev, reg, buf); +if (ret <0 ) +return ret; + + stopped = false; + timeout = jiffies +HW_DISABLE_TIMEOUT ; +do { + ret =lan78xx_read_reg (dev, reg, &buf); +if (ret <0 ) +return ret; + +if (buf & hw_disabled) + stopped = true; +else +msleep (HW_DISABLE_DELAY_MS ); + }while (!stopped && !time_after (jiffies, timeout)); + } + + ret = stopped ?0 : -ETIME ; + +return ret; +} + +static int lan78xx_flush_fifo (struct lan78xx_net *dev,u32 reg,u32 fifo_flush) +{ +return lan78xx_update_reg (dev, reg, fifo_flush, fifo_flush); +} + +static int lan78xx_start_tx_path (struct lan78xx_net *dev) +{ +int ret; + +netif_dbg (dev, drv, dev->net,"start tx path" ); + +/* Start the MAC transmitter */ + + ret =lan78xx_start_hw (dev,MAC_TX ,MAC_TX_TXEN_ ); +if (ret <0 ) +return ret; + +/* Start the Tx FIFO */ + + ret =lan78xx_start_hw (dev,FCT_TX_CTL ,FCT_TX_CTL_EN_ ); +if (ret <0 ) +return ret; + +return 0 ; +} + +static int lan78xx_stop_tx_path (struct lan78xx_net *dev) +{ +int ret; + +netif_dbg (dev, drv, dev->net,"stop tx path" ); + +/* Stop the Tx FIFO */ + + ret =lan78xx_stop_hw (dev,FCT_TX_CTL ,FCT_TX_CTL_EN_ ,FCT_TX_CTL_DIS_ ); +if (ret <0 ) +return ret; + +/* Stop the MAC transmitter */ + + ret =lan78xx_stop_hw (dev,MAC_TX ,MAC_TX_TXEN_ ,MAC_TX_TXD_ ); +if (ret <0 ) +return ret; + +return 0 ; +} + +/* The caller must ensure the Tx path is stopped before calling +* lan78xx_flush_tx_fifo(). +*/ +static int lan78xx_flush_tx_fifo (struct lan78xx_net *dev) +{ +return lan78xx_flush_fifo (dev,FCT_TX_CTL ,FCT_TX_CTL_RST_ ); +} + +static int lan78xx_start_rx_path (struct lan78xx_net *dev) +{ +int ret; + +netif_dbg (dev, drv, dev->net,"start rx path" ); + +/* Start the Rx FIFO */ + + ret =lan78xx_start_hw (dev,FCT_RX_CTL ,FCT_RX_CTL_EN_ ); +if (ret <0 ) +return ret; + +/* Start the MAC receiver*/ + + ret =lan78xx_start_hw (dev,MAC_RX ,MAC_RX_RXEN_ ); +if (ret <0 ) +return ret; + +return 0 ; +} + +static int lan78xx_stop_rx_path (struct lan78xx_net *dev) +{ +int ret; + +netif_dbg (dev, drv, dev->net,"stop rx path" ); + +/* Stop the MAC receiver */ + + ret =lan78xx_stop_hw (dev,MAC_RX ,MAC_RX_RXEN_ ,MAC_RX_RXD_ ); +if (ret <0 ) +return ret; + +/* Stop the Rx FIFO */ + + ret =lan78xx_stop_hw (dev,FCT_RX_CTL ,FCT_RX_CTL_EN_ ,FCT_RX_CTL_DIS_ ); +if (ret <0 ) +return ret; + +return 0 ; +} + +/* The caller must ensure the Rx path is stopped before calling +* lan78xx_flush_rx_fifo(). +*/ +static int lan78xx_flush_rx_fifo (struct lan78xx_net *dev) +{ +return lan78xx_flush_fifo (dev,FCT_RX_CTL ,FCT_RX_CTL_RST_ ); +} +/* Loop until the read is completed with timeout called with phy_mutex held */ static int lan78xx_phy_wait_not_busy (struct lan78xx_net *dev) {
@@ -2747,156 +2897,6 @@ static int lan78xx_urb_config_init(struct lan78xx_net *dev)return result; } -static int lan78xx_start_hw (struct lan78xx_net *dev,u32 reg,u32 hw_enable) -{ -return lan78xx_update_reg (dev, reg, hw_enable, hw_enable); -} - -static int lan78xx_stop_hw (struct lan78xx_net *dev,u32 reg,u32 hw_enabled, -u32 hw_disabled) -{ -unsigned long timeout; -bool stopped = true; -int ret; -u32 buf; - -/* Stop the h/w block (if not already stopped) */ - - ret =lan78xx_read_reg (dev, reg, &buf); -if (ret <0 ) -return ret; - -if (buf & hw_enabled) { - buf &= ~hw_enabled; - - ret =lan78xx_write_reg (dev, reg, buf); -if (ret <0 ) -return ret; - - stopped = false; - timeout = jiffies +HW_DISABLE_TIMEOUT ; -do { - ret =lan78xx_read_reg (dev, reg, &buf); -if (ret <0 ) -return ret; - -if (buf & hw_disabled) - stopped = true; -else -msleep (HW_DISABLE_DELAY_MS ); - }while (!stopped && !time_after (jiffies, timeout)); - } - - ret = stopped ?0 : -ETIME ; - -return ret; -} - -static int lan78xx_flush_fifo (struct lan78xx_net *dev,u32 reg,u32 fifo_flush) -{ -return lan78xx_update_reg (dev, reg, fifo_flush, fifo_flush); -} - -static int lan78xx_start_tx_path (struct lan78xx_net *dev) -{ -int ret; - -netif_dbg (dev, drv, dev->net,"start tx path" ); - -/* Start the MAC transmitter */ - - ret =lan78xx_start_hw (dev,MAC_TX ,MAC_TX_TXEN_ ); -if (ret <0 ) -return ret; - -/* Start the Tx FIFO */ - - ret =lan78xx_start_hw (dev,FCT_TX_CTL ,FCT_TX_CTL_EN_ ); -if (ret <0 ) -return ret; - -return 0 ; -} - -static int lan78xx_stop_tx_path (struct lan78xx_net *dev) -{ -int ret; - -netif_dbg (dev, drv, dev->net,"stop tx path" ); - -/* Stop the Tx FIFO */ - - ret =lan78xx_stop_hw (dev,FCT_TX_CTL ,FCT_TX_CTL_EN_ ,FCT_TX_CTL_DIS_ ); -if (ret <0 ) -return ret; - -/* Stop the MAC transmitter */ - - ret =lan78xx_stop_hw (dev,MAC_TX ,MAC_TX_TXEN_ ,MAC_TX_TXD_ ); -if (ret <0 ) -return ret; - -return 0 ; -} - -/* The caller must ensure the Tx path is stopped before calling -* lan78xx_flush_tx_fifo(). -*/ -static int lan78xx_flush_tx_fifo (struct lan78xx_net *dev) -{ -return lan78xx_flush_fifo (dev,FCT_TX_CTL ,FCT_TX_CTL_RST_ ); -} - -static int lan78xx_start_rx_path (struct lan78xx_net *dev) -{ -int ret; - -netif_dbg (dev, drv, dev->net,"start rx path" ); - -/* Start the Rx FIFO */ - - ret =lan78xx_start_hw (dev,FCT_RX_CTL ,FCT_RX_CTL_EN_ ); -if (ret <0 ) -return ret; - -/* Start the MAC receiver*/ - - ret =lan78xx_start_hw (dev,MAC_RX ,MAC_RX_RXEN_ ); -if (ret <0 ) -return ret; - -return 0 ; -} - -static int lan78xx_stop_rx_path (struct lan78xx_net *dev) -{ -int ret; - -netif_dbg (dev, drv, dev->net,"stop rx path" ); - -/* Stop the MAC receiver */ - - ret =lan78xx_stop_hw (dev,MAC_RX ,MAC_RX_RXEN_ ,MAC_RX_RXD_ ); -if (ret <0 ) -return ret; - -/* Stop the Rx FIFO */ - - ret =lan78xx_stop_hw (dev,FCT_RX_CTL ,FCT_RX_CTL_EN_ ,FCT_RX_CTL_DIS_ ); -if (ret <0 ) -return ret; - -return 0 ; -} - -/* The caller must ensure the Rx path is stopped before calling -* lan78xx_flush_rx_fifo(). -*/ -static int lan78xx_flush_rx_fifo (struct lan78xx_net *dev) -{ -return lan78xx_flush_fifo (dev,FCT_RX_CTL ,FCT_RX_CTL_RST_ ); -} -static int lan78xx_reset (struct lan78xx_net *dev) {struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0 ]);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 869/1266 net: usb: lan78xx: disable VLAN filter in promiscuous mode
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Enrico Pozzobon <[email protected]>
[ Upstream commit 60444706aa17616efc03190d099ac347e28b3d0a ]
The hardware VLAN filter (RFE_CTL_VLAN_FILTER_) drops VLAN-tagged frames whose VID has not been registered via lan78xx_vlan_rx_add_vid(). It is left enabled in promiscuous mode, so packet capture (e.g. tcpdump or Wireshark) does not see tagged frames for unregistered VIDs.
Clear the filter while the interface is promiscuous and restore it from NETIF_F_HW_VLAN_CTAG_FILTER otherwise. Enforce the same condition in lan78xx_set_features() so netdev_update_features() cannot re-enable the filter while promiscuous.
--- drivers/net/usb/lan78xx.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
drivers/net/usb/lan78xx.c +14 -4
@@ -1418,6 +1418,17 @@ static void lan78xx_deferred_multicast_write(struct work_struct *param)lan78xx_write_reg (dev,RFE_CTL , pdata->rfe_ctl); } +static void lan78xx_update_vlan_filter (struct lan78xx_priv *pdata, +struct net_device *netdev, +netdev_features_t features) +{ +if ((features &NETIF_F_HW_VLAN_CTAG_FILTER ) && + !(netdev->flags &IFF_PROMISC )) + pdata->rfe_ctl |=RFE_CTL_VLAN_FILTER_ ; +else + pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_ ; +} +static void lan78xx_set_multicast (struct net_device *netdev) {struct lan78xx_net *dev =netdev_priv (netdev);
@@ -1452,6 +1463,8 @@ static void lan78xx_set_multicast(struct net_device *netdev) } } +lan78xx_update_vlan_filter (pdata, dev->net, dev->net->features); +if (netdev_mc_count (dev->net)) {struct netdev_hw_addr *ha;int i;
@@ -2756,10 +2769,7 @@ static int lan78xx_set_features(struct net_device *netdev,else pdata->rfe_ctl &= ~RFE_CTL_VLAN_STRIP_ ; -if (features &NETIF_F_HW_VLAN_CTAG_FILTER ) - pdata->rfe_ctl |=RFE_CTL_VLAN_FILTER_ ; -else - pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_ ; +lan78xx_update_vlan_filter (pdata, netdev, features);spin_unlock_irqrestore (&pdata->rfe_ctl_lock, flags);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 870/1266 net/sched: cake: reject overhead values that underflow length
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Samuel Moelius <[email protected]>
[ Upstream commit b7f97cae7ec1b6c3c32843c42be218690d310467 ]
CAKE accepts signed overhead values and stores them in an s16, but the adjusted packet length calculation uses unsigned arithmetic. A negative effective length can therefore wrap to a large value.
Such configurations make rate accounting depend on integer wraparound rather than on the packet size userspace intended to model. A static netlink lower bound is not enough because packets reaching CAKE can be smaller than any reasonable manual-overhead allowance.
Fold the signed overhead adjustment into the existing datapath MPU clamp so negative adjusted lengths are clamped before link-layer framing adjustments.
--- net/sched/sch_cake.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-)
net/sched/sch_cake.c +1 -4
@@ -1382,10 +1382,7 @@ static u32 cake_calc_overhead(struct cake_sched_data *q, u32 len, u32 off)if (q->min_netlen > len) q->min_netlen = len; - len += q->rate_overhead; - -if (len < q->rate_mpu) - len = q->rate_mpu; + len =max ((s32 )len + q->rate_overhead, (s32 )q->rate_mpu);if (q->atm_mode ==CAKE_ATM_ATM ) { len +=47 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 871/1266 octeontx2-pf: check DMAC extraction support before filtering
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Suman Ghosh <[email protected]>
[ Upstream commit 235acadd310533ba386ae61ad155b72bee381559 ]
Currently, configuring a VF MAC address via the PF (e.g., 'ip link set <pf> vf 0 mac <mac>') blindly attempts to install a DMAC-based hardware filter. However, the hardware parser profile might not support DMAC extraction.
Check if the hardware parsing profile supports DMAC extraction before adding the filter. Additionally, emit a warning message to inform the operator if the MAC filter installation fails due to missing DMAC extraction support. Update config->mac only after hardware programming succeeds in otx2_set_vf_mac().
--- .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-)
drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +37 -6
@@ -2361,10 +2361,42 @@ EXPORT_SYMBOL(otx2_ioctl);static int otx2_do_set_vf_mac (struct otx2_nic *pf,int vf,const u8 *mac) { +struct npc_get_field_status_req *freq; +struct npc_get_field_status_rsp *frsp;struct npc_install_flow_req *req;int err;mutex_lock (&pf->mbox.lock); + +/* Skip installing the DMAC filter if the hardware parser profile +* does not support DMAC extraction. +*/ + freq =otx2_mbox_alloc_msg_npc_get_field_status (&pf->mbox); +if (!freq) { + err = -ENOMEM ; + goto out; + } + + freq->field =NPC_DMAC ; + err =otx2_sync_mbox_msg (&pf->mbox); +if (err) + goto out; + + frsp = (struct npc_get_field_status_rsp *)otx2_mbox_get_rsp + (&pf->mbox.mbox,0 , &freq->hdr); +if (IS_ERR (frsp)) { + err =PTR_ERR (frsp); + goto out; + } + +if (!frsp->enable) { +netdev_warn (pf->netdev, +"VF %d MAC filter not installed: DMAC extraction not supported by parser profile\n" , + vf); + err = -EOPNOTSUPP ; + goto out; + } + req =otx2_mbox_alloc_msg_npc_install_flow (&pf->mbox);if (!req) { err = -ENOMEM ;
@@ -2403,13 +2435,12 @@ static int otx2_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)if (!is_valid_ether_addr (mac))return -EINVAL ; - config = &pf->vf_configs[vf]; -ether_addr_copy (config->mac, mac); - ret =otx2_do_set_vf_mac (pf, vf, mac); -if (ret ==0 ) -dev_info (&pdev->dev, -"Load/Reload VF driver\n" ); +if (ret ==0 ) { + config = &pf->vf_configs[vf]; +ether_addr_copy (config->mac, mac); +dev_info (&pdev->dev,"Load/Reload VF driver\n" ); + }return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 872/1266 ipv4: igmp: Fix potential UAF in igmp_gq_start_timer()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 7b19c0f81ed1fdaec6bc522569be367199a9edf3 ]
A race condition exists between device teardown (inetdev_destroy) and incoming IGMP query processing (igmp_rcv), leading to a Use-After-Free in the IGMP timer callback.
During device destruction, inetdev_destroy() drops the primary reference to in_device, which can drop its refcount to 0. The actual freeing of in_device memory is deferred via RCU (using call_rcu()).
Concurrently, igmp_rcv() runs under RCU read lock and obtains the in_device pointer. Because the memory is RCU-protected, CPU-0 can safely dereference in_device even if its refcount has hit 0.
However, if CPU-0 calls igmp_gq_start_timer() and re-arms the timer, it attempts to acquire a reference using in_dev_hold(). This increments the refcount from 0 to 1, triggering a "refcount_t: addition on 0" warning. Since the in_device memory is still scheduled to be freed after the RCU grace period (as the free callback does not check the refcount again), the device is freed while the timer is still armed. When the timer expires, it accesses the freed memory, causing a kernel panic.
Fix this by using refcount_inc_not_zero() (via a new helper in_dev_hold_safe()) to prevent acquiring a reference if the device is already being destroyed. If the refcount is 0, we do not arm the timer.
A similar issue in IPv6 MLD is fixed in a subsequent patch.
--- include/linux/inetdevice.h | 5 +++++ net/ipv4/igmp.c | 14 +++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-)
include/linux/inetdevice.h +5 -0
@@ -282,6 +282,11 @@ static inline void in_dev_put(struct in_device *idev)#define __in_dev_put (idev) refcount_dec(&(idev)->refcnt)#define in_dev_hold (idev) refcount_inc(&(idev)->refcnt) +static inline bool in_dev_hold_safe (struct in_device *idev) +{ +return refcount_inc_not_zero (&idev->refcnt); +} +#endif /* __KERNEL__ */ static __inline____be32 inet_make_mask (int logmask)
net/ipv4/igmp.c +9 -5
@@ -232,16 +232,20 @@ static void igmp_gq_start_timer(struct in_device *in_dev)return ; in_dev->mr_gq_running =1 ; -if (!mod_timer (&in_dev->mr_gq_timer, exp)) -in_dev_hold (in_dev); +if (in_dev_hold_safe (in_dev)) { +if (mod_timer (&in_dev->mr_gq_timer, exp)) +in_dev_put (in_dev); + } }static void igmp_ifc_start_timer (struct in_device *in_dev,int delay) { -int tv =get_random_u32_below (delay); +if (in_dev_hold_safe (in_dev)) { +int tv =get_random_u32_below (delay); -if (!mod_timer (&in_dev->mr_ifc_timer, jiffies+tv+2 )) -in_dev_hold (in_dev); +if (mod_timer (&in_dev->mr_ifc_timer, jiffies + tv +2 )) +in_dev_put (in_dev); + } }static void igmp_mod_timer (struct ip_mc_list *im,int max_delay)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 873/1266 ipv6: mcast: Replace locking comments with lockdep annotations.
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 818ae1a5ecb41d82ad180a99c39111f051894d90 ]
Commit 63ed8de4be81 ("mld: add mc_lock for protecting per-interface mld data") added the same comments regarding locking to many functions.
Let's replace the comments with lockdep annotation, which is more helpful.
Note that we just remove the comment for mld_clear_zeros() and mld_send_cr(), where mc_dereference() is used in the entry of the function.
While at it, a comment for __ipv6_sock_mc_join() is moved back to the correct place.
Stable-dep-of: 9b26518b6896 ("ipv6: mcast: Fix potential UAF in MLD delayed work")
--- net/ipv6/mcast.c | 125 +++++++++++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 54 deletions(-)
net/ipv6/mcast.c +71 -54
@@ -105,9 +105,9 @@ static int __ipv6_dev_mc_inc(struct net_device *dev,int sysctl_mld_max_msf__read_mostly =IPV6_MLD_MAX_MSF ;int sysctl_mld_qrv__read_mostly =MLD_QRV_DEFAULT ; -/* -* socket join on multicast group -*/ +#define mc_assert_locked (idev) \ + lockdep_assert_held(&(idev)->mc_lock) +#define mc_dereference (e, idev) \ rcu_dereference_protected(e, lockdep_is_held(&(idev)->mc_lock))
@@ -166,6 +166,9 @@ static int unsolicited_report_interval(struct inet6_dev *idev)return iv >0 ? iv :1 ; } +/* +* socket join on multicast group +*/ static int __ipv6_sock_mc_join (struct sock *sk,int ifindex,const struct in6_addr *addr,unsigned int mode) {
@@ -664,12 +667,13 @@ bool inet6_mc_check(const struct sock *sk, const struct in6_addr *mc_addr,return rv; } -/* called with mc_lock */ static void igmp6_group_added (struct ifmcaddr6 *mc) {struct net_device *dev = mc->idev->dev;char buf[MAX_ADDR_LEN ]; +mc_assert_locked (mc->idev); +if (IPV6_ADDR_MC_SCOPE (&mc->mca_addr) <IPV6_ADDR_SCOPE_LINKLOCAL )return ;
@@ -699,12 +703,13 @@ static void igmp6_group_added(struct ifmcaddr6 *mc)mld_ifc_event (mc->idev); } -/* called with mc_lock */ static void igmp6_group_dropped (struct ifmcaddr6 *mc) {struct net_device *dev = mc->idev->dev;char buf[MAX_ADDR_LEN ]; +mc_assert_locked (mc->idev); +if (IPV6_ADDR_MC_SCOPE (&mc->mca_addr) <IPV6_ADDR_SCOPE_LINKLOCAL )return ;
@@ -725,14 +730,13 @@ static void igmp6_group_dropped(struct ifmcaddr6 *mc)refcount_dec (&mc->mca_refcnt); } -/* -* deleted ifmcaddr6 manipulation -* called with mc_lock -*/ +/* deleted ifmcaddr6 manipulation */ static void mld_add_delrec (struct inet6_dev *idev,struct ifmcaddr6 *im) {struct ifmcaddr6 *pmc; +mc_assert_locked (idev); +/* this is an "ifmcaddr6" for convenience; only the fields below * are actually used. In particular, the refcnt and users are not * used for management of the delete list. Using the same structure
@@ -766,13 +770,14 @@ static void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)rcu_assign_pointer (idev->mc_tomb, pmc); } -/* called with mc_lock */ static void mld_del_delrec (struct inet6_dev *idev,struct ifmcaddr6 *im) {struct ip6_sf_list *psf, *sources, *tomb;struct in6_addr *pmca = &im->mca_addr;struct ifmcaddr6 *pmc, *pmc_prev; +mc_assert_locked (idev); + pmc_prev =NULL ;for_each_mc_tomb (idev, pmc) {if (ipv6_addr_equal (&pmc->mca_addr, pmca))
@@ -809,11 +814,12 @@ static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im) } } -/* called with mc_lock */ static void mld_clear_delrec (struct inet6_dev *idev) {struct ifmcaddr6 *pmc, *nextpmc; +mc_assert_locked (idev); + pmc =mc_dereference (idev->mc_tomb, idev);RCU_INIT_POINTER (idev->mc_tomb,NULL );
@@ -870,13 +876,14 @@ static void ma_put(struct ifmcaddr6 *mc) } } -/* called with mc_lock */ static struct ifmcaddr6 *mca_alloc (struct inet6_dev *idev,const struct in6_addr *addr,unsigned int mode) {struct ifmcaddr6 *mc; +mc_assert_locked (idev); + mc =kzalloc (sizeof (*mc),GFP_KERNEL );if (!mc)return NULL ;
@@ -1046,46 +1053,51 @@ bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,return rv; } -/* called with mc_lock */ static void mld_gq_start_work (struct inet6_dev *idev) {unsigned long tv =get_random_u32_below (idev->mc_maxdelay); +mc_assert_locked (idev); + idev->mc_gq_running =1 ;if (!mod_delayed_work (mld_wq, &idev->mc_gq_work, tv +2 ))in6_dev_hold (idev); } -/* called with mc_lock */ static void mld_gq_stop_work (struct inet6_dev *idev) { +mc_assert_locked (idev); + idev->mc_gq_running =0 ;if (cancel_delayed_work (&idev->mc_gq_work))__in6_dev_put (idev); } -/* called with mc_lock */ static void mld_ifc_start_work (struct inet6_dev *idev,unsigned long delay) {unsigned long tv =get_random_u32_below (delay); +mc_assert_locked (idev); +if (!mod_delayed_work (mld_wq, &idev->mc_ifc_work, tv +2 ))in6_dev_hold (idev); } -/* called with mc_lock */ static void mld_ifc_stop_work (struct inet6_dev *idev) { +mc_assert_locked (idev); + idev->mc_ifc_count =0 ;if (cancel_delayed_work (&idev->mc_ifc_work))__in6_dev_put (idev); } -/* called with mc_lock */ static void mld_dad_start_work (struct inet6_dev *idev,unsigned long delay) {unsigned long tv =get_random_u32_below (delay); +mc_assert_locked (idev); +if (!mod_delayed_work (mld_wq, &idev->mc_dad_work, tv +2 ))in6_dev_hold (idev); }
@@ -1110,14 +1122,13 @@ static void mld_report_stop_work(struct inet6_dev *idev)__in6_dev_put (idev); } -/* -* IGMP handling (alias multicast ICMPv6 messages) -* called with mc_lock -*/ +/* IGMP handling (alias multicast ICMPv6 messages) */ static void igmp6_group_queried (struct ifmcaddr6 *ma,unsigned long resptime) {unsigned long delay = resptime; +mc_assert_locked (ma->idev); +/* Do not start work for these addresses */ if (ipv6_addr_is_ll_all_nodes (&ma->mca_addr) ||IPV6_ADDR_MC_SCOPE (&ma->mca_addr) <IPV6_ADDR_SCOPE_LINKLOCAL )
@@ -1136,15 +1147,15 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime) ma->mca_flags |=MAF_TIMER_RUNNING ; } -/* mark EXCLUDE-mode sources -* called with mc_lock -*/ +/* mark EXCLUDE-mode sources */ static bool mld_xmarksources (struct ifmcaddr6 *pmc,int nsrcs,const struct in6_addr *srcs) {struct ip6_sf_list *psf;int i, scount; +mc_assert_locked (pmc->idev); + scount =0 ;for_each_psf_mclock (pmc, psf) {if (scount == nsrcs)
@@ -1167,13 +1178,14 @@ static bool mld_xmarksources(struct ifmcaddr6 *pmc, int nsrcs,return true; } -/* called with mc_lock */ static bool mld_marksources (struct ifmcaddr6 *pmc,int nsrcs,const struct in6_addr *srcs) {struct ip6_sf_list *psf;int i, scount; +mc_assert_locked (pmc->idev); +if (pmc->mca_sfmode ==MCAST_EXCLUDE )return mld_xmarksources (pmc, nsrcs, srcs);
@@ -1868,7 +1880,6 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,#define AVAILABLE (skb) ((skb) ? skb_availroom(skb) : 0) -/* called with mc_lock */ static struct sk_buff *add_grec (struct sk_buff *skb,struct ifmcaddr6 *pmc,int type,int gdeleted,int sdeleted,int crsend)
@@ -1882,6 +1893,8 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,struct mld2_report *pmr;unsigned int mtu; +mc_assert_locked (idev); +if (pmc->mca_flags &MAF_NOREPORT )return skb;
@@ -2000,12 +2013,13 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,return skb; } -/* called with mc_lock */ static void mld_send_report (struct inet6_dev *idev,struct ifmcaddr6 *pmc) {struct sk_buff *skb =NULL ;int type; +mc_assert_locked (idev); +if (!pmc) {for_each_mc_mclock (idev, pmc) {if (pmc->mca_flags &MAF_NOREPORT )
@@ -2027,10 +2041,7 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc)mld_sendpack (skb); } -/* -* remove zero-count source records from a source filter list -* called with mc_lock -*/ +/* remove zero-count source records from a source filter list */ static void mld_clear_zeros (struct ip6_sf_list __rcu **ppsf,struct inet6_dev *idev) {struct ip6_sf_list *psf_prev, *psf_next, *psf;
@@ -2054,7 +2065,6 @@ static void mld_clear_zeros(struct ip6_sf_list __rcu **ppsf, struct inet6_dev *i } } -/* called with mc_lock */ static void mld_send_cr (struct inet6_dev *idev) {struct ifmcaddr6 *pmc, *pmc_prev, *pmc_next;
@@ -2218,13 +2228,14 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) goto out; } -/* called with mc_lock */ static void mld_send_initial_cr (struct inet6_dev *idev) { -struct sk_buff *skb;struct ifmcaddr6 *pmc; +struct sk_buff *skb;int type; +mc_assert_locked (idev); +if (mld_in_v1_mode (idev))return ;
@@ -2271,13 +2282,14 @@ static void mld_dad_work(struct work_struct *work)in6_dev_put (idev); } -/* called with mc_lock */ static int ip6_mc_del1_src (struct ifmcaddr6 *pmc,int sfmode, -const struct in6_addr *psfsrc) +const struct in6_addr *psfsrc) {struct ip6_sf_list *psf, *psf_prev;int rv =0 ; +mc_assert_locked (pmc->idev); + psf_prev =NULL ;for_each_psf_mclock (pmc, psf) {if (ipv6_addr_equal (&psf->sf_addr, psfsrc))
@@ -2314,7 +2326,6 @@ static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,return rv; } -/* called with mc_lock */ static int ip6_mc_del_src (struct inet6_dev *idev,const struct in6_addr *pmca,int sfmode,int sfcount,const struct in6_addr *psfsrc,int delta)
@@ -2326,6 +2337,8 @@ static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca,if (!idev)return -ENODEV ; +mc_assert_locked (idev); +for_each_mc_mclock (idev, pmc) {if (ipv6_addr_equal (pmca, &pmc->mca_addr))break ;
@@ -2367,15 +2380,14 @@ static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca,return err; } -/* -* Add multicast single-source filter to the interface list -* called with mc_lock -*/ +/* Add multicast single-source filter to the interface list */ static int ip6_mc_add1_src (struct ifmcaddr6 *pmc,int sfmode, -const struct in6_addr *psfsrc) +const struct in6_addr *psfsrc) {struct ip6_sf_list *psf, *psf_prev; +mc_assert_locked (pmc->idev); + psf_prev =NULL ;for_each_psf_mclock (pmc, psf) {if (ipv6_addr_equal (&psf->sf_addr, psfsrc))
@@ -2398,11 +2410,12 @@ static int ip6_mc_add1_src(struct ifmcaddr6 *pmc, int sfmode,return 0 ; } -/* called with mc_lock */ static void sf_markstate (struct ifmcaddr6 *pmc) { -struct ip6_sf_list *psf;int mca_xcount = pmc->mca_sfcount[MCAST_EXCLUDE ]; +struct ip6_sf_list *psf; + +mc_assert_locked (pmc->idev);for_each_psf_mclock (pmc, psf) {if (pmc->mca_sfcount[MCAST_EXCLUDE ]) {
@@ -2415,14 +2428,15 @@ static void sf_markstate(struct ifmcaddr6 *pmc) } } -/* called with mc_lock */ static int sf_setstate (struct ifmcaddr6 *pmc) { -struct ip6_sf_list *psf, *dpsf;int mca_xcount = pmc->mca_sfcount[MCAST_EXCLUDE ]; +struct ip6_sf_list *psf, *dpsf;int qrv = pmc->idev->mc_qrv;int new_in, rv; +mc_assert_locked (pmc->idev); + rv =0 ;for_each_psf_mclock (pmc, psf) {if (pmc->mca_sfcount[MCAST_EXCLUDE ]) {
@@ -2481,10 +2495,7 @@ static int sf_setstate(struct ifmcaddr6 *pmc)return rv; } -/* -* Add multicast source filter list to the interface list -* called with mc_lock -*/ +/* Add multicast source filter list to the interface list */ static int ip6_mc_add_src (struct inet6_dev *idev,const struct in6_addr *pmca,int sfmode,int sfcount,const struct in6_addr *psfsrc,int delta)
@@ -2496,6 +2507,8 @@ static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca,if (!idev)return -ENODEV ; +mc_assert_locked (idev); +for_each_mc_mclock (idev, pmc) {if (ipv6_addr_equal (pmca, &pmc->mca_addr))break ;
@@ -2541,11 +2554,12 @@ static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca,return err; } -/* called with mc_lock */ static void ip6_mc_clear_src (struct ifmcaddr6 *pmc) {struct ip6_sf_list *psf, *nextpsf; +mc_assert_locked (pmc->idev); +for (psf =mc_dereference (pmc->mca_tomb, pmc->idev); psf; psf = nextpsf) {
@@ -2565,11 +2579,12 @@ static void ip6_mc_clear_src(struct ifmcaddr6 *pmc) pmc->mca_sfcount[MCAST_EXCLUDE ] =1 ; } -/* called with mc_lock */ static void igmp6_join_group (struct ifmcaddr6 *ma) {unsigned long delay; +mc_assert_locked (ma->idev); +if (ma->mca_flags &MAF_NOREPORT )return ;
@@ -2616,9 +2631,10 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,return err; } -/* called with mc_lock */ static void igmp6_leave_group (struct ifmcaddr6 *ma) { +mc_assert_locked (ma->idev); +if (mld_in_v1_mode (ma->idev)) {if (ma->mca_flags &MAF_LAST_REPORTER ) { igmp6_send(&ma->mca_addr, ma->idev->dev,
@@ -2663,9 +2679,10 @@ static void mld_ifc_work(struct work_struct *work)in6_dev_put (idev); } -/* called with mc_lock */ static void mld_ifc_event (struct inet6_dev *idev) { +mc_assert_locked (idev); +if (mld_in_v1_mode (idev))return ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 874/1266 ipv6: mcast: Fix potential UAF in MLD delayed work
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 9b26518b6896a16b809b1e42986f4ebac7bccc1e ]
A race condition exists between device teardown and incoming MLD query processing, leading to a Use-After-Free in the MLD delayed work.
During device destruction, the primary reference to inet6_dev is dropped, which can drop its refcount to 0. The actual freeing of inet6_dev memory is deferred via RCU.
Concurrently, the packet receive path runs under RCU read lock and obtains the inet6_dev pointer. Because the memory is RCU-protected, CPU-0 can safely dereference inet6_dev even if its refcount has hit 0.
However, if CPU-0 calls igmp6_event_query() and schedules delayed work, it attempts to acquire a reference using in6_dev_hold(). This increments the refcount from 0 to 1, triggering a "refcount_t: addition on 0" warning. Since the inet6_dev memory is still scheduled to be freed after the RCU grace period, the device is freed while the work is still scheduled. When the work runs, it accesses the freed memory, causing a kernel panic.
Fix this by using refcount_inc_not_zero() (via a new helper in6_dev_hold_safe()) to prevent acquiring a reference if the device is already being destroyed. If the refcount is 0, we do not schedule the work.
--- include/net/addrconf.h | 5 +++++ net/ipv6/mcast.c | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 12 deletions(-)
include/net/addrconf.h +5 -0
@@ -411,6 +411,11 @@ static inline void in6_dev_hold(struct inet6_dev *idev)refcount_inc (&idev->refcnt); } +static inline bool in6_dev_hold_safe (struct inet6_dev *idev) +{ +return refcount_inc_not_zero (&idev->refcnt); +} +/* called with rcu_read_lock held */ static inline bool ip6_ignore_linkdown (const struct net_device *dev) {
net/ipv6/mcast.c +28 -12
@@ -1060,8 +1060,10 @@ static void mld_gq_start_work(struct inet6_dev *idev)mc_assert_locked (idev); idev->mc_gq_running =1 ; -if (!mod_delayed_work (mld_wq, &idev->mc_gq_work, tv +2 )) -in6_dev_hold (idev); +if (in6_dev_hold_safe (idev)) { +if (mod_delayed_work (mld_wq, &idev->mc_gq_work, tv +2 )) +in6_dev_put (idev); + } }static void mld_gq_stop_work (struct inet6_dev *idev)
@@ -1079,8 +1081,10 @@ static void mld_ifc_start_work(struct inet6_dev *idev, unsigned long delay)mc_assert_locked (idev); -if (!mod_delayed_work (mld_wq, &idev->mc_ifc_work, tv +2 )) -in6_dev_hold (idev); +if (in6_dev_hold_safe (idev)) { +if (mod_delayed_work (mld_wq, &idev->mc_ifc_work, tv +2 )) +in6_dev_put (idev); + } }static void mld_ifc_stop_work (struct inet6_dev *idev)
@@ -1098,8 +1102,10 @@ static void mld_dad_start_work(struct inet6_dev *idev, unsigned long delay)mc_assert_locked (idev); -if (!mod_delayed_work (mld_wq, &idev->mc_dad_work, tv +2 )) -in6_dev_hold (idev); +if (in6_dev_hold_safe (idev)) { +if (mod_delayed_work (mld_wq, &idev->mc_dad_work, tv +2 )) +in6_dev_put (idev); + } }static void mld_dad_stop_work (struct inet6_dev *idev)
@@ -1385,18 +1391,23 @@ static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld,void igmp6_event_query (struct sk_buff *skb) {struct inet6_dev *idev =__in6_dev_get (skb->dev); +bool put = false;if (!idev || idev->dead) goto out;spin_lock_bh (&idev->mc_query_lock); -if (skb_queue_len (&idev->mc_query_queue) <MLD_MAX_SKBS ) { +if (skb_queue_len (&idev->mc_query_queue) <MLD_MAX_SKBS && +in6_dev_hold_safe (idev)) {__skb_queue_tail (&idev->mc_query_queue, skb); -if (!mod_delayed_work (mld_wq, &idev->mc_query_work,0 )) -in6_dev_hold (idev); +if (mod_delayed_work (mld_wq, &idev->mc_query_work,0 )) + put = true; skb =NULL ; }spin_unlock_bh (&idev->mc_query_lock); + +if (put) +in6_dev_put (idev); out:kfree_skb (skb); }
@@ -1553,18 +1564,23 @@ static void mld_query_work(struct work_struct *work)void igmp6_event_report (struct sk_buff *skb) {struct inet6_dev *idev =__in6_dev_get (skb->dev); +bool put = false;if (!idev || idev->dead) goto out;spin_lock_bh (&idev->mc_report_lock); -if (skb_queue_len (&idev->mc_report_queue) <MLD_MAX_SKBS ) { +if (skb_queue_len (&idev->mc_report_queue) <MLD_MAX_SKBS && +in6_dev_hold_safe (idev)) {__skb_queue_tail (&idev->mc_report_queue, skb); -if (!mod_delayed_work (mld_wq, &idev->mc_report_work,0 )) -in6_dev_hold (idev); +if (mod_delayed_work (mld_wq, &idev->mc_report_work,0 )) + put = true; skb =NULL ; }spin_unlock_bh (&idev->mc_report_lock); + +if (put) +in6_dev_put (idev); out:kfree_skb (skb); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 875/1266 ipvs: pass parsed transport offset to state handlers
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yizhou Zhao <[email protected]>
[ Upstream commit bae7ce7bafb59e42dc0e0e2999fdd9d1cffe3866 ]
IPVS callers already parse the packet into struct ip_vs_iphdr before updating connection state. For IPv6 this records the real transport-header offset after extension headers in iph.len.
Pass this parsed transport offset through ip_vs_set_state() and the protocol state_transition() callback so protocol handlers can use the same packet context as scheduling and NAT handling. This patch only changes the common callback plumbing and adapts the protocol callback signatures; TCP and SCTP start using the value in follow-up patches.
Stable-dep-of: 2500fa3958b1 ("ipvs: use parsed transport offset in TCP state lookup")
--- include/net/ip_vs.h | 3 ++- net/netfilter/ipvs/ip_vs_core.c | 10 +++++----- net/netfilter/ipvs/ip_vs_proto_sctp.c | 3 ++- net/netfilter/ipvs/ip_vs_proto_tcp.c | 3 ++- net/netfilter/ipvs/ip_vs_proto_udp.c | 3 ++- 5 files changed, 13 insertions(+), 9 deletions(-)
include/net/ip_vs.h +2 -1
@@ -516,7 +516,8 @@ struct ip_vs_protocol {void (*state_transition)(struct ip_vs_conn *cp,int direction,const struct sk_buff *skb, -struct ip_vs_proto_data *pd); +struct ip_vs_proto_data *pd, +unsigned int iph_len);int (*register_app)(struct netns_ipvs *ipvs,struct ip_vs_app *inc);
net/netfilter/ipvs/ip_vs_core.c +5 -5
@@ -220,10 +220,10 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)static inline void ip_vs_set_state (struct ip_vs_conn *cp,int direction,const struct sk_buff *skb, -struct ip_vs_proto_data *pd) +struct ip_vs_proto_data *pd,unsigned int iph_len) {if (likely (pd->pp->state_transition)) - pd->pp->state_transition (cp, direction, skb, pd); + pd->pp->state_transition (cp, direction, skb, pd, iph_len); } static inline int
@@ -625,7 +625,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,ip_vs_in_stats (cp, skb);/* set state */ -ip_vs_set_state (cp,IP_VS_DIR_INPUT , skb, pd); +ip_vs_set_state (cp,IP_VS_DIR_INPUT , skb, pd, iph->len);/* transmit the first SYN packet */ ret = cp->packet_xmit (skb, cp, pd->pp, iph);
@@ -1306,7 +1306,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, after_nat:ip_vs_out_stats (cp, skb); -ip_vs_set_state (cp,IP_VS_DIR_OUTPUT , skb, pd); +ip_vs_set_state (cp,IP_VS_DIR_OUTPUT , skb, pd, iph->len); skb->ipvs_property =1 ;if (!(cp->flags &IP_VS_CONN_F_NFCT ))ip_vs_notrack (skb);
@@ -2051,7 +2051,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stateIP_VS_DBG_PKT (11 , af, pp, skb, iph.off,"Incoming packet" );ip_vs_in_stats (cp, skb); -ip_vs_set_state (cp,IP_VS_DIR_INPUT , skb, pd); +ip_vs_set_state (cp,IP_VS_DIR_INPUT , skb, pd, iph.len);if (cp->packet_xmit) ret = cp->packet_xmit (skb, cp, pp, &iph);/* do not touch skb anymore */
net/netfilter/ipvs/ip_vs_proto_sctp.c +2 -1
@@ -468,7 +468,8 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,static void sctp_state_transition (struct ip_vs_conn *cp,int direction, -const struct sk_buff *skb,struct ip_vs_proto_data *pd) +const struct sk_buff *skb,struct ip_vs_proto_data *pd, +unsigned int iph_len) {spin_lock_bh (&cp->lock);set_sctp_state (pd, cp, direction, skb);
net/netfilter/ipvs/ip_vs_proto_tcp.c +2 -1
@@ -580,7 +580,8 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,static void tcp_state_transition (struct ip_vs_conn *cp,int direction,const struct sk_buff *skb, -struct ip_vs_proto_data *pd) +struct ip_vs_proto_data *pd, +unsigned int iph_len) {struct tcphdr _tcph, *th;
net/netfilter/ipvs/ip_vs_proto_udp.c +2 -1
@@ -445,7 +445,8 @@ static const char * udp_state_name(int state)static void udp_state_transition (struct ip_vs_conn *cp,int direction,const struct sk_buff *skb, -struct ip_vs_proto_data *pd) +struct ip_vs_proto_data *pd, +unsigned int iph_len) {if (unlikely (!pd)) {pr_err ("UDP no ns data\n" );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 876/1266 ipvs: use parsed transport offset in TCP state lookup
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yizhou Zhao <[email protected]>
[ Upstream commit 2500fa3958b1ba51c2b065e39db1b04dfa7e23a2 ]
TCP state handling reparses the skb to find the TCP header. For IPv6 it uses sizeof(struct ipv6hdr), while the surrounding IPVS code already parsed the packet with ip_vs_fill_iph_skb() and has the real transport-header offset in iph.len.
This makes TCP state handling look at the wrong bytes when an IPv6 packet carries extension headers. Use the parsed transport offset passed down from ip_vs_set_state() when reading the TCP header.
For IPv4 and for IPv6 packets without extension headers, the passed offset matches the previous value.
--- net/netfilter/ipvs/ip_vs_proto_tcp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
net/netfilter/ipvs/ip_vs_proto_tcp.c +1 -7
@@ -585,13 +585,7 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction, {struct tcphdr _tcph, *th; -#ifdef CONFIG_IP_VS_IPV6 -int ihl = cp->af ==AF_INET ?ip_hdrlen (skb) :sizeof (struct ipv6hdr ); -#else -int ihl =ip_hdrlen (skb); -#endif - - th =skb_header_pointer (skb, ihl,sizeof (_tcph), &_tcph); + th =skb_header_pointer (skb, iph_len,sizeof (_tcph), &_tcph);if (th ==NULL )return ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 877/1266 ipvs: fix PMTU for GUE/GRE tunnel ICMP errors
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yizhou Zhao <[email protected]>
[ Upstream commit 6b335af0d0d1ff44ac579d106953bf19299e5233 ]
When an ICMP Fragmentation Needed error is received for a tunneled IPVS connection, ip_vs_in_icmp() recomputes the MTU that the original packet can use by subtracting the tunnel overhead from the reported next-hop MTU.
The current code always subtracts sizeof(struct iphdr), which is only the IPIP overhead. For GUE and GRE tunnels, ipvs_udp_decap() and ipvs_gre_decap() already compute the additional tunnel header length, but that value is scoped to the decapsulation block and is lost before the ICMP_FRAG_NEEDED handling. As a result, the ICMP error sent back to the client advertises an MTU that is too large, so PMTUD can fail to converge for GUE/GRE-tunneled real servers.
With a reported next-hop MTU of 1400, a GUE tunnel currently returns 1380 to the client. The correct value is 1368:
1400 - sizeof(struct iphdr) - sizeof(struct udphdr) - sizeof(struct guehdr)
Hoist the tunnel header length into the main ip_vs_in_icmp() scope and subtract sizeof(struct iphdr) + ulen in the Fragmentation Needed path. The IPIP path keeps ulen as 0, so its existing 1400 - 20 = 1380 result is unchanged.
Stable-dep-of: 3f7a535ff0fa ("ipvs: ensure inner headers in ICMP errors are in headroom")
--- net/netfilter/ipvs/ip_vs_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
net/netfilter/ipvs/ip_vs_core.c +3 -3
@@ -1585,6 +1585,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,bool tunnel, new_cp = false;union nf_inet_addr *raddr;char *outer_proto ="IPIP" ; +int ulen =0 ; *related =1 ;
@@ -1649,7 +1650,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,/* Error for our tunnel must arrive at LOCAL_IN */ (skb_rtable(skb)->rt_flags & RTCF_LOCAL)) {__u8 iproto; -int ulen;/* Non-first fragment has no UDP/GRE header */ if (unlikely (cih->frag_off &htons (IP_OFFSET )))
@@ -1754,8 +1754,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,if (dest_dst) mtu =dst_mtu (dest_dst->dst_cache); } -if (mtu >68 +sizeof (struct iphdr )) - mtu -=sizeof (struct iphdr ); +if (mtu >68 +sizeof (struct iphdr ) + ulen) + mtu -=sizeof (struct iphdr ) + ulen; info =htonl (mtu); }/* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 878/1266 ipvs: ensure inner headers in ICMP errors are in headroom
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julian Anastasov <[email protected]>
[ Upstream commit 3f7a535ff0fa627a0132803e4c2f903ceffcbc1c ]
Sashiko points out that after stripping the outer headers with pskb_pull() we should ensure the inner IP headers in ICMP errors from tunnels are present in the skb headroom for functions like ipv4_update_pmtu(), icmp_send() and IP_VS_DBG().
Also, add more checks for the length of the inner headers.
--- net/netfilter/ipvs/ip_vs_core.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-)
net/netfilter/ipvs/ip_vs_core.c +15 -6
@@ -1585,6 +1585,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,bool tunnel, new_cp = false;union nf_inet_addr *raddr;char *outer_proto ="IPIP" ; +unsigned int hlen_ipip;int ulen =0 ; *related =1 ;
@@ -1622,9 +1623,10 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,/* Now find the contained IP header */ offset +=sizeof (_icmph); cih =skb_header_pointer (skb, offset,sizeof (_ciph), &_ciph); -if (cih ==NULL ) +if (!(cih && cih->version ==4 && cih->ihl >=5 ))return NF_ACCEPT ;/* The packet looks wrong, ignore */ raddr = (union nf_inet_addr *)&cih->daddr; + hlen_ipip = cih->ihl *4 ;/* Special case for errors for IPIP/UDP/GRE tunnel packets */ tunnel = false;
@@ -1640,9 +1642,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,/* Only for known tunnel */ if (!dest || dest->tun_type !=IP_VS_CONN_F_TUNNEL_TYPE_IPIP )return NF_ACCEPT ; - offset += cih->ihl *4 ; + offset += hlen_ipip; cih =skb_header_pointer (skb, offset,sizeof (_ciph), &_ciph); -if (cih ==NULL ) +if (!(cih && cih->version ==4 && cih->ihl >=5 ))return NF_ACCEPT ;/* The packet looks wrong, ignore */ tunnel = true; } else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */
@@ -1654,7 +1656,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,/* Non-first fragment has no UDP/GRE header */ if (unlikely (cih->frag_off &htons (IP_OFFSET )))return NF_ACCEPT ; - offset2 = offset + cih->ihl *4 ; + offset2 = offset + hlen_ipip;if (cih->protocol ==IPPROTO_UDP ) { ulen =ipvs_udp_decap (ipvs, skb, offset2,AF_INET , raddr, &iproto);
@@ -1723,6 +1725,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, }if (tunnel) { +unsigned int hlen_orig = cih->ihl *4 ;__be32 info = ic->un.gateway;__u8 type = ic->type;__u8 code = ic->code;
@@ -1739,6 +1742,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, goto ignore_tunnel; offset2 -= ihl +sizeof (_icmph);skb_reset_network_header (skb); +/* Ensure the IP header is present in headroom */ +if (!pskb_may_pull (skb, hlen_ipip)) + goto ignore_tunnel;IP_VS_DBG (12 ,"ICMP for %s %pI4->%pI4: mtu=%u\n" , outer_proto, &ip_hdr (skb)->saddr, &ip_hdr (skb)->daddr, mtu);
@@ -1754,8 +1760,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,if (dest_dst) mtu =dst_mtu (dest_dst->dst_cache); } -if (mtu >68 +sizeof (struct iphdr ) + ulen) - mtu -=sizeof (struct iphdr ) + ulen; +if (mtu >68 + hlen_ipip + ulen) + mtu -= hlen_ipip + ulen; info =htonl (mtu); }/* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of
@@ -1764,6 +1770,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,if (pskb_pull (skb, offset2) ==NULL ) goto ignore_tunnel;skb_reset_network_header (skb); +/* Ensure the IP header is present in headroom */ +if (!pskb_may_pull (skb, hlen_orig)) + goto ignore_tunnel;IP_VS_DBG (12 ,"Sending ICMP for %pI4->%pI4: t=%u, c=%u, i=%u\n" , &ip_hdr (skb)->saddr, &ip_hdr (skb)->daddr, type, code,ntohl (info));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 879/1266 s390/zcrypt: Remove the empty file
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rongguang Wei <[email protected]>
[ Upstream commit 575f87b9d4882b0d621192fdd754d7e09dbd5789 ]
The files has no real user because CEX2 and CEX3 device drivers are removed, also remove these empty files.
--- drivers/s390/crypto/zcrypt_cex2a.c | 0 drivers/s390/crypto/zcrypt_cex2a.h | 0 drivers/s390/crypto/zcrypt_cex2c.c | 0 drivers/s390/crypto/zcrypt_cex2c.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 drivers/s390/crypto/zcrypt_cex2a.c delete mode 100644 drivers/s390/crypto/zcrypt_cex2a.h delete mode 100644 drivers/s390/crypto/zcrypt_cex2c.c delete mode 100644 drivers/s390/crypto/zcrypt_cex2c.h
drivers/s390/crypto/zcrypt_cex2a.c deleted +0 -0
drivers/s390/crypto/zcrypt_cex2a.h deleted +0 -0
drivers/s390/crypto/zcrypt_cex2c.c deleted +0 -0
drivers/s390/crypto/zcrypt_cex2c.h deleted +0 -0
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 880/1266 cifs: validate DFS referral string offsets
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
[ Upstream commit 027a84ac6b50c12ef767c15abfc58aa865820e9e ]
parse_dfs_referrals() validates that the response header and referral array fit in the received buffer, but each referral also contains string offsets supplied by the server.
Those offsets are used to compute the DfsPath and NetworkAddress string pointers without checking whether they still point inside the response buffer. A malformed referral can therefore make the computed pointer exceed the end of the buffer. The resulting negative max_len is then passed to cifs_strndup_from_utf16(), and the non-Unicode path forwards it to kstrndup() as a size_t, allowing strnlen() to read out of bounds.
Validate each string offset before deriving the string pointer.
--- fs/smb/client/misc.c | 8 ++++++++ 1 file changed, 8 insertions(+)
fs/smb/client/misc.c +8 -0
@@ -1039,6 +1039,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, node->ref_flag =le16_to_cpu (ref->ReferralEntryFlags);/* copy DfsPath */ +if (le16_to_cpu (ref->DfsPathOffset) > data_end - (char *)ref) { + rc = -EINVAL ; + goto parse_DFS_referrals_exit; + } temp = (char *)ref +le16_to_cpu (ref->DfsPathOffset); max_len = data_end - temp; node->path_name = cifs_strndup_from_utf16(temp, max_len,
@@ -1049,6 +1053,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, }/* copy link target UNC */ +if (le16_to_cpu (ref->NetworkAddressOffset) > data_end - (char *)ref) { + rc = -EINVAL ; + goto parse_DFS_referrals_exit; + } temp = (char *)ref +le16_to_cpu (ref->NetworkAddressOffset); max_len = data_end - temp; node->node_name = cifs_strndup_from_utf16(temp, max_len,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 881/1266 SUNRPC: release lower rpc_clnt if killed waiting for XPRT_LOCKED
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 29bacb096d43bd04062f6655e50ceeed27fe6d2f ]
xs_tcp_tls_setup_socket() creates a temporary "lower" rpc_clnt with rpc_create() to drive the inner TLS handshake, then waits for XPRT_LOCKED on its xprt with TASK_KILLABLE so a stuck handshake can be aborted by signal. When the wait is interrupted, the function jumps to out_unlock without releasing lower_clnt. The success path and the out_close error path both call rpc_shutdown_client(lower_clnt); only the killed-wait path skips it, leaking the clnt and its underlying xprt.
Call rpc_shutdown_client() on this path before joining out_unlock. xprt_release_write() is not needed here because XPRT_LOCKED was never acquired.
--- net/sunrpc/xprtsock.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/sunrpc/xprtsock.c +4 -1
@@ -2713,8 +2713,11 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work) lower_xprt =rcu_dereference (lower_clnt->cl_xprt);rcu_read_unlock (); -if (wait_on_bit_lock (&lower_xprt->state,XPRT_LOCKED ,TASK_KILLABLE )) +if (wait_on_bit_lock (&lower_xprt->state,XPRT_LOCKED ,TASK_KILLABLE )) { +/* XPRT_LOCKED was never acquired. */ +rpc_shutdown_client (lower_clnt); goto out_unlock; + } status =xs_tls_handshake_sync (lower_xprt, &upper_xprt->xprtsec);if (status) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 882/1266 SUNRPC: pin upper rpc_clnt across the TLS connect_worker
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 46bc86c833956219bbfd246c1ffd832a479c5199 ]
The TLS connect path has a use-after-free: nothing pins the upper rpc_clnt across the delayed connect_worker. xs_connect() stores task->tk_client in sock_xprt::clnt as a raw pointer and queues the worker; for TLS-secured transports that worker is xs_tcp_tls_setup_socket(), which reads several fields out of the saved pointer (cl_timeout, cl_program, cl_prog, cl_vers, cl_cred, cl_stats) to construct the args for the inner handshake rpc_clnt.
The xprt does not reference the rpc_clnt; the rpc_clnt references the xprt. xs_destroy() does cancel the connect_worker, but it runs only when the xprt's refcount drops to zero, which cannot happen until the rpc_clnt releases its cl_xprt reference in rpc_free_client_work(). When a TLS handshake fails fatally (for example, an mTLS mount whose client cert does not match the server), the connecting task is woken with -EACCES and exits, the mount caller invokes rpc_shutdown_client(), and the upper rpc_clnt is freed before the queued connect_worker fires. xs_tcp_tls_setup_socket() then dereferences the freed clnt, producing the refcount_t underflow Michael Nemanov reported.
Take a reference on the upper rpc_clnt in xs_connect() for TLS transports via a new rpc_hold_client() helper, and drop it in the connect_worker's exit path with rpc_release_client(). The xprt_lock_connect() / xprt_unlock_connect() pairing already serialises xs_connect() with xs_tcp_tls_setup_socket(), so the take and release are balanced one-for-one.
The non-TLS connect worker (xs_tcp_setup_socket) never reads sock_xprt::clnt, so leave that path alone and avoid the clnt-holds-xprt-holds-clnt cycle that would otherwise prevent xprt destruction.
--- include/linux/sunrpc/clnt.h | 1 + net/sunrpc/clnt.c | 19 +++++++++++++++++-- net/sunrpc/xprtsock.c | 11 ++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-)
include/linux/sunrpc/clnt.h +1 -0
@@ -186,6 +186,7 @@ int rpc_switch_client_transport(struct rpc_clnt *, const struct rpc_timeout *); void rpc_shutdown_client(struct rpc_clnt *); +void rpc_hold_client (struct rpc_clnt *);void rpc_release_client (struct rpc_clnt *);void rpc_task_release_transport (struct rpc_task *);void rpc_task_release_client (struct rpc_task *);
net/sunrpc/clnt.c +17 -2
@@ -1016,8 +1016,23 @@ rpc_free_auth(struct rpc_clnt *clnt)return NULL ; } -/* -* Release reference to the RPC client +/** +* rpc_hold_client - acquire a reference on an rpc_clnt +* @clnt: rpc_clnt to pin +* +* Pairs with rpc_release_client(). +*/ +void rpc_hold_client (struct rpc_clnt *clnt) +{ +refcount_inc (&clnt->cl_count); +} + +/** +* rpc_release_client - release a reference on an rpc_clnt +* @clnt: rpc_clnt to release +* +* Pairs with rpc_hold_client(). The rpc_clnt's resources are +* freed once its reference count drops to zero. */ void rpc_release_client (struct rpc_clnt *clnt)
net/sunrpc/xprtsock.c +10 -1
@@ -2751,6 +2751,7 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work) out_unlock:current_restore_flags (pflags,PF_MEMALLOC ); upper_transport->clnt =NULL ; +rpc_release_client (upper_clnt);xprt_unlock_connect (upper_xprt, upper_transport);return ;
@@ -2798,7 +2799,15 @@ static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task) }else dprintk ("RPC: xs_connect scheduled xprt %p\n" , xprt); - transport->clnt = task->tk_client; +/* +* Only the TLS connect_worker reads transport->clnt; pinning +* the upper rpc_clnt unconditionally would form a cycle with +* cl_xprt and prevent xprt destruction. +*/ +if (xprt->xprtsec.policy !=RPC_XPRTSEC_NONE ) { +rpc_hold_client (task->tk_client); + transport->clnt = task->tk_client; + }queue_delayed_work (xprtiod_workqueue, &transport->connect_worker, delay);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 883/1266 dm era: fix NULL pointer dereference in metadata_open()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cao Guanghui <[email protected]>
[ Upstream commit 9ae672606c17891d90b282e3490b817620549599 ]
metadata_open() returns NULL when kzalloc_obj() fails, but the caller era_ctr() only checks IS_ERR(md). Since IS_ERR(NULL) returns false, the NULL pointer is treated as a valid result and later assigned to era->md, leading to a NULL pointer dereference when the metadata is accessed.
Fix this by returning ERR_PTR(-ENOMEM) on allocation failure, consistent with dm-cache-metadata.c, dm-thin-metadata.c, and dm-clone-metadata.c which all use ERR_PTR(-ENOMEM) for the same pattern.
--- drivers/md/dm-era-target.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/md/dm-era-target.c +4 -2
@@ -810,8 +810,10 @@ static struct era_metadata *metadata_open(struct block_device *bdev,int r;struct era_metadata *md =kzalloc (sizeof (*md),GFP_KERNEL ); -if (!md) -return NULL ; +if (!md) { +DMERR ("could not allocate metadata struct" ); +return ERR_PTR (-ENOMEM ); + } md->bdev = bdev; md->block_size = block_size;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 884/1266 regulator: core: regulator_lock_two() should test for EDEADLK not EDEADLOCK
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Timur Tabi <[email protected]>
[ Upstream commit d38f8bd771c4999b797d7074b348cf201414bd34 ]
Compare against -EDEADLK, which is what ww_mutex_lock() actually returns and what every other deadlock check in this file already uses.
Function regulator_lock_two() acquires two regulators via regulator_lock_nested() -> ww_mutex_lock(). On contention, ww_mutex_lock() returns -EDEADLK, which is the caller's signal to drop the lock it holds and retry the acquisition in the canonical order.
However, regulator_lock_two() tests the return value against -EDEADLOCK rather than -EDEADLK. On most architectures, EDEADLK and EDEADLOCK are the same value, so the comparison happens to be correct and the bug is invisible. But on MIPS, SPARC, and PowerPC, those two errors have different values. The test is wrong: a genuine -EDEADLK backoff no longer matches -EDEADLOCK, so instead of unlocking and retrying, the code falls into WARN_ON(ret) and returns with only one of the two regulators locked.
In practice, this is a bug only on MIPS, because the regulator core is not built or used on the other two platforms.
In general, EDEADLK is preferred over EDEADLOCK for new code.
--- drivers/regulator/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/regulator/core.c +2 -2
@@ -228,7 +228,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1, ret =regulator_lock_nested (rdev1, ww_ctx);WARN_ON (ret); ret =regulator_lock_nested (rdev2, ww_ctx); -if (ret != -EDEADLOCK ) { +if (ret != -EDEADLK ) {WARN_ON (ret); goto exit; }
@@ -244,7 +244,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1,swap (held, contended); ret =regulator_lock_nested (contended, ww_ctx); -if (ret != -EDEADLOCK ) { +if (ret != -EDEADLK ) {WARN_ON (ret);break ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 885/1266 selftests/net: fix EVP_MD_CTX leak in tcp_mmap
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wang Yan <[email protected]>
[ Upstream commit f4ef35efbb49527293309f668ea73ec5de9b8e7a ]
In tcp_mmap.c, both child_thread() and main() allocate an EVP_MD_CTX via EVP_MD_CTX_new() when integrity checking is enabled, but neither function releases the context. child_thread() misses the free in its common cleanup block, and main() returns without freeing the context.
This results in a SHA256 context leak on every run that uses the ‑i (integrity) option. Add the missing EVP_MD_CTX_free() calls to the appropriate cleanup paths to fix the leak.
--- tools/testing/selftests/net/tcp_mmap.c | 4 ++++ 1 file changed, 4 insertions(+)
tools/testing/selftests/net/tcp_mmap.c +4 -0
@@ -313,6 +313,8 @@ void *child_thread(void *arg) tcp_info_get_rcv_mss(fd)); } error: +if (ctx) +EVP_MD_CTX_free (ctx);munmap (buffer, buffer_sz);close (fd);if (zflg)
@@ -606,6 +608,8 @@ int main(int argc, char *argv[])EVP_DigestFinal_ex (ctx, digest, &digest_len);send (fd, digest, (size_t )SHA256_DIGEST_LENGTH ,0 ); } +if (ctx) +EVP_MD_CTX_free (ctx);close (fd);munmap (buffer, buffer_sz);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 886/1266 net/mlx5: Fix L3 tunnel entropy refcount leak
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Li RongQing <[email protected]>
[ Upstream commit c914307e1d41c2cb7bcdcbfde4cd2f214f6aa027 ]
mlx5_tun_entropy_refcount_inc() counts both VXLAN and L2-to-L3 tunnel reformat entries as entropy-enabling users. The matching decrement path only handled VXLAN, leaving L2-to-L3 tunnel entries counted after release.
Handle MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL in mlx5_tun_entropy_refcount_dec() as well so the enabling entry refcount remains balanced.
--- drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c +2 -1
@@ -176,7 +176,8 @@ void mlx5_tun_entropy_refcount_dec(struct mlx5_tun_entropy *tun_entropy, int reformat_type) {mutex_lock (&tun_entropy->lock); -if (reformat_type ==MLX5_REFORMAT_TYPE_L2_TO_VXLAN ) +if (reformat_type ==MLX5_REFORMAT_TYPE_L2_TO_VXLAN || + reformat_type ==MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL ) tun_entropy->num_enabling_entries--;else if (reformat_type ==MLX5_REFORMAT_TYPE_L2_TO_NVGRE && --tun_entropy->num_disabling_entries ==0 )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 887/1266 octeontx2-af: fix VF bringup affecting PF promiscuous state
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harman Kalra <[email protected]>
[ Upstream commit fabb881df322da25442f98d23f5fa371e3c78ec4 ]
Mbox handling of nix_set_rx_mode for a VF with promiscuous and all_multi flags set to false causes deletion of the PF's promiscuous and allmulti MCAM rules. This occurs because the APIs that enable/disable these rules operate only on the PF, even when the mbox request is made via a VF interface.
Guard both rvu_npc_enable_allmulti_entry() and rvu_npc_enable_promisc_entry() disable paths with an is_vf() check so that a VF bringing up or tearing down its interface cannot inadvertently clear the PF's MCAM rules.
--- drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +2 -2
@@ -4121,7 +4121,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req,rvu_npc_install_allmulti_entry (rvu, pcifunc, nixlf, pfvf->rx_chan_base); }else { -if (!nix_rx_multicast) +if (!nix_rx_multicast && !is_vf (pcifunc))rvu_npc_enable_allmulti_entry (rvu, pcifunc, nixlf, false); }
@@ -4131,7 +4131,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, pfvf->rx_chan_base, pfvf->rx_chan_cnt); else - if (!nix_rx_multicast) + if (!nix_rx_multicast && !is_vf(pcifunc))rvu_npc_enable_promisc_entry (rvu, pcifunc, nixlf, false);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 888/1266 smb: client: fix overflow in passthrough ioctl bounds check
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
[ Upstream commit a4f27ad055392fa164f5649e89a3637b033c5fcc ]
smb2_ioctl_query_info() validates the PASSTHRU_FSCTL response payload before copying it to userspace.
The payload offset and length both come from 32-bit fields. The bounds check currently adds OutputOffset and qi.input_buffer_length directly, so the addition can wrap in 32-bit arithmetic before the result is compared against the response buffer length.
A malicious server can use a large OutputOffset and a small OutputCount to make the wrapped sum pass the bounds check. The later copy_to_user() then reads from io_rsp + OutputOffset, outside the response buffer.
Use size_add() for the offset plus length check so overflow is treated as out of bounds.
--- fs/smb/client/smb2ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/smb/client/smb2ops.c +2 -2
@@ -1728,8 +1728,8 @@ smb2_ioctl_query_info(const unsigned int xid,if (le32_to_cpu (io_rsp->OutputCount) < qi.input_buffer_length) qi.input_buffer_length =le32_to_cpu (io_rsp->OutputCount);if (qi.input_buffer_length >0 && -le32_to_cpu (io_rsp->OutputOffset) + qi.input_buffer_length - > rsp_iov[1 ].iov_len) { +size_add (le32_to_cpu (io_rsp->OutputOffset), + qi.input_buffer_length) > rsp_iov[1 ].iov_len) { rc = -EFAULT ; goto out; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 889/1266 mlxsw: fix refcount leak in mlxsw_sp_port_lag_join()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Liang <[email protected]>
commit 41c8c1d65b32beacd8d916a22457b4f6e47f45af upstream.
When mlxsw_sp_port_lag_index_get() fails, mlxsw_sp_port_lag_join() returns an error without releasing the lag reference obtained by the earlier mlxsw_sp_lag_get(). All other error paths in the function jump to the cleanup label that ends with mlxsw_sp_lag_put(), so this is a single missed release.
Fix the leak by replacing the bare 'return err' with a goto to the existing error cleanup label, which will drop the reference safely.
--- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/mellanox/mlxsw/spectrum.c +1 -1
@@ -4427,7 +4427,7 @@ static int mlxsw_sp_port_lag_join(struct err =mlxsw_sp_port_lag_index_get (mlxsw_sp, lag_id, &port_index);if (err) -return err; + goto err_lag_uppers_bridge_join; err =mlxsw_sp_lag_uppers_bridge_join (mlxsw_sp_port, lag_dev, extack);
Greg Kroah-Hartman
maintainerPATCH 890/1266 mlxsw: fix refcount leak in mlxsw_sp_vrs_lpm_tree_replace()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Liang <[email protected]>
commit 21cf8dc478a49e8de039c2739b1646a774cb1944 upstream.
When mlxsw_sp_vrs_lpm_tree_replace() fails after replacing some VRs, the error rollback loop does not correctly revert the preceding replacements. The loop decrements the index but fails to update the vr pointer, which still points to the VR that caused the failure. As a result, the condition and the rollback call always operate on the same VR, potentially calling mlxsw_sp_vr_lpm_tree_replace() multiple times on it while never rolling back the earlier VRs. Those VRs continue to hold a reference to new_tree acquired via mlxsw_sp_lpm_tree_hold(), leaking the reference count of new_tree.
Fix by reinitializing vr inside the error loop with the updated index:
vr = &mlxsw_sp->router->vrs[i];
so that the loop correctly iterates over all VRs that were actually replaced.
--- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +1 -0
@@ -1018,6 +1018,7 @@ static int mlxsw_sp_vrs_lpm_tree_replace err_tree_replace:for (i--; i >=0 ; i--) { + vr = &mlxsw_sp->router->vrs[i];if (!mlxsw_sp_vr_lpm_tree_should_replace (vr, proto, new_id))continue ; mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
Greg Kroah-Hartman
maintainerPATCH 891/1266 vduse: Fix race in vduse_dev_msg_sync and vduse_dev_read_iter
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhang Tianci <[email protected]>
commit ae9c13b6fd79087cc5a216ee1649b6f012c2a238 upstream.
There is one race case in vduse_dev_msg_sync and vduse_dev_read_iter:
vduse_dev_read_iter():
lock(msg_lock);
dequeue_msg(send_list);
unlock(msg_lock);
vduse_dev_msg_sync():
wait_timeout() finish
lock(msg_lock);
check msg->complete is false
list_del(msg); <- double list_del() crash!To fix this case, we shall ensure vduse_msg is on send_list or recv_list outside the msg_lock critical section.
Message-ID: <[email protected]>
--- drivers/vdpa/vdpa_user/vduse_dev.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-)
drivers/vdpa/vdpa_user/vduse_dev.c +27 -10
@@ -330,6 +330,7 @@ static ssize_t vduse_dev_read_iter(strucstruct file *file = iocb->ki_filp;struct vduse_dev *dev = file->private_data;struct vduse_dev_msg *msg; +struct vduse_dev_request req;int size =sizeof (struct vduse_dev_request );ssize_t ret;
@@ -341,12 +342,11 @@ static ssize_t vduse_dev_read_iter(struc msg =vduse_dequeue_msg (&dev->send_list);if (msg)break ; +spin_unlock (&dev->msg_lock); - ret = -EAGAIN ;if (file->f_flags &O_NONBLOCK ) - goto unlock; +return -EAGAIN ; -spin_unlock (&dev->msg_lock); ret =wait_event_interruptible_exclusive (dev->waitq, !list_empty (&dev->send_list));if (ret)
@@ -354,17 +354,34 @@ static ssize_t vduse_dev_read_iter(strucspin_lock (&dev->msg_lock); } + +memcpy (&req, &msg->req,sizeof (req)); +/* +* We must ensure vduse_msg is on send_list or recv_list before unlock +* dev->msg_lock. Because vduse_dev_msg_sync() may be timeout when we +* copy data to userspace, and will call list_del() for this msg. +*/ +vduse_enqueue_msg (&dev->recv_list, msg);spin_unlock (&dev->msg_lock); - ret =copy_to_iter (&msg->req, size, to); -spin_lock (&dev->msg_lock); + + ret =copy_to_iter (&req, size, to);if (ret != size) { +/* +* Roll back: move msg back to send_list if still pending. +* +* NOTE: +* vduse_find_msg() must use req.request_id instead of `msg`. +* A malicious userspace may reply to this request, and wake up +* the caller, after which `msg` will have already been freed. +* And here vduse_find_msg() will return NULL then do nothing. +*/ +spin_lock (&dev->msg_lock); + msg =vduse_find_msg (&dev->recv_list, req.request_id); +if (msg) +vduse_enqueue_msg_head (&dev->send_list, msg); +spin_unlock (&dev->msg_lock); ret = -EFAULT ; -vduse_enqueue_msg_head (&dev->send_list, msg); - goto unlock; } -vduse_enqueue_msg (&dev->recv_list, msg); -unlock: -spin_unlock (&dev->msg_lock);return ret; }
Greg Kroah-Hartman
maintainerPATCH 892/1266 ASoC: SOF: ipc4-control: Fix TOCTOU in sof_ipc4_bytes_put
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Ujfalusi <[email protected]>
commit 3ad673e7139cf214afd24321a829aad6575f4163 upstream.
In sof_ipc4_bytes_put(), the copy size is derived from the old data->size in the buffer rather than the incoming new data's size field from ucontrol. If the new data has a different size, the copy uses the wrong length: it may truncate valid data or copy stale bytes.
Fix by validating and using the incoming data's sof_abi_hdr.size from ucontrol before copying.
--- sound/soc/sof/ipc4-control.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
sound/soc/sof/ipc4-control.c +7 -4
@@ -407,6 +407,8 @@ static int sof_ipc4_bytes_put(struct sndstruct snd_soc_component *scomp = scontrol->scomp;struct snd_sof_dev *sdev =snd_soc_component_get_drvdata (scomp);struct sof_abi_hdr *data = cdata->data; +const struct sof_abi_hdr *new_hdr = + (const struct sof_abi_hdr *)ucontrol->value.bytes.data;size_t size;int ret;
@@ -417,15 +419,16 @@ static int sof_ipc4_bytes_put(struct sndreturn -EINVAL ; } -/* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ -if (data->size > scontrol->max_size -sizeof (*data)) { +/* Validate the new data's size, not the old one */ +if (new_hdr->size > scontrol->max_size -sizeof (*new_hdr)) {dev_err_ratelimited (scomp->dev,"data size too big %u bytes max is %zu\n" , - data->size, scontrol->max_size -sizeof (*data)); + new_hdr->size, + scontrol->max_size -sizeof (*new_hdr));return -EINVAL ; } - size = data->size +sizeof (*data); + size = new_hdr->size +sizeof (*new_hdr);/* copy from kcontrol */ memcpy (data, ucontrol->value.bytes.data, size);
Greg Kroah-Hartman
maintainerPATCH 893/1266 ASoC: SOF: ipc3-control: Use overflow checks in control_update size calc
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Ujfalusi <[email protected]>
commit 8791977d7289f6e9d2b014f60a5455f053a7bc04 upstream.
In sof_ipc3_control_update(), the expected_size calculation uses firmware-provided cdata->num_elems in arithmetic that could overflow on 32-bit platforms, wrapping to a small value. This would allow the cdata->rhdr.hdr.size comparison to pass with mismatched sizes, potentially leading to out-of-bounds access in snd_sof_update_control.
Use check_mul_overflow() and check_add_overflow() to detect and reject overflowed size calculations.
--- sound/soc/sof/ipc3-control.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-)
sound/soc/sof/ipc3-control.c +16 -4
@@ -626,16 +626,28 @@ static void sof_ipc3_control_update(strureturn ; } - expected_size =sizeof (struct sof_ipc_ctrl_data );switch (cdata->type) {case SOF_CTRL_TYPE_VALUE_CHAN_GET :case SOF_CTRL_TYPE_VALUE_CHAN_SET : - expected_size += cdata->num_elems * -sizeof (struct sof_ipc_ctrl_value_chan ); +if (check_mul_overflow ((size_t )cdata->num_elems, +sizeof (struct sof_ipc_ctrl_value_chan ), + &expected_size)) +return ; +if (check_add_overflow (expected_size, +sizeof (struct sof_ipc_ctrl_data ), + &expected_size)) +return ;break ;case SOF_CTRL_TYPE_DATA_GET :case SOF_CTRL_TYPE_DATA_SET : - expected_size += cdata->num_elems +sizeof (struct sof_abi_hdr ); +if (check_add_overflow ((size_t )cdata->num_elems, +sizeof (struct sof_abi_hdr ), + &expected_size)) +return ; +if (check_add_overflow (expected_size, +sizeof (struct sof_ipc_ctrl_data ), + &expected_size)) +return ;break ;default :return ;
Greg Kroah-Hartman
maintainerPATCH 894/1266 ASoC: SOF: ipc3-control: Fix TOCTOU in bytes_put and bytes_get
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Ujfalusi <[email protected]>
commit 1f97760417b5faa60e9642fd0ed61eb17d0b1b39 upstream.
In sof_ipc3_bytes_put(), the size used for the memcpy is derived from the old data->size already in the buffer, not the incoming new data's size field. If the new data has a different size, the copy length is
wrong: it may truncate valid data or copy stale bytes.
Similarly, sof_ipc3_bytes_get() checks data->size against max_size without accounting for the sizeof(struct sof_ipc_ctrl_data) offset of the flex array within the allocation.
Fix bytes_put to validate and use the incoming data's sof_abi_hdr.size from ucontrol before copying. Fix bytes_get to subtract sizeof(*cdata) from the bounds check to match the actual available space.
--- sound/soc/sof/ipc3-control.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-)
sound/soc/sof/ipc3-control.c +16 -7
@@ -315,10 +315,13 @@ static int sof_ipc3_bytes_get(struct snd }/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ -if (data->size > scontrol->max_size -sizeof (*data)) { +if (data->size > scontrol->max_size -sizeof (*cdata) - +sizeof (*data)) {dev_err_ratelimited (scomp->dev,"%u bytes of control data is invalid, max is %zu\n" , - data->size, scontrol->max_size -sizeof (*data)); + data->size, + scontrol->max_size -sizeof (*cdata) - +sizeof (*data));return -EINVAL ; }
@@ -336,6 +339,8 @@ static int sof_ipc3_bytes_put(struct sndstruct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;struct snd_soc_component *scomp = scontrol->scomp;struct sof_abi_hdr *data = cdata->data; +const struct sof_abi_hdr *new_hdr = + (const struct sof_abi_hdr *)ucontrol->value.bytes.data;size_t size;if (scontrol->max_size >sizeof (ucontrol->value.bytes.data)) {
@@ -344,14 +349,18 @@ static int sof_ipc3_bytes_put(struct sndreturn -EINVAL ; } -/* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ -if (data->size > scontrol->max_size -sizeof (*data)) { -dev_err_ratelimited (scomp->dev,"data size too big %u bytes max is %zu\n" , - data->size, scontrol->max_size -sizeof (*data)); +/* Validate the new data's size, not the old one */ +if (new_hdr->size > scontrol->max_size -sizeof (*cdata) - +sizeof (*new_hdr)) { +dev_err_ratelimited (scomp->dev, +"data size too big %u bytes max is %zu\n" , + new_hdr->size, + scontrol->max_size -sizeof (*cdata) - +sizeof (*new_hdr));return -EINVAL ; } - size = data->size +sizeof (*data); + size = new_hdr->size +sizeof (*new_hdr);/* copy from kcontrol */ memcpy (data, ucontrol->value.bytes.data, size);
Greg Kroah-Hartman
maintainerPATCH 895/1266 ASoC: SOF: topology: validate vendor array size before parsing
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cássio Gabriel <[email protected]>
commit 8468dd79cfb2ffbdeaf7c353f63d64941cb8ba05 upstream.
sof_parse_token_sets() reads array->size while iterating over topology private data. The loop condition only checks that some data remains, so a malformed topology with a truncated trailing vendor array can make the parser read the size field before a full vendor-array header is available.
Validate that the remaining private data contains a complete snd_soc_tplg_vendor_array header before reading array->size.
The declared array size check also needs to remain signed. asize is an int, but sizeof(*array) has type size_t, so comparing them directly promotes negative asize values to unsigned and lets them pass the check, as reported in the stable review thread reference below.
Cast sizeof(*array) to int when validating the declared array size. This rejects negative, zero and otherwise too-small sizes before the parser dispatches to the tuple-specific code.
--- sound/soc/sof/topology.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
sound/soc/sof/topology.c +4 -1
@@ -719,10 +719,13 @@ static int sof_parse_token_sets(struct sint ret;while (array_size >0 && total < count * token_instance_num) { +if (array_size < (int )sizeof (*array)) +return -EINVAL ; + asize =le32_to_cpu (array->size);/* validate asize */ -if (asize <sizeof (*array)) { +if (asize < (int )sizeof (*array)) {dev_err (scomp->dev,"error: invalid array size 0x%x\n" , asize);return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 896/1266 net: qrtr: fix 32-bit integer overflow in qrtr_endpoint_post()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 20054869770c7df060c5ecee3e8bbf9029c47191 upstream.
qrtr_endpoint_post() validates an incoming packet with
if (!size || len != ALIGN(size, 4) + hdrlen) goto err;
where size comes from the wire. On 32-bit, size_t is 32 bits and ALIGN(size, 4) wraps to 0 for size >= 0xfffffffd, so the check passes and skb_put_data(skb, data + hdrlen, size) writes past the hdrlen-sized skb and oopses the kernel. 64-bit is unaffected.
This is the 32-bit residual of ad9d24c9429e2 ("net: qrtr: fix OOB Read in qrtr_endpoint_post"), which fixed only the 64-bit case.
Reject any size that cannot fit the buffer before the ALIGN.
--- net/qrtr/af_qrtr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/qrtr/af_qrtr.c +1 -1
@@ -496,7 +496,7 @@ int qrtr_endpoint_post(struct qrtr_endpoif (cb->dst_port ==QRTR_PORT_CTRL_LEGACY ) cb->dst_port =QRTR_PORT_CTRL ; -if (!size || len !=ALIGN (size,4 ) + hdrlen) +if (!size || size > len || len !=ALIGN (size,4 ) + hdrlen) goto err; if ((cb->type == QRTR_TYPE_NEW_SERVER ||
Greg Kroah-Hartman
maintainerPATCH 897/1266 net: atm: reject out-of-range traffic classes in QoS validation
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhengchuan Liang <[email protected]>
commit cdf19f380e46192e7084be559638aab1f6ed86a2 upstream.
Reject ATM traffic classes above ATM_ANYCLASS in check_tp(). SO_ATMQOS stores the supplied QoS after check_qos() succeeds, so accepting larger values leaves invalid traffic_class values in vcc->qos.
That bad state later reaches pvc_info(), which indexes class_name[] with vcc->qos.{rx,tp}.traffic_class. Values above ATM_ANYCLASS cause an out-of-bounds read when /proc/net/atm/pvc is read.
Tighten the existing QoS validation so invalid traffic_class values are rejected at the point where user supplied QoS is accepted.
--- net/atm/common.c | 2 ++ 1 file changed, 2 insertions(+)
net/atm/common.c +2 -0
@@ -720,6 +720,8 @@ static int atm_change_qos(struct atm_vccstatic int check_tp (const struct atm_trafprm *tp) {/* @@@ Should be merged with adjust_tp */ +if (tp->traffic_class >ATM_ANYCLASS ) +return -EINVAL ;if (!tp->traffic_class || tp->traffic_class ==ATM_ANYCLASS )return 0 ; if (tp->traffic_class != ATM_UBR && !tp->min_pcr && !tp->pcr &&
Greg Kroah-Hartman
maintainerPATCH 898/1266 net: ife: require ETH_HLEN to be pullable in ife_decode()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yong Wang <[email protected]>
commit 9406f6012b7343661efb516a11c62d4db2b62f75 upstream.
ife decode may return after making only the outer IFE header and metadata pullable. The caller then passes the decapsulated packet to eth_type_trans(), which expects the inner Ethernet header to be accessible from the linear data area.
With a malformed IFE frame, the inner Ethernet header may still be shorter than ETH_HLEN in the linear area, which can lead to a crash in the original code.
Fix this by extending the pull check in ife_decode() so that the inner Ethernet header is also guaranteed to be pullable before returning.
--- net/ife/ife.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ife/ife.c +1 -1
@@ -79,7 +79,7 @@ void *ife_decode(struct sk_buff *skb, u1if (unlikely (ifehdrln <2 ))return NULL ; -if (unlikely (!pskb_may_pull (skb, total_pull))) +if (unlikely (!pskb_may_pull (skb, total_pull +ETH_HLEN )))return NULL ; ifehdr = (struct ifeheadr *)(skb->data + skb->dev->hard_header_len);
Greg Kroah-Hartman
maintainerPATCH 899/1266 arm64: fpsimd: Fix type mismatch in sve_{save,load}_state()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mark Rutland <[email protected]>
commit ae24f6b06e90681ec36b9c21c3f5c09618350f5a upstream.
The sve_save_state() and sve_load_state() functions take a 32-bit int argument that describes whether to save/restore the FFR. Their assembly implementations consume the entire 64-bit register containing this 32-bit value, and will attempt to save/restore the FFR if any bit of that 64-bit register is non-zero.
Per the AAPCS64 parameter passing rules, the callee is responsible for any necessary widening, and the upper 32-bits are permitted to contain arbitrary values. If the upper 32 bits are non-zero, this could result in an unexpected attempt to save/restore the FFR, and consequently could lead to unexpected traps/undefs/faults.
In practice compilers are very unlikely to generate code where the upper 32-bits would be non-zero, but they are permitted to do so.
Fix this by only consuming the low 32 bits of the register, and update comments accordingly.
The hyp code __sve_save_state() and __sve_restore_state() functions don't have the same latent bug as they override the full 64-bit register containing the argument.
--- arch/arm64/kernel/entry-fpsimd.S | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
arch/arm64/kernel/entry-fpsimd.S +4 -4
@@ -38,10 +38,10 @@ SYM_FUNC_END(fpsimd_load_state) * * x0 - pointer to buffer for state * x1 - pointer to storage for FPSR - * x2 - Save FFR if non-zero + * w2 - Save FFR if non-zero */ SYM_FUNC_START(sve_save_state) - sve_save 0, x1, x2, 3 + sve_save 0, x1, w2, 3 ret SYM_FUNC_END(sve_save_state)
@@ -50,10 +50,10 @@ SYM_FUNC_END(sve_save_state) * * x0 - pointer to buffer for state * x1 - pointer to storage for FPSR - * x2 - Restore FFR if non-zero + * w2 - Restore FFR if non-zero */ SYM_FUNC_START(sve_load_state) - sve_load 0, x1, x2, 4 + sve_load 0, x1, w2, 4 ret SYM_FUNC_END(sve_load_state)
Greg Kroah-Hartman
maintainerPATCH 900/1266 arm64: dts: qcom: sdm630: describe adsp_mem region properly
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nickolay Goppen <[email protected]>
commit ce414263e9ebe5080381a50cbdf9065c29816202 upstream.
Downstream [1] this region is marked as shared, reusable and dynamic so describe it that way.
--- arch/arm64/boot/dts/qcom/sdm630.dtsi | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
arch/arm64/boot/dts/qcom/sdm630.dtsi +6 -3
@@ -491,9 +491,12 @@ no-map; }; - adsp_mem: adsp-region@f6000000 { - reg = <0x0 0xf6000000 0x0 0x800000>; - no-map; + adsp_mem: adsp-region { + compatible = "shared-dma-pool"; + alloc-ranges = <0x0 0x80000000 0x0 0x80000000>; + alignment = <0x0 0x400000>; + size = <0x0 0x800000>; + reusable; }; qseecom_mem: qseecom-region@f6800000 {
Greg Kroah-Hartman
maintainerPATCH 901/1266 KVM: s390: pci: Fix GISC refcount leak on AIF enable failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
commit 7b69729046a4c58f4cb457184e5ac4aaa179bff4 upstream.
kvm_s390_gisc_register() registers the guest ISC before pinning the guest interrupt forwarding pages and allocating the AISB bit. If any of the later setup steps fails, the function unwinds the pinned pages and other local state, but does not unregister the GISC reference. Add the missing kvm_s390_gisc_unregister() to the error unwind path.
Message-ID: <[email protected]>
--- arch/s390/kvm/pci.c | 1 + 1 file changed, 1 insertion(+)
arch/s390/kvm/pci.c +1 -0
@@ -330,6 +330,7 @@ unpin2: unpin1:unpin_user_page (aibv_page); out: +kvm_s390_gisc_unregister (kvm, fib->fmt0.isc);return rc; }
Greg Kroah-Hartman
maintainerPATCH 902/1266 KVM: arm64: vgic: Check the interrupt is still ours before migrating it
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hyunwoo Kim <[email protected]>
commit 0074b82cdfcb5fd13710a0ac308ade68ac6f6fbe upstream.
vgic_prune_ap_list() drops both ap_list_lock and irq_lock while migrating an interrupt to another vCPU. After reacquiring the locks it only checks that the affinity is unchanged (target_vcpu == vgic_target_oracle(irq)) before moving the interrupt, which assumes that an interrupt whose affinity is preserved is still queued on this vCPU's ap_list.
That assumption no longer holds if the interrupt is taken off the ap_list while the locks are dropped. vgic_flush_pending_lpis() removes the interrupt from the list and sets irq->vcpu to NULL, but leaves enabled/pending/target_vcpu untouched. As the interrupt is still enabled and pending, vgic_target_oracle() returns the same target_vcpu, so the affinity check passes and list_del() is run a second time on an entry that has already been removed.
Also check that the interrupt is still assigned to this vCPU (irq->vcpu == vcpu) before moving it.
--- arch/arm64/kvm/vgic/vgic.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
arch/arm64/kvm/vgic/vgic.c +6 -5
@@ -701,15 +701,16 @@ retry:raw_spin_lock (&irq->irq_lock);/* -* If the affinity has been preserved, move the -* interrupt around. Otherwise, it means things have -* changed while the interrupt was unlocked, and we -* need to replay this. +* If the interrupt is still ours and its affinity has +* been preserved, move it around. Otherwise, it means +* things have changed while the interrupt was unlocked +* (it may even have been taken off the list with its +* affinity left untouched), and we need to replay this. * * In all cases, we cannot trust the list not to have * changed, so we restart from the beginning. */ -if (target_vcpu ==vgic_target_oracle (irq)) { +if (irq->vcpu == vcpu && target_vcpu ==vgic_target_oracle (irq)) {struct vgic_cpu *new_cpu = &target_vcpu->arch.vgic_cpu;list_del (&irq->ap_list);
Greg Kroah-Hartman
maintainerPATCH 903/1266 KVM: s390: pci: Fix handling of AIF enable without AISB
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matthew Rosato <[email protected]>
commit 3e3aa6da87d30a0064a17b836685cd43c90a3572 upstream.
When a guest seeks to register IRQs without a summary bit specified, ensure that the associated GAITE then stores 0 for the guest AISB location instead of virt_to_phys(page_address(NULL)).
--- arch/s390/kvm/pci.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
arch/s390/kvm/pci.c +8 -3
@@ -302,9 +302,14 @@ static int kvm_s390_pci_aif_enable(struc gaite->gisc = fib->fmt0.isc; gaite->count++; - gaite->aisbo = fib->fmt0.aisbo; - gaite->aisb =virt_to_phys (page_address (aisb_page) + (fib->fmt0.aisb & - ~PAGE_MASK )); +if (fib->fmt0.sum ==1 ) { + gaite->aisbo = fib->fmt0.aisbo; + gaite->aisb =virt_to_phys (page_address (aisb_page) + + (fib->fmt0.aisb & ~PAGE_MASK )); + }else { + gaite->aisbo =0 ; + gaite->aisb =0 ; + } aift->kzdev[zdev->aisb] = zdev->kzdev;spin_unlock_irq (&aift->gait_lock);
Greg Kroah-Hartman
maintainerPATCH 904/1266 KVM: x86: Ignore pending PV EOI if the vCPU has since disabled PV EOIs
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
commit 9285e4070df2c40585c3d7ec9571faa7a2b97e17 upstream.
Ignore KVM's internal "service pending PV EOI" request if the vCPU has disabled PV EOIs since the request was made. Asserting that PV EOIs are enabled can fail if reading guest memory in pv_eoi_get_user() fails, i.e. if pv_eoi_test_and_clr_pending() bails early, *and* the vCPU also disables PV EOIs.
kernel BUG at arch/x86/kvm/lapic.c:3338!
Oops: invalid opcode: 0000 [#1] SMP CPU: 4 UID: 1000 PID: 890 Comm: pv_eoi_test Not tainted 7.0.0-d585aa5894d8-vm #337 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:kvm_lapic_sync_from_vapic+0x12b/0x140 [kvm] Call Trace: <TASK> kvm_arch_vcpu_ioctl_run+0x1075/0x1c30 [kvm] kvm_vcpu_ioctl+0x2d5/0x980 [kvm] __x64_sys_ioctl+0x8a/0xd0 do_syscall_64+0xb5/0xb40 entry_SYSCALL_64_after_hwframe+0x4b/0x53 </TASK> Modules linked in: kvm_intel kvm irqbypass ---[ end trace 0000000000000000 ]---
--- arch/x86/kvm/lapic.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
arch/x86/kvm/lapic.c +6 -2
@@ -3138,6 +3138,12 @@ static void apic_sync_pv_eoi_from_guest( struct kvm_lapic *apic) {int vector; + +if (unlikely (!pv_eoi_enabled (vcpu))) { +__clear_bit (KVM_APIC_PV_EOI_PENDING , &vcpu->arch.apic_attention); +return ; + } +/* * PV EOI state is derived from KVM_APIC_PV_EOI_PENDING in host * and KVM_PV_EOI_ENABLED in guest memory as follows:
@@ -3149,8 +3155,6 @@ static void apic_sync_pv_eoi_from_guest(* KVM_APIC_PV_EOI_PENDING is set, KVM_PV_EOI_ENABLED is unset: * -> host enabled PV EOI, guest executed EOI. */ -BUG_ON (!pv_eoi_enabled (vcpu)); -if (pv_eoi_test_and_clr_pending (vcpu))return ; vector =apic_set_eoi (apic);
Greg Kroah-Hartman
maintainerPATCH 905/1266 fbdev: metronomefb: fix potential memory leak in metronomefb_probe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 894632b862a39b3fe1cb5de06fbae86225ea64de upstream.
The memory allocated for pagerefs in fb_deferred_io_init() is not freed on the error path. Fix it by calling fb_deferred_io_cleanup().
--- drivers/video/fbdev/metronomefb.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/video/fbdev/metronomefb.c +6 -2
@@ -645,12 +645,14 @@ static int metronomefb_probe(struct plat info->flags =FBINFO_VIRTFB ; info->fbdefio = &metronomefb_defio; -fb_deferred_io_init (info); + retval =fb_deferred_io_init (info); +if (retval) + goto err_free_irq; retval =fb_alloc_cmap (&info->cmap,8 ,0 );if (retval <0 ) {dev_err (&dev->dev,"Failed to allocate colormap\n" ); - goto err_free_irq; + goto err_fbdefio; }/* set cmap */
@@ -673,6 +675,8 @@ static int metronomefb_probe(struct plat err_cmap:fb_dealloc_cmap (&info->cmap); +err_fbdefio: +fb_deferred_io_cleanup (info); err_free_irq: board->cleanup (par); err_csum_table:
Greg Kroah-Hartman
maintainerPATCH 906/1266 fbdev: broadsheetfb: fix potential memory leak in broadsheetfb_probe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 9d18a4e4234fd3ee0d0eed8ccbbb50cb76b2232c upstream.
The memory allocated for pagerefs in fb_deferred_io_init() is not freed on the error path. Fix it by calling fb_deferred_io_cleanup().
--- drivers/video/fbdev/broadsheetfb.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/video/fbdev/broadsheetfb.c +6 -2
@@ -1072,12 +1072,14 @@ static int broadsheetfb_probe(struct pla info->flags =FBINFO_VIRTFB ; info->fbdefio = &broadsheetfb_defio; -fb_deferred_io_init (info); + retval =fb_deferred_io_init (info); +if (retval) + goto err_vfree; retval =fb_alloc_cmap (&info->cmap,16 ,0 );if (retval <0 ) {dev_err (&dev->dev,"Failed to allocate colormap\n" ); - goto err_vfree; + goto err_fbdefio; }/* set cmap */
@@ -1121,6 +1123,8 @@ err_free_irq: board->cleanup (par); err_cmap:fb_dealloc_cmap (&info->cmap); +err_fbdefio: +fb_deferred_io_cleanup (info); err_vfree:vfree (videomemory); err_fb_rel:
Greg Kroah-Hartman
maintainerPATCH 907/1266 fbdev: hecubafb: fix potential memory leak in hecubafb_probe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit cbef2a305a8a72969b86f96b7c07b86edde61aff upstream.
The memory allocated for pagerefs in fb_deferred_io_init() is not freed on the error path. Fix it by calling fb_deferred_io_cleanup().
--- drivers/video/fbdev/hecubafb.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/video/fbdev/hecubafb.c +5 -1
@@ -192,7 +192,9 @@ static int hecubafb_probe(struct platfor info->flags =FBINFO_VIRTFB ; info->fbdefio = &hecubafb_defio; -fb_deferred_io_init (info); + retval =fb_deferred_io_init (info); +if (retval) + goto err_fbdefio; retval =register_framebuffer (info);if (retval <0 )
@@ -209,6 +211,8 @@ static int hecubafb_probe(struct platforreturn 0 ; err_fbreg: +fb_deferred_io_cleanup (info); +err_fbdefio:framebuffer_release (info); err_fballoc:vfree (videomemory);
Greg Kroah-Hartman
maintainerPATCH 908/1266 fbdev: sm712: Fix operator precedence in big_swap macro
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Li RongQing <[email protected]>
commit 5dbe5b65df0b0c0ec77492427c274b7b5011890e upstream.
The big_swap(p) macro was intended to swap bytes within 16-bit halves of a 32-bit value. However, because the bitwise shift operators (<<, >>) have higher precedence than the bitwise AND operator (&), the original code failed to perform any shifting on the masked bits.
For example, 'p & 0xff00ff00 >> 8' was evaluated as 'p & (0xff00ff00 >> 8)', effectively neutralizing the intended swap.
Fix this by adding parentheses to ensure the bitwise AND is performed before the shift, correctly implementing the byte swap logic.
--- drivers/video/fbdev/sm712.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/video/fbdev/sm712.h +1 -1
@@ -101,7 +101,7 @@ struct modeinit {#define mmio_addr 0x00800000#define seqw17 () smtc_seqw(0x17, 0x30)#define big_pixel_depth (p, d) {if (p == 24) {p = 32; d = 32; } } -#define big_swap (p) ((p & 0xff00ff00 >> 8) | (p & 0x00ff00ff << 8)) +#define big_swap (p) (((p & 0xff00ff00) >> 8) | ((p & 0x00ff00ff) << 8))#else #define pal_rgb (r, g, b, val) val#define big_addr 0
Greg Kroah-Hartman
maintainerPATCH 909/1266 fbdev: radeon: fix potential memory leak in radeonfb_pci_register()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit df8c1101c9a08859da612b5d0a08d55d475522c6 upstream.
The function radeonfb_pci_register() allocates memory for modelist (by calling radeon_check_modes() which calls fb_add_videomode()). The memory is appended to info->modelist, but is not freed in subsequent error paths. Fix this by calling fb_destroy_modelist().
--- drivers/video/fbdev/aty/radeon_base.c | 1 + 1 file changed, 1 insertion(+)
drivers/video/fbdev/aty/radeon_base.c +1 -0
@@ -2474,6 +2474,7 @@ static int radeonfb_pci_register(structreturn 0 ; err_unmap_fb:iounmap (rinfo->fb_base); +fb_destroy_modelist (&info->modelist); err_unmap_rom:kfree (rinfo->mon1_EDID);kfree (rinfo->mon2_EDID);
Greg Kroah-Hartman
maintainerPATCH 910/1266 fbdev: i740fb: fix potential memory leak in i740fb_probe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 5936063409af230a2c88b8700c47b89a19fd70b5 upstream.
In i740fb_probe(), the memory allocated in fb_videomode_to_modelist() for modelist is not freed in the error paths. Fix that by calling fb_destroy_modelist().
--- drivers/video/fbdev/i740fb.c | 1 + 1 file changed, 1 insertion(+)
drivers/video/fbdev/i740fb.c +1 -0
@@ -1153,6 +1153,7 @@ err_reg_framebuffer:fb_dealloc_cmap (&info->cmap); err_alloc_cmap: err_find_mode: +fb_destroy_modelist (&info->modelist);if (par->ddc_registered)i2c_del_adapter (&par->ddc_adapter);pci_iounmap (dev, par->regs);
Greg Kroah-Hartman
maintainerPATCH 911/1266 fbdev: s3fb: fix potential memory leak in s3_pci_probe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 3b0ed04bc852887a9164e1bbf521652e8ef3eb92 upstream.
In s3_pci_probe(), the memory allocated for modelist using fb_videomode_to_modelist() is not freed in subsequent error paths. Fix that by calling fb_destroy_modelist()
--- drivers/video/fbdev/s3fb.c | 1 + 1 file changed, 1 insertion(+)
drivers/video/fbdev/s3fb.c +1 -0
@@ -1374,6 +1374,7 @@ err_reg_fb: err_alloc_cmap: err_find_mode:#ifdef CONFIG_FB_S3_DDC +fb_destroy_modelist (&info->modelist);if (par->ddc_registered)i2c_del_adapter (&par->ddc_adapter);if (par->mmio)
Greg Kroah-Hartman
maintainerPATCH 912/1266 fbdev: uvesafb: fix potential memory leak in uvesafb_probe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 033e56fed09047ee63072e9f58789f40c1c7079d upstream.
Due to an incorrect goto label, memory allocated for modedb and modelist in uvesafb_vbe_init() is not freed in some error paths. Fix this by updating the goto label.
--- drivers/video/fbdev/uvesafb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/video/fbdev/uvesafb.c +2 -2
@@ -1697,14 +1697,14 @@ static int uvesafb_probe(struct platform i =uvesafb_vbe_init_mode (info);if (i <0 ) { err = -EINVAL ; - goto out; + goto out_mode; }else { mode = &par->vbe_modes[i]; }if (fb_alloc_cmap (&info->cmap,256 ,0 ) <0 ) { err = -ENXIO ; - goto out; + goto out_mode; }uvesafb_init_info (info, mode);
Greg Kroah-Hartman
maintainerPATCH 913/1266 fbdev: tdfxfb: fix potential memory leak in tdfxfb_probe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit bb019d755366cc3e777a12d4bf457ff289837370 upstream.
In tdfxfb_probe(), the memory allocated for modelist using fb_videomode_to_modelist() when CONFIG_FB_3DFX_I2C is defined, is not freed in the subsequent error paths.
Fix that by calling fb_destroy_modelist().
--- drivers/video/fbdev/tdfxfb.c | 1 + 1 file changed, 1 insertion(+)
drivers/video/fbdev/tdfxfb.c +1 -0
@@ -1551,6 +1551,7 @@ static int tdfxfb_probe(struct pci_dev * out_err_iobase:#ifdef CONFIG_FB_3DFX_I2C +fb_destroy_modelist (&info->modelist);tdfxfb_delete_i2c_busses (default_par);#endif arch_phys_wc_del (default_par->wc_cookie);
Greg Kroah-Hartman
maintainerPATCH 914/1266 fbdev: carminefb: fix potential memory leak in alloc_carmine_fb()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 6fcca16a2b19c37f60693c56cbc0c923364ff3ef upstream.
The memory allocated for modelist in fb_videomode_to_modelist() is not freed in the subsequent error path.
Fix that by calling fb_destroy_modelist()
--- drivers/video/fbdev/carminefb.c | 1 + 1 file changed, 1 insertion(+)
drivers/video/fbdev/carminefb.c +1 -0
@@ -589,6 +589,7 @@ static int alloc_carmine_fb(void __iomemreturn 0 ; err_dealloc_cmap: +fb_destroy_modelist (&info->modelist);fb_dealloc_cmap (&info->cmap); err_free_fb:framebuffer_release (info);
Greg Kroah-Hartman
maintainerPATCH 915/1266 fbdev: vesafb: fix memory leak in vesafb_probe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit b15d708995c01bbffe7dcd634a31959f6805bed3 upstream.
Since commit 73ce73c30ba9 ("fbdev: Transfer video= option strings to caller; clarify ownership") the string returned from fb_get_options() is expected to be freed by the caller. But the string is not freed in vesafb_probe(). Fix that by freeing the option string after setup.
--- drivers/video/fbdev/vesafb.c | 1 + 1 file changed, 1 insertion(+)
drivers/video/fbdev/vesafb.c +1 -0
@@ -255,6 +255,7 @@ static int vesafb_probe(struct platform_/* ignore error return of fb_get_options */ fb_get_options ("vesafb" , &option);vesafb_setup (option); +kfree (option);if (si->orig_video_isVGA !=VIDEO_TYPE_VLFB )return -ENODEV ;
Greg Kroah-Hartman
maintainerPATCH 916/1266 fbdev: nvidia: fix potential memory leak in nvidiafb_probe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 85f5e38c162bdf9dbbe197275d416402712f3707 upstream.
In nvidiafb_probe(), the memory allocated for modelist in nvidia_set_fbinfo() is not freed in the subsequent error paths. Fix that by calling fb_destroy_modelist().
--- drivers/video/fbdev/nvidia/nvidia.c | 1 + 1 file changed, 1 insertion(+)
drivers/video/fbdev/nvidia/nvidia.c +1 -0
@@ -1418,6 +1418,7 @@ static int nvidiafb_probe(struct pci_dev err_out_iounmap_fb:iounmap (info->screen_base); +fb_destroy_modelist (&info->modelist); err_out_free_base1:fb_destroy_modedb (info->monspecs.modedb);nvidia_delete_i2c_busses (par);
Greg Kroah-Hartman
maintainerPATCH 917/1266 fbdev: tridentfb: fix potential memory leak in trident_pci_probe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 7a35ec619d9af8ee128320975c1252b8ad65f1e8 upstream.
In trident_pci_probe(), the memory allocated for modelist using fb_videomode_to_modelist() is not freed in subsequent error paths. Fix that by calling fb_destroy_modelist().
--- drivers/video/fbdev/tridentfb.c | 1 + 1 file changed, 1 insertion(+)
drivers/video/fbdev/tridentfb.c +1 -0
@@ -1705,6 +1705,7 @@ static int trident_pci_probe(struct pci_return 0 ; out_unmap2: +fb_destroy_modelist (&info->modelist);if (default_par->ddc_registered)i2c_del_adapter (&default_par->ddc_adapter);kfree (info->pixmap.addr);
Greg Kroah-Hartman
maintainerPATCH 918/1266 ASoC: SOF: ipc3-control: Fix heap overflow in bytes_ext put/get
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Ujfalusi <[email protected]>
commit fd46668d538993218eea19c6925c868ac0f2630c upstream.
The ipc_control_data buffer is allocated as kzalloc(max_size), where max_size covers the entire struct sof_ipc_ctrl_data including its flexible array payload. However, the bounds checks in bytes_ext_put and _bytes_ext_get compared user data lengths against max_size directly, ignoring that cdata->data sits at an offset of sizeof(struct sof_ipc_ctrl_data) bytes into the allocation.
This allowed writing up to sizeof(struct sof_ipc_ctrl_data) bytes past the end of the heap buffer from unprivileged userspace via the ALSA TLV kcontrol interface, and similarly allowed over-reading adjacent heap data on the get path.
Fix all bounds checks to subtract sizeof(*cdata) from max_size so they reflect the actual space available at the cdata->data offset. Also fix the error-path restore in bytes_ext_put which wrote to cdata->data instead of cdata, causing the same overflow.
--- sound/soc/sof/ipc3-control.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)
sound/soc/sof/ipc3-control.c +19 -8
@@ -398,9 +398,17 @@ static int sof_ipc3_bytes_ext_put(struct }/* be->max is coming from topology */ -if (header.length > scontrol->max_size) { -dev_err_ratelimited (scomp->dev,"Bytes data size %d exceeds max %zu\n" , - header.length, scontrol->max_size); +if (header.length > scontrol->max_size -sizeof (*cdata)) { +dev_err_ratelimited (scomp->dev,"Bytes data size %u exceeds max %zu\n" , + header.length, scontrol->max_size -sizeof (*cdata)); +return -EINVAL ; + } + +/* Ensure the data is large enough to contain the ABI header */ +if (header.length <sizeof (struct sof_abi_hdr )) { +dev_err_ratelimited (scomp->dev, +"Bytes data size %u less than ABI header %zu\n" , + header.length,sizeof (struct sof_abi_hdr ));return -EINVAL ; }
@@ -436,7 +444,7 @@ static int sof_ipc3_bytes_ext_put(struct }/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ -if (cdata->data->size > scontrol->max_size -sizeof (struct sof_abi_hdr )) { +if (cdata->data->size > scontrol->max_size -sizeof (*cdata) -sizeof (struct sof_abi_hdr )) {dev_err_ratelimited (scomp->dev,"Mismatch in ABI data size (truncated?)\n" ); goto err_restore; }
@@ -452,7 +460,7 @@ static int sof_ipc3_bytes_ext_put(struct err_restore:/* If we have an issue, we restore the old, valid bytes control data */ if (scontrol->old_ipc_control_data) { -memcpy (cdata->data, scontrol->old_ipc_control_data, scontrol->max_size); +memcpy (cdata, scontrol->old_ipc_control_data, scontrol->max_size);kfree (scontrol->old_ipc_control_data); scontrol->old_ipc_control_data =NULL ; }
@@ -491,10 +499,13 @@ static int _sof_ipc3_bytes_ext_get(struc }/* check data size doesn't exceed max coming from topology */ -if (cdata->data->size > scontrol->max_size -sizeof (struct sof_abi_hdr )) { -dev_err_ratelimited (scomp->dev,"User data size %d exceeds max size %zu\n" , +if (cdata->data->size > scontrol->max_size -sizeof (*cdata) - +sizeof (struct sof_abi_hdr )) { +dev_err_ratelimited (scomp->dev, +"User data size %u exceeds max size %zu\n" , cdata->data->size, - scontrol->max_size -sizeof (struct sof_abi_hdr )); + scontrol->max_size -sizeof (*cdata) - +sizeof (struct sof_abi_hdr ));return -EINVAL ; }
Greg Kroah-Hartman
maintainerPATCH 919/1266 ASoC: SOF: ipc3-control: Validate size in snd_sof_update_control
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Ujfalusi <[email protected]>
commit 390aa4c9339bb0ec0bc8d554e830faf93ca9d49e upstream.
In snd_sof_update_control(), firmware-provided cdata->num_elems is checked against local_cdata->data->size but never against the actual allocation size. If local_cdata->data->size was previously set to an inconsistent value, the memcpy could write past the allocated buffer.
Add a bounds check to ensure num_elems fits within the available space in the ipc_control_data allocation before copying.
--- sound/soc/sof/ipc3-control.c | 9 +++++++++ 1 file changed, 9 insertions(+)
sound/soc/sof/ipc3-control.c +9 -0
@@ -555,6 +555,15 @@ static void snd_sof_update_control(strucreturn ; } +/* Verify the size fits within the allocation */ +if (cdata->num_elems > scontrol->max_size -sizeof (*local_cdata) - +sizeof (*local_cdata->data)) { +dev_err (scomp->dev, +"cdata binary size %u exceeds buffer\n" , + cdata->num_elems); +return ; + } +/* copy the new binary data */ memcpy (local_cdata->data, cdata->data, cdata->num_elems); }else if (cdata->num_elems != scontrol->num_channels) {
Greg Kroah-Hartman
maintainerPATCH 920/1266 ASoC: mediatek: mt8192: Release reserved memory on cleanup
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cássio Gabriel <[email protected]>
commit 965e17ae6751c5d3302430c8ebd650e72d45a85f upstream.
The MT8192 AFE probe calls of_reserved_mem_device_init() and falls back to preallocated buffers when no reserved memory region is available. When the reserved memory assignment succeeds, however, the driver never releases it.
Register a devm cleanup action after a successful reserved-memory assignment so the assignment is released on probe failure and driver unbind.
--- sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 9 +++++++++ 1 file changed, 9 insertions(+)
sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +9 -0
@@ -2171,6 +2171,11 @@ static const dai_register_cb dai_registe mt8192_dai_memif_register, }; +static void mt8192_afe_release_reserved_mem (void *data) +{ +of_reserved_mem_device_release (data); +} +static int mt8192_afe_pcm_dev_probe (struct platform_device *pdev) {struct mtk_base_afe *afe;
@@ -2201,6 +2206,10 @@ static int mt8192_afe_pcm_dev_probe(struif (ret) {dev_info (dev,"no reserved memory found, pre-allocating buffers instead\n" ); afe->preallocate_buffers = true; + }else { + ret =devm_add_action_or_reset (dev, mt8192_afe_release_reserved_mem, dev); +if (ret) +return ret; }/* init audio related clock */
Greg Kroah-Hartman
maintainerPATCH 921/1266 ASoC: mediatek: mt8183: Release reserved memory on cleanup
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cássio Gabriel <[email protected]>
commit bee65e00c0924ebecf97718d95dcf4a05ee36471 upstream.
The MT8183 AFE probe can assign reserved memory with of_reserved_mem_device_init(), but the assignment is never released on driver removal or later probe failures.
Register a devm cleanup action so the reserved memory assignment is released consistently, matching newer Mediatek AFE drivers.
--- sound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +11 -0
@@ -1085,6 +1085,11 @@ static const dai_register_cb dai_registe mt8183_dai_memif_register, }; +static void mt8183_afe_release_reserved_mem (void *data) +{ +of_reserved_mem_device_release (data); +} +static int mt8183_afe_pcm_dev_probe (struct platform_device *pdev) {struct mtk_base_afe *afe;
@@ -1111,6 +1116,12 @@ static int mt8183_afe_pcm_dev_probe(struif (ret) {dev_info (dev,"no reserved memory found, pre-allocating buffers instead\n" ); afe->preallocate_buffers = true; + }else { + ret =devm_add_action_or_reset (dev, + mt8183_afe_release_reserved_mem, + dev); +if (ret) +return ret; }/* initial audio related clock */
Greg Kroah-Hartman
maintainerPATCH 922/1266 ASoC: qcom: q6apm: fix NULL pointer dereference in graph_callback
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Kandagatla <[email protected]>
commit 2e9261761b35f0b67b7487688cd1365f535be0b3 upstream.
When q6apm_free_fragments() is called it frees rx_data.buf/tx_data.buf and sets them to NULL under graph->lock. A late DSP buffer-done response can race with this: graph_callback() passes the !graph->ar_graph guard (not yet NULL), acquires the lock, but then dereferences a now-NULL buf pointer to read buf[token].phys, crashing at virtual address 0x10.
Add a NULL check for buf inside the mutex-protected section in both the write-done (DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2) and read-done (DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2) handlers and bail out cleanly if buffers have already been freed.
This problem is only shown up recently while apr bus was updated to process the commands per service rather from single global queue.
--- sound/soc/qcom/qdsp6/q6apm.c | 8 ++++++++ 1 file changed, 8 insertions(+)
sound/soc/qcom/qdsp6/q6apm.c +8 -0
@@ -533,6 +533,10 @@ static int graph_callback(struct gpr_res token = hdr->token &APM_WRITE_TOKEN_MASK ; done = data->payload; +if (!graph->rx_data.buf) { +mutex_unlock (&graph->lock); +break ; + } phys = graph->rx_data.buf[token].phys;mutex_unlock (&graph->lock);/* token numbering starts at 0 */
@@ -567,6 +571,10 @@ static int graph_callback(struct gpr_res client_event =APM_CLIENT_EVENT_DATA_READ_DONE ;mutex_lock (&graph->lock); rd_done = data->payload; +if (!graph->tx_data.buf) { +mutex_unlock (&graph->lock); +break ; + } phys = graph->tx_data.buf[hdr->token].phys;mutex_unlock (&graph->lock);/* token numbering starts at 0 */
Greg Kroah-Hartman
maintainerPATCH 923/1266 netfilter: nf_conntrack_irc: fix parse_dcc() off-by-one OOB read
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit ef6400ca25a13fd6dedbe8ef4a1d0979bbbfe88a upstream.
parse_dcc() treats data_end as an inclusive end pointer, but its only caller passes data_limit = ib_ptr + datalen, which points one past the last valid byte.
The newline search loop iterates while tmp <= data_end, so when no newline is present, *tmp is read at tmp == data_end, one byte beyond the region filled by skb_header_pointer().
irc_buffer is kmalloc'd as MAX_SEARCH_SIZE + 1 bytes and datalen is capped at MAX_SEARCH_SIZE, so the stray read does not fault. The byte is uninitialized or stale; if it contains an ASCII digit, simple_strtoul will consume it and produce a wrong DCC IP or port in the conntrack expectation. The extra allocation byte is also a fragile guard: if the cap or allocation size changes, this becomes a real out-of-bounds read.
Change the loop and its post-loop check to use strict less-than, consistent with the caller's exclusive-end convention. Update the function comment accordingly.
--- net/netfilter/nf_conntrack_irc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
net/netfilter/nf_conntrack_irc.c +3 -3
@@ -64,7 +64,7 @@ static const char *const dccprotos[] = {/* tries to get the ip_addr and port out of a dcc command * return value: -1 on failure, 0 on success * data pointer to first byte of DCC command data -* data_end pointer to last byte of dcc command data +* data_end one past end of data * ip returns parsed ip of dcc command * port returns parsed port of dcc command * ad_beg_p returns pointer to first byte of addr data
@@ -82,10 +82,10 @@ static int parse_dcc(char *data, const c/* Make sure we have a newline character within the packet boundaries * because simple_strtoul parses until the first invalid character. */ -for (tmp = data; tmp <= data_end; tmp++) +for (tmp = data; tmp < data_end; tmp++)if (*tmp =='\n' )break ; -if (tmp > data_end || *tmp !='\n' ) +if (tmp >= data_end || *tmp !='\n' )return -1 ; *ad_beg_p = data;
Greg Kroah-Hartman
maintainerPATCH 924/1266 netfilter: nfnl_cthelper: apply per-class values when updating policies
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Carlier <[email protected]>
commit d738feccb98cb224ebabecb703e98f5008276bff upstream.
When a userspace conntrack helper with multiple expectation classes is updated via nfnetlink, every class ends up with the first class's max_expected and timeout values.
nfnl_cthelper_update_policy_all() validates each new policy into the corresponding slot of the temporary new_policy array, but the second loop that commits the values into the live helper dereferences new_policy as a pointer instead of indexing it, so every iteration reads new_policy[0] regardless of i. An update that changes per-class values is silently collapsed onto class 0's values with no error returned to userspace.
Index the temporary array by i in the commit loop so each class gets its own validated values.
--- net/netfilter/nfnetlink_cthelper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/netfilter/nfnetlink_cthelper.c +2 -2
@@ -348,8 +348,8 @@ static int nfnl_cthelper_update_policy_afor (i =0 ; i < helper->expect_class_max +1 ; i++) { policy = (struct nf_conntrack_expect_policy *) &helper->expect_policy[i]; - policy->max_expected = new_policy->max_expected; - policy->timeout = new_policy->timeout; + policy->max_expected = new_policy[i].max_expected; + policy->timeout = new_policy[i].timeout; } err:
Greg Kroah-Hartman
maintainerPATCH 925/1266 netfilter: xt_cluster: reject template conntracks in hash match
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wyatt Feng <[email protected]>
commit 5feba91006ec92da57acc1cc2e34df623b98541e upstream.
xt_cluster_mt() treats any non-NULL nf_ct_get() result as a fully initialized conntrack and passes it to xt_cluster_hash().
This causes a state confusion bug when the raw table CT target attaches a template conntrack to skb->_nfct before normal conntrack processing. Templates carry IPS_TEMPLATE status but do not have a valid tuple for hashing yet, so xt_cluster_hash() can hit its WARN_ON() path on the zeroed l3num field.
Reject template conntracks before hashing them. This matches existing netfilter handling for template objects and avoids hashing incomplete conntrack state.
--- net/netfilter/xt_cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/netfilter/xt_cluster.c +1 -1
@@ -107,7 +107,7 @@ xt_cluster_mt(const struct sk_buff *skb, } ct =nf_ct_get (skb, &ctinfo); -if (ct ==NULL ) +if (!ct ||nf_ct_is_template (ct))return false;if (ct->master)
Greg Kroah-Hartman
maintainerPATCH 926/1266 netfilter: nf_queue: pin bridge device while NFQUEUE holds fake dst
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoze Xie <[email protected]>
commit c9c9b37f8c5505224e8d206184df3bb668ee00cf upstream.
The br_netfilter fake rtable is embedded in struct net_bridge and is attached to bridged packets with skb_dst_set_noref(). If such a packet is queued to NFQUEUE, __nf_queue() upgrades that fake dst with skb_dst_force().
At that point the queued skb can hold a real dst reference after bridge teardown has started. The problem is not that every bridged packet needs its own dst reference. The problem is that NFQUEUE can keep the bridge private fake dst alive after unregister begins.
Fix this by keeping the bridge fake dst model unchanged and pinning the bridge master device only while the packet sits in NFQUEUE. Record the bridge device in nf_queue_entry when the queued skb carries a bridge fake dst, take a device reference for the queue lifetime, and drop it when the queue entry is freed.
Also make sure queued entries are reaped when that bridge device goes down, and drop the redundant nf_bridge_info_exists() test from the fake dst detection.
This keeps netdev_priv(br->dev) alive until verdict completion, so the embedded fake rtable and its metrics backing storage cannot be freed out from under dst_release(). It also avoids the constant refcount bump and avoids using ipv4-specific dst helpers for IPv6 bridge traffic.
--- include/net/netfilter/nf_queue.h | 1 + net/netfilter/nf_queue.c | 14 ++++++++++++++ net/netfilter/nfnetlink_queue.c | 3 +++ 3 files changed, 18 insertions(+)
include/net/netfilter/nf_queue.h +1 -0
@@ -16,6 +16,7 @@ struct nf_queue_entry {unsigned int id;unsigned int hook_index;/* index in hook_entries->hook[] */ #if IS_ENABLED (CONFIG_BRIDGE_NETFILTER ) +struct net_device *bridge_dev;struct net_device *physin;struct net_device *physout;#endif
net/netfilter/nf_queue.c +14 -0
@@ -67,6 +67,7 @@ static void nf_queue_entry_release_refs(nf_queue_sock_put (state->sk);#if IS_ENABLED (CONFIG_BRIDGE_NETFILTER ) +dev_put (entry->bridge_dev);dev_put (entry->physin);dev_put (entry->physout);#endif
@@ -83,6 +84,8 @@ static void __nf_queue_entry_init_physde {#if IS_ENABLED (CONFIG_BRIDGE_NETFILTER )const struct sk_buff *skb = entry->skb; +struct dst_entry *dst =skb_dst (skb); +struct net_device *dev =NULL ;if (nf_bridge_info_exists (skb)) { entry->physin =nf_bridge_get_physindev (skb, entry->state.net);
@@ -91,6 +94,16 @@ static void __nf_queue_entry_init_physde entry->physin =NULL ; entry->physout = NULL; } + +if (entry->state.pf ==NFPROTO_BRIDGE && + dst && (dst->flags &DST_FAKE_RTABLE )) + dev =dst_dev_rcu (dst); + +/* Must hold a reference on the bridge device: dst_hold() protects +* the dst itself, but the fake rtable is embedded in bridge-private +* storage that netdevice teardown can free independently. +*/ + entry->bridge_dev = dev;#endif }
@@ -107,6 +120,7 @@ bool nf_queue_entry_get_refs(struct nf_qdev_hold (state->out);#if IS_ENABLED (CONFIG_BRIDGE_NETFILTER ) +dev_hold (entry->bridge_dev);dev_hold (entry->physin);dev_hold (entry->physout);#endif
net/netfilter/nfnetlink_queue.c +3 -0
@@ -971,6 +971,9 @@ dev_cmp(struct nf_queue_entry *entry, unif (physinif == ifindex || physoutif == ifindex)return 1 ; + +if (entry->bridge_dev && entry->bridge_dev->ifindex == ifindex) +return 1 ;#endif if (entry->skb_dev && entry->skb_dev->ifindex == ifindex)return 1 ;
Greg Kroah-Hartman
maintainerPATCH 927/1266 netfilter: nf_nat_sip: reload possible stale data pointer
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
commit 77e43bcb7ec177e293a5c3f1b91a2c5aebfb6c68 upstream.
quoting sashiko:
------------------------------------------------------------------------
[..] noticed a potential memory bug and header corruption involving the SIP NAT helper.
In net/netfilter/nf_nat_sip.c:nf_nat_sip():
if (skb_ensure_writable(skb, skb->len)) {
nf_ct_helper_log(skb, ct, "cannot mangle packet");
return NF_DROP;
}
uh = (void *)skb->data + protoff;
uh->dest = ct_sip_info->forced_dport;
if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, protoff,
0, 0, NULL, 0)) { If a cloned or fragmented SKB is reallocated by skb_ensure_writable(), the old data buffer is freed. However, nf_nat_sip() fails to update *dptr to point to the new buffer.
It also appears to use nf_nat_mangle_udp_packet() on what could be a TCP packet, which would overwrite the sequence number with a checksum update.
------------------------------------------------------------------------
nf_conntrack_sip linerizes skbs, hence no fragmented skb can be seen. But clones are possible, so rebuild dptr.
Disable nf_nat_mangle_udp_packet() branch for TCP streams. It doesn't look like this can ever happen, else we should have received bug reports about this, so just check the conntrack is UDP and drop otherwise.
The calling conntrack_sip set ->forced_dport for SIP_HDR_VIA_UDP messages, so I don't think this is ever expected to be true for a TCP stream.
--- net/netfilter/nf_nat_sip.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
net/netfilter/nf_nat_sip.c +11 -0
@@ -283,13 +283,24 @@ next:/* Mangle destination port for Cisco phones, then fix up checksums */ if (dir ==IP_CT_DIR_REPLY && ct_sip_info->forced_dport) { +int doff = *dptr - (const char *)skb->data;struct udphdr *uh; +if (doff <=0 ) { +DEBUG_NET_WARN_ON_ONCE (1 ); +return NF_DROP ; + } + +/* ct_sip_info->forced_dport only expected with UDP */ +if (nf_ct_protonum (ct) !=IPPROTO_UDP ) +return NF_DROP ; +if (skb_ensure_writable (skb, skb->len)) {nf_ct_helper_log (skb, ct,"cannot mangle packet" );return NF_DROP ; } + *dptr = skb->data + doff; uh = (void *)skb->data + protoff; uh->dest = ct_sip_info->forced_dport;
Greg Kroah-Hartman
maintainerPATCH 928/1266 netfilter: nf_conntrack_reasm: guard mac_header adjustment after IPv6 defrag
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
commit 3b08fed5b7e0d5e3a25d73ef3ba09cd33ade16c9 upstream.
nf_ct_frag6_reasm() slides the packet head forward to drop the IPv6 fragment header and then unconditionally advances skb->mac_header:
skb->mac_header += sizeof(struct frag_hdr);
On the NF_INET_LOCAL_OUT defrag path the skb has no link-layer header yet, so skb->mac_header is still the "not set" sentinel (u16)~0U. Adding sizeof(struct frag_hdr) wraps it to a small value (0xffff + 8 == 7), after which skb_mac_header_was_set() wrongly reports a MAC header is present and skb_mac_header() points into the headroom.
The reassembler has done this unconditional add since it was introduced; it was harmless while mac_header was a bare pointer, but wrong once mac_header became a u16 offset whose unset state is the ~0U sentinel tested by skb_mac_header_was_set(). The sibling net/ipv6/reassembly.c does the same relocation and does guard the adjustment; mirror the guard here.
--- net/ipv6/netfilter/nf_conntrack_reasm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/ipv6/netfilter/nf_conntrack_reasm.c +2 -1
@@ -346,7 +346,8 @@ static int nf_ct_frag6_reasm(struct fragskb_network_header (skb)[fq->nhoffset] =skb_transport_header (skb)[0 ];memmove (skb->head +sizeof (struct frag_hdr ), skb->head, (skb->data - skb->head) -sizeof (struct frag_hdr )); - skb->mac_header +=sizeof (struct frag_hdr ); +if (skb_mac_header_was_set (skb)) + skb->mac_header +=sizeof (struct frag_hdr ); skb->network_header +=sizeof (struct frag_hdr );skb_reset_transport_header (skb);
Greg Kroah-Hartman
maintainerPATCH 929/1266 netfilter: nf_conncount: fix zone comparison in tuple dedup
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yizhou Zhao <[email protected]>
commit f62c41b4910e65da396ec9a8c40c1fe7fe82e449 upstream.
The "already exists" dedup logic in __nf_conncount_add() decides whether a connection has already been counted and can be skipped instead of incrementing the connlimit count. It compares the conntrack zone of a list entry with the zone of the connection being added using nf_ct_zone_id() and nf_ct_zone_equal(), passing conn->zone.dir or zone->dir as the direction argument.
Those helpers take enum ip_conntrack_dir values: IP_CT_DIR_ORIGINAL is 0 and IP_CT_DIR_REPLY is 1. However, zone->dir is a u8 bitmask:NF_CT_ZONE_DIR_ORIG is 1, NF_CT_ZONE_DIR_REPL is 2 and NF_CT_DEFAULT_ZONE_DIR is 3. Passing that bitmask as the enum direction shifts the meaning of every non-zero value. An ORIG-only zone passes 1 and is tested as REPLY, while REPL-only and default zones pass 2 or 3 and test bits beyond the valid direction range. In those cases nf_ct_zone_id() can fall back to NF_CT_DEFAULT_ZONE_ID instead of using the real zone id, so different zones can be treated as equal and dedup collapses to tuple equality alone.
nf_conncount stores and compares the original-direction tuple for a connection. If an skb already has an attached conntrack entry, get_ct_or_tuple_from_skb() explicitly copies ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, regardless of the packet's ctinfo. Therefore the zone comparison in the tuple dedup path must use IP_CT_DIR_ORIGINAL as well; the zone direction bitmask describes where a zone id applies, not which direction this conncount tuple represents.
Fix the two dedup comparisons by passing IP_CT_DIR_ORIGINAL directly. Do not special-case NF_CT_DEFAULT_ZONE_DIR and do not compare raw zone
ids: using the existing helpers with IP_CT_DIR_ORIGINAL preserves the direction-aware NF_CT_DEFAULT_ZONE_ID fallback. A default bidirectional zone contains the ORIG bit, so it naturally returns the real zone id; reply-only zones continue to fall back for original-direction tuple comparisons.
--- net/netfilter/nf_conncount.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
net/netfilter/nf_conncount.c +3 -3
@@ -207,8 +207,8 @@ check_connections:/* Not found, but might be about to be confirmed */ if (PTR_ERR (found) == -EAGAIN ) {if (nf_ct_tuple_equal (&conn->tuple, &tuple) && -nf_ct_zone_id (&conn->zone, conn->zone.dir) == -nf_ct_zone_id (zone, zone->dir)) +nf_ct_zone_id (&conn->zone,IP_CT_DIR_ORIGINAL ) == +nf_ct_zone_id (zone,IP_CT_DIR_ORIGINAL )) goto out_put;/* already exists */ }else { collect++;
@@ -219,7 +219,7 @@ check_connections: found_ct =nf_ct_tuplehash_to_ctrack (found);if (nf_ct_tuple_equal (&conn->tuple, &tuple) && -nf_ct_zone_equal (found_ct, zone, zone->dir)) { +nf_ct_zone_equal (found_ct, zone,IP_CT_DIR_ORIGINAL )) {/* * We should not see tuples twice unless someone hooks * this into a table without "-p tcp --syn".
Greg Kroah-Hartman
maintainerPATCH 930/1266 netfilter: ecache: fix inverted time_after() check
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yizhou Zhao <[email protected]>
commit b06163ce52ec0561f202fbfb9b08090cb61f512e upstream.
ecache_work_evict_list() redelivers DESTROY events for conntracks that were moved to the per-netns dying_list after event delivery failed. It sets a 10ms deadline:
stop = jiffies + ECACHE_MAX_JIFFIES
but then tests:
time_after(stop, jiffies)
This condition is true while the deadline is still in the future, so the worker returns STATE_RESTART after the first successful redelivery in the usual case. ecache_work() maps STATE_RESTART to delay 0, which turns the redelivery path into one dying conntrack per workqueue dispatch and makes the sent > 16 batching/cond_resched() path effectively unreachable.
A conntrack netlink listener whose receive queue is congested can make DESTROY event delivery fail with -ENOBUFS. With sustained conntrack churn, entries then accumulate on the dying_list and are only drained at the degraded one-entry-per-dispatch rate once delivery succeeds again, wasting CPU on back-to-back workqueue reschedules and prolonging conntrack memory/resource pressure.
In a KASAN QEMU test with CONFIG_NF_CONNTRACK_EVENTS=y and nf_conntrack.enable_hooks=1, a congested DESTROY listener caused 8192 nf_ct_delete() calls to return false and move entries to the dying_list. After closing the listener, the unfixed kernel needed 7670 ecache_work() entries to destroy 7669 conntracks. With this change, the same 8192 entries were destroyed by 2 ecache_work() entries.
Swap the comparison so the worker restarts only after the deadline has expired.
--- net/netfilter/nf_conntrack_ecache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/netfilter/nf_conntrack_ecache.c +1 -1
@@ -77,7 +77,7 @@ next:hlist_nulls_del_rcu (&ct->tuplehash[IP_CT_DIR_ORIGINAL ].hnnode);hlist_nulls_add_head (&ct->tuplehash[IP_CT_DIR_REPLY ].hnnode, &evicted_list); -if (time_after (stop, jiffies)) { +if (time_after (jiffies, stop)) { ret =STATE_RESTART ;break ; }
Greg Kroah-Hartman
maintainerPATCH 931/1266 netfilter: xt_nat: reject unsupported target families
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wyatt Feng <[email protected]>
commit 5d1a2240935ea47e2673d0ea17fdb058e4dc91dd upstream.
xt_nat SNAT and DNAT target handlers assume IP-family conntrack state is present and can dereference a NULL pointer when instantiated from an unsupported family through nft_compat. A bridge-family compat rule can therefore trigger a NULL-dereference in nf_nat_setup_info().
Reject non-IP families in xt_nat_checkentry() so unsupported targets cannot be installed. Keep NFPROTO_INET allowed for valid inet NAT compat users and leave the runtime fast path unchanged.
[ The crash was fixed via
9dbba7e694ec ("netfilter: nft_compat: ebtables emulation must reject non-bridge targets"), so this patch is no longer critical.
Nevertheless, NAT is only relevant for ipv4/ipv6, so this extra
family check is a good idea in any case. ]
--- net/netfilter/xt_nat.c | 9 +++++++++ 1 file changed, 9 insertions(+)
net/netfilter/xt_nat.c +9 -0
@@ -26,6 +26,15 @@ static int xt_nat_checkentry_v0(const ststatic int xt_nat_checkentry (const struct xt_tgchk_param *par) { +switch (par->family) { +case NFPROTO_IPV4 : +case NFPROTO_IPV6 : +case NFPROTO_INET : +break ; +default : +return -EINVAL ; + } +return nf_ct_netns_get (par->net, par->family); }
Greg Kroah-Hartman
maintainerPATCH 932/1266 netfilter: bridge: fix stale prevhdr pointer in br_ip6_fragment()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei (Microsoft) <[email protected]>
commit 86f3ce81dd2b4b0aa2c3016c989a943e4b1b643d upstream.
br_ip6_fragment() gets prevhdr, a pointer into the skb head, from ip6_find_1stfragopt(), then calls skb_checksum_help(). For a cloned skb skb_checksum_help() reallocates the head via pskb_expand_head(), leaving prevhdr dangling. It is later dereferenced in ip6_frag_next(), causing a use-after-free write.
Save prevhdr's offset before skb_checksum_help() and recompute it after, like commit ef0efcd3bd3f ("ipv6: Fix dangling pointer when ipv6 fragment").
BUG: KASAN: slab-use-after-free in ip6_frag_next (net/ipv6/ip6_output.c:857) Write of size 1 at addr ffff888013ff5016 by task exploit/141 Call Trace: ... kasan_report (mm/kasan/report.c:595) ip6_frag_next (net/ipv6/ip6_output.c:857) br_ip6_fragment (net/ipv6/netfilter.c:212) nf_ct_bridge_post (net/bridge/netfilter/nf_conntrack_bridge.c:407) nf_hook_slow (net/netfilter/core.c:619) br_forward_finish (net/bridge/br_forward.c:66) __br_forward (net/bridge/br_forward.c:115) maybe_deliver (net/bridge/br_forward.c:191) br_flood (net/bridge/br_forward.c:245) br_handle_frame_finish (net/bridge/br_input.c:229) br_handle_frame (net/bridge/br_input.c:442) ... packet_sendmsg (net/packet/af_packet.c:3114) ... do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Kernel panic - not syncing: Fatal exception in interrupt
--- net/ipv6/netfilter.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
net/ipv6/netfilter.c +3 -1
@@ -130,7 +130,7 @@ int br_ip6_fragment(struct net *net, strktime_t tstamp = skb->tstamp;struct ip6_frag_state state;u8 *prevhdr, nexthdr =0 ; -unsigned int mtu, hlen; +unsigned int mtu, hlen, nexthdr_offset;int hroom, err =0 ;__be32 frag_id;
@@ -139,6 +139,7 @@ int br_ip6_fragment(struct net *net, str goto blackhole; hlen = err; nexthdr = *prevhdr; + nexthdr_offset = prevhdr -skb_network_header (skb); mtu = skb->dev->mtu; if (frag_max_size > mtu ||
@@ -157,6 +158,7 @@ int br_ip6_fragment(struct net *net, str (err = skb_checksum_help(skb))) goto blackhole; + prevhdr =skb_network_header (skb) + nexthdr_offset; hroom =LL_RESERVED_SPACE (skb->dev);if (skb_has_frag_list (skb)) {unsigned int first_len =skb_pagelen (skb);
Greg Kroah-Hartman
maintainerPATCH 933/1266 gpu: host1x: Fix device reference leak in host1x_device_parse_dt() error path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit e75717f9aec04355777be41070890c6a815c76df upstream.
After device_initialize(), the embedded struct device in struct host1x_device should be released through the device core with put_device().
In host1x_device_add(), if host1x_device_parse_dt() fails, the current error path frees the object directly with kfree(device). That bypasses the normal device lifetime handling and leaks the reference held on the embedded struct device.
The issue was identified by a static analysis tool I developed and confirmed by manual review.
Fix this by using put_device() in the host1x_device_parse_dt() failure path.
--- drivers/gpu/host1x/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/host1x/bus.c +1 -1
@@ -457,7 +457,7 @@ static int host1x_device_add(struct host err =host1x_device_parse_dt (device, driver);if (err <0 ) { -kfree (device); +put_device (&device->dev);return err; }
Greg Kroah-Hartman
maintainerPATCH 934/1266 soc: ti: k3-ringacc: Fix access mode for k3_ringacc_ring_pop_tail_io/proxy
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Siddharth Vadapalli <[email protected]>
commit b920352cfd2b0fcd1249ff006618c939b64fc8f7 upstream.
k3_ringacc_ring_pop_tail_io() and k3_ringacc_ring_pop_tail_proxy() incorrectly use K3_RINGACC_ACCESS_MODE_POP_HEAD instead of K3_RINGACC_ACCESS_MODE_POP_TAIL. This will result in ring elements being popped in the reverse order of that which the caller expects. Fix this.
--- drivers/soc/ti/k3-ringacc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/soc/ti/k3-ringacc.c +2 -2
@@ -1012,7 +1012,7 @@ static int k3_ringacc_ring_pop_head_proxstatic int k3_ringacc_ring_pop_tail_proxy (struct k3_ring *ring,void *elem) {return k3_ringacc_ring_access_proxy (ring, elem, -K3_RINGACC_ACCESS_MODE_POP_HEAD ); +K3_RINGACC_ACCESS_MODE_POP_TAIL ); } static int k3_ringacc_ring_access_io(struct k3_ring *ring, void *elem,
@@ -1083,7 +1083,7 @@ static int k3_ringacc_ring_pop_io(structstatic int k3_ringacc_ring_pop_tail_io (struct k3_ring *ring,void *elem) {return k3_ringacc_ring_access_io (ring, elem, -K3_RINGACC_ACCESS_MODE_POP_HEAD ); +K3_RINGACC_ACCESS_MODE_POP_TAIL ); }/*
Greg Kroah-Hartman
maintainerPATCH 935/1266 soc: fsl: qe: panic on ioremap() failure in qe_reset()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wang Jun <[email protected]>
commit a0fe29d20e7822182e12324905af5115c1b3aed3 upstream.
When ioremap() fails in qe_reset(), the global pointer qe_immr remains NULL, leading to a subsequent NULL pointer dereference when the pointer is accessed. Since this happens early in the boot process, a failure to map a few bytes of I/O memory indicates a fatal error from which the system cannot recover.
Follow the same pattern as qe_sdma_init() and panic immediately when ioremap() fails. This avoids a silent NULL pointer dereference later and makes the error explicit.
--- drivers/soc/fsl/qe/qe.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/soc/fsl/qe/qe.c +3 -0
@@ -89,6 +89,9 @@ void qe_reset(void)if (qe_immr ==NULL ) qe_immr =ioremap (get_qe_base (),QE_IMMAP_SIZE ); +if (!qe_immr) +panic ("QE:ioremap failed!" ); +qe_snums_init (); qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID,
Greg Kroah-Hartman
maintainerPATCH 936/1266 selinux: check connect-related permissions on TCP Fast Open
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stephen Smalley <[email protected]>
commit 44c74d27d1b9aaa99fa8a83640c1223575262b80 upstream.
Similar to Landlock, SELinux was not updated when TCP Fast Open support was introduced to ensure connect-related permissions are checked when using TCP Fast Open. Update its socket_sendmsg() hook to call selinux_socket_connect() when MSG_FASTOPEN is passed.
--- security/selinux/hooks.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
security/selinux/hooks.c +18 -1
@@ -5047,7 +5047,24 @@ static int selinux_socket_accept(structstatic int selinux_socket_sendmsg (struct socket *sock,struct msghdr *msg,int size) { -return sock_has_perm (sock->sk,SOCKET__WRITE ); +int rc; +struct sockaddr *const addr = msg->msg_name; +const int addrlen = msg->msg_namelen; + + rc =sock_has_perm (sock->sk,SOCKET__WRITE ); +if (rc) +return rc; + +if (addr && (msg->msg_flags &MSG_FASTOPEN ) && + (sk_is_tcp (sock->sk) || + (sk_is_inet (sock->sk) && sock->sk->sk_type ==SOCK_STREAM && + sock->sk->sk_protocol ==IPPROTO_MPTCP ))) { + rc =selinux_socket_connect (sock, addr, addrlen); +if (rc) +return rc; + } + +return 0 ; } static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
Greg Kroah-Hartman
maintainerPATCH 937/1266 selinux: fix incorrect execmem checks on overlayfs
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ondrej Mosnacek <[email protected]>
commit 9fe595fad54d4ac6a402edb3f60bec859d52cea6 upstream.
The commit fixing the overlayfs mmap() and mprotect() access checks failed to skip the execmem check in __file_map_prot_check() for the case where the "mounter check" is being performed. This check should be performed only against the credentials of the task that is calling mmap()/mprotect(), since it doesn't pertain to the file itself, but rather just gates the ability of the calling task to get an executable memory mapping in general.
The purpose of the "mounter check" is to guard against using an overlayfs mount to gain file access that would otherwise be denied to the mounter. For execmem this is not relevant, as there is no further file access granted based on it (notice that the file's context is not used as the target in the check), so checking it also against the mounter credentials would be incorrect.
Fix this by passing a boolean to [__]file_map_prot_check() and selinux_mmap_file_common() that indicates if we are doing the "mounter check" and skiping the execmem check in that case. Since this boolean also indicates if we use current_cred() or the mounter cred as the subject, also remove the "cred" argument from these functions and determine it based on the boolean and the file struct.
--- security/selinux/hooks.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-)
security/selinux/hooks.c +24 -18
@@ -3789,9 +3789,9 @@ static int selinux_file_ioctl_compat(strstatic int default_noexec__ro_after_init ; -static int __file_map_prot_check (const struct cred *cred, -const struct file *file,unsigned long prot, -bool shared,bool bf_user_file) +static int __file_map_prot_check (const struct file *file,unsigned long prot, +bool shared,bool mounter_check, +bool bf_user_file) {struct inode *inode =NULL ;bool prot_exec = prot &PROT_EXEC ;
@@ -3804,10 +3804,10 @@ static int __file_map_prot_check(const s inode =file_inode (file); } -if (default_noexec && prot_exec && +if (!mounter_check && default_noexec && prot_exec && (!file ||IS_PRIVATE (inode) || (!shared && prot_write))) {int rc; -u32 sid =cred_sid (cred); +u32 sid =current_sid ();/* * We are making executable an anonymous mapping or a private
@@ -3820,6 +3820,8 @@ static int __file_map_prot_check(const s }if (file) { +const struct cred *cred = mounter_check ? + file->f_cred :current_cred ();/* "read" always possible, "write" only if shared */ u32 av =FILE__READ ;if (shared && prot_write)
@@ -3833,11 +3835,11 @@ static int __file_map_prot_check(const sreturn 0 ; } -static inline int file_map_prot_check (const struct cred *cred, -const struct file *file, -unsigned long prot,bool shared) +static inline int file_map_prot_check (const struct file *file, +unsigned long prot,bool shared, +bool mounter_check) { -return __file_map_prot_check (cred, file, prot, shared, false); +return __file_map_prot_check (file, prot, shared, mounter_check, false); }static int selinux_mmap_addr (unsigned long addr)
@@ -3853,12 +3855,14 @@ static int selinux_mmap_addr(unsigned loreturn rc; } -static int selinux_mmap_file_common (const struct cred *cred,struct file *file, -unsigned long prot,bool shared) +static int selinux_mmap_file_common (struct file *file,unsigned long prot, +bool shared,bool mounter_check) {if (file) {int rc;struct common_audit_data ad; +const struct cred *cred = mounter_check ? + file->f_cred :current_cred (); ad.type =LSM_AUDIT_DATA_FILE ; ad.u.file = file;
@@ -3867,15 +3871,16 @@ static int selinux_mmap_file_common(consreturn rc; } -return file_map_prot_check (cred, file, prot, shared); +return file_map_prot_check (file, prot, shared, mounter_check); }static int selinux_mmap_file (struct file *file,unsigned long reqprot__always_unused ,unsigned long prot,unsigned long flags) { -return selinux_mmap_file_common (current_cred (), file, prot, - (flags &MAP_TYPE ) ==MAP_SHARED ); +return selinux_mmap_file_common (file, prot, + (flags &MAP_TYPE ) ==MAP_SHARED , + false); }/**
@@ -3907,8 +3912,9 @@ static int selinux_mmap_backing_file(strif (vma->vm_flags &VM_EXEC ) prot |=PROT_EXEC ; -return selinux_mmap_file_common (backing_file->f_cred, backing_file, - prot, vma->vm_flags &VM_SHARED ); +return selinux_mmap_file_common (backing_file, prot, + vma->vm_flags &VM_SHARED , + true); } static int selinux_file_mprotect(struct vm_area_struct *vma,
@@ -3969,11 +3975,11 @@ static int selinux_file_mprotect(struct } } - rc =__file_map_prot_check (cred, file, prot, shared, backing_file); + rc =__file_map_prot_check (file, prot, shared, false, backing_file);if (rc)return rc;if (backing_file) { - rc =file_map_prot_check (file->f_cred, file, prot, shared); + rc =file_map_prot_check (file, prot, shared, true);if (rc)return rc; }
Greg Kroah-Hartman
maintainerPATCH 938/1266 leds: uleds: Fix potential buffer overread
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Armin Wolf <[email protected]>
commit c19fe864f667afc49d1391d764e20b66555bcf7a upstream.
The name string supplied by userspace is not guaranteed to be null-terminated, so using strchr() on it might result in a buffer overread. The same thing will happen when said string is used by the LED class device.
Fix this by using strnchr() instead and explicitly check that the name string is properly null-terminated.
--- drivers/leds/uleds.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/leds/uleds.c +2 -1
@@ -102,7 +102,8 @@ static ssize_t uleds_write(struct file * name = udev->user_dev.name;if (!name[0 ] || !strcmp (name,"." ) || !strcmp (name,".." ) || -strchr (name,'/' )) { +strnchr (name,sizeof (udev->user_dev.name),'/' ) || + !strnchr (name,sizeof (udev->user_dev.name),'\0' )) { ret = -EINVAL ; goto out; }
Greg Kroah-Hartman
maintainerPATCH 939/1266 mfd: sm501: Fix reference leak on failed device registration
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit 8c2f0b42fc252e1bf1c7746447091a468e784ca1 upstream.
When platform_device_register() fails in sm501_register_device(), the embedded struct device in pdev has already been initialized by device_initialize(), but the failure path only reports the error and returns without dropping the device reference for the current platform device:
sm501_register_device()
-> platform_device_register(pdev)
-> device_initialize(&pdev->dev)
-> setup_pdev_dma_masks(pdev)
-> platform_device_add(pdev)This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error.
The issue was identified by a static analysis tool I developed and confirmed by manual review.
--- drivers/mfd/sm501.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/mfd/sm501.c +3 -1
@@ -747,9 +747,11 @@ static int sm501_register_device(structif (ret >=0 ) {dev_dbg (sm->dev,"registered %s\n" , pdev->name);list_add_tail (&smdev->list, &sm->devices); - }else + }else {dev_err (sm->dev,"error registering %s (%d)\n" , pdev->name, ret); +platform_device_put (pdev); + }return ret; }
Greg Kroah-Hartman
maintainerPATCH 940/1266 tools/power/x86/intel-speed-select: Harden daemon pidfile open
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ali Ahmet MEMIS <[email protected]>
commit 607af438e6430893a822964c841a1994b33acccc upstream.
Avoid symlink-based pidfile clobbering by opening the pidfile with O_NOFOLLOW and validating it with fstat() before locking/writing.
The daemon currently uses a fixed pidfile path under /tmp. A local unprivileged user can pre-create a symlink at that path and cause a root-run daemon instance to write into an attacker-chosen file.
--- tools/power/x86/intel-speed-select/isst-daemon.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
tools/power/x86/intel-speed-select/isst-daemon.c +8 -1
@@ -147,6 +147,7 @@ static void daemonize(char *rundir, char {int pid, sid, i;char str[10 ]; +struct stat st;struct sigaction sig_actions;sigset_t sig_set;int ret;
@@ -199,11 +200,17 @@ static void daemonize(char *rundir, charif (ret ==-1 )exit (EXIT_FAILURE ); - pid_file_handle =open (pidfile,O_RDWR |O_CREAT ,0600 ); + pid_file_handle =open (pidfile,O_RDWR |O_CREAT |O_NOFOLLOW ,0600 );if (pid_file_handle ==-1 ) {/* Couldn't open lock file */ exit (1 ); } + +if (fstat (pid_file_handle, &st) ==-1 ) +exit (1 ); + +if (!S_ISREG (st.st_mode)) +exit (1 );/* Try to lock file */ #ifdef LOCKF_SUPPORT if (lockf (pid_file_handle,F_TLOCK ,0 ) ==-1 ) {
Greg Kroah-Hartman
maintainerPATCH 941/1266 x86/boot: Validate console=uart8250 baud rate to fix early boot hang
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
commit ffa0aa5b625fe0bed7463ac613f8b06676ff4542 upstream.
When the baud rate is empty, 0, invalid, or overflows to 0 when stored as an int, the system will hang during early boot because of a division by zero in early_serial_init().
Fall back to DEFAULT_BAUD when the resulting baud rate is 0 to prevent an early system hang.
--- arch/x86/boot/early_serial_console.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
arch/x86/boot/early_serial_console.c +7 -4
@@ -117,7 +117,7 @@ static unsigned int probe_baud(int port)static void parse_console_uart8250 (void ) {char optstr[64 ], *options; -int baud =DEFAULT_BAUD ; +int baud;int port =0 ;/*
@@ -136,10 +136,13 @@ static void parse_console_uart8250(void)else return ; -if (options && (options[0 ] ==',' )) - baud =simple_strtoull (options +1 , &options,0 ); -else +if (options && (options[0 ] ==',' )) { + baud =simple_strtoull (options +1 ,NULL ,0 ); +if (!baud) + baud =DEFAULT_BAUD ; + }else { baud =probe_baud (port); + }if (port)early_serial_init (port, baud);
Greg Kroah-Hartman
maintainerPATCH 942/1266 x86/boot: Reject too long acpi_rsdp= values
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
commit d130041a7b96f79cd4c7079a6c2431a6db4c9619 upstream.
cmdline_find_option() returns the full length of the parsed acpi_rsdp= value. get_cmdline_acpi_rsdp() then silently truncates values which do not fit in the val[] buffer.
Prevent boot_kstrtoul() from parsing a truncated value and then the kernel from silently using the wrong RSDP address, see discussion in Link:.
Issue a warning so that the user is aware that s/he supplied a malformed value and can get feedback instead of silent crashes.
[ bp: Make commit message more precise. ]
--- arch/x86/boot/compressed/acpi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
arch/x86/boot/compressed/acpi.c +6 -1
@@ -182,10 +182,15 @@ static unsigned long get_cmdline_acpi_rschar val[MAX_ADDR_LEN ] = { };int ret; - ret =cmdline_find_option ("acpi_rsdp" , val,MAX_ADDR_LEN ); + ret =cmdline_find_option ("acpi_rsdp" , val,sizeof (val));if (ret <0 )return 0 ; +if (ret >=sizeof (val)) { +warn ("acpi_rsdp= value too long; ignoring" ); +return 0 ; + } +if (boot_kstrtoul (val,16 , &addr))return 0 ;#endif
Greg Kroah-Hartman
maintainerPATCH 943/1266 perf/x86/amd/lbr: Fix kernel address leakage
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sandipan Das <[email protected]>
commit 2a892294b83f541115c94b0bb637f39bef187657 upstream.
A user-only branch stack can contain branches that originate from the kernel. As a result, kernel addresses are exposed to user space even when PERF_SAMPLE_BRANCH_USER is requested. On AMD processors supporting X86_FEATURE_AMD_LBR_V2, perf can still report SYSRET/ERET entries for which the branch-from addresses are in the kernel.
E.g.
$ perf record -e cycles -o - -j any,save_type,u -- \
perf bench syscall basic --loop 1000 | \
perf script -i - -F brstack|tr ' ' '\n'| \
grep -E '0x[89a-f][0-9a-f]{15}' ...
0xffffffff81001268/0x717a90a38f1a/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a39157/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a2c628/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a41b60/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a260db/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a260db/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a8bef1c30/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a8e4d3c90/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH
...
The reason is that the hardware filter only considers the privilege level applicable to the branch target. Extend software filtering to also validate the branch-from addresses against br_sel, so that any branch record whose branch-from address is in the kernel is dropped when PERF_SAMPLE_BRANCH_USER is requested.
--- arch/x86/events/amd/lbr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
arch/x86/events/amd/lbr.c +2 -1
@@ -124,7 +124,8 @@ static void amd_pmu_lbr_filter(void) }/* If type does not correspond, then discard */ -if (type ==X86_BR_NONE || (br_sel & type) != type) { +if (type ==X86_BR_NONE || (br_sel & type) != type || + (!(br_sel &X86_BR_KERNEL ) &&kernel_ip (cpuc->lbr_entries[i].from))) { cpuc->lbr_entries[i].from =0 ;/* mark invalid */ compress = true; }
Greg Kroah-Hartman
maintainerPATCH 944/1266 s390/perf_cpum_cf: Add missing array_index_nospec() to __hw_perf_event_init()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sumanth Korikkar <[email protected]>
commit 49145bce539117db4b6e9e83c0e5ef528e361050 upstream.
ev variable is userspace controlled via event->attr.config and used as an array index after bounds checking, but without speculation barriers.
Add the missing array_index_nospec() call to prevent speculative execution.
--- arch/s390/kernel/perf_cpum_cf.c | 3 +++ 1 file changed, 3 insertions(+)
arch/s390/kernel/perf_cpum_cf.c +3 -0
@@ -17,6 +17,7 @@#include <linux/export.h> #include <linux/miscdevice.h> #include <linux/perf_event.h> +#include <linux/nospec.h> #include <asm/cpu_mf.h> #include <asm/hwctrset.h>
@@ -766,6 +767,7 @@ static int __hw_perf_event_init(struct pif (!is_userspace_event (ev)) {if (ev >=ARRAY_SIZE (cpumf_generic_events_user))return -EOPNOTSUPP ; + ev =array_index_nospec (ev,ARRAY_SIZE (cpumf_generic_events_user)); ev = cpumf_generic_events_user[ev]; } }else if (!attr->exclude_kernel && attr->exclude_user) {
@@ -776,6 +778,7 @@ static int __hw_perf_event_init(struct pif (!is_userspace_event (ev)) {if (ev >=ARRAY_SIZE (cpumf_generic_events_basic))return -EOPNOTSUPP ; + ev =array_index_nospec (ev,ARRAY_SIZE (cpumf_generic_events_basic)); ev = cpumf_generic_events_basic[ev]; } }
Greg Kroah-Hartman
maintainerPATCH 945/1266 batman-adv: gw: acquire ethernet header only after skb realloc
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 77880a3be88d378d60cc1e8f8ec70430e2ed0518 upstream.
The pskb_may_pull() called by batadv_get_vid() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free.
--- net/batman-adv/gateway_client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/batman-adv/gateway_client.c +2 -1
@@ -693,12 +693,13 @@ bool batadv_gw_out_of_range(struct batadstruct batadv_gw_node *gw_node =NULL ;struct batadv_gw_node *curr_gw =NULL ;struct batadv_neigh_ifinfo *curr_ifinfo, *old_ifinfo; -struct ethhdr *ethhdr = (struct ethhdr *)skb->data; +struct ethhdr *ethhdr;bool out_of_range = false;u8 curr_tq_avg;unsigned short vid; vid =batadv_get_vid (skb,0 ); + ethhdr = (struct ethhdr *)skb->data;if (is_multicast_ether_addr (ethhdr->h_dest)) goto out;
Greg Kroah-Hartman
maintainerPATCH 946/1266 batman-adv: access unicast_ttvn skb->data only after skb realloc
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 7141990add3f75436f2933cb310654cad3b1e3e9 upstream.
The pskb_may_pull() called by batadv_get_vid() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free.
This was done correctly for the ethernet header but missed for the unicast_packet pointer.
--- net/batman-adv/routing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/batman-adv/routing.c +1 -1
@@ -855,8 +855,8 @@ static bool batadv_check_unicast_ttvn(stif (skb_cow (skb,sizeof (*unicast_packet)) <0 )return false; - unicast_packet = (struct batadv_unicast_packet *)skb->data; vid =batadv_get_vid (skb, hdr_len); + unicast_packet = (struct batadv_unicast_packet *)skb->data; ethhdr = (struct ethhdr *)(skb->data + hdr_len);/* do not reroute multicast frames in a unicast header */
Greg Kroah-Hartman
maintainerPATCH 947/1266 batman-adv: dat: acquire ARP hw source only after skb realloc
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 48067b2ae4504500a7093d9e1e16b42e70330480 upstream.
The pskb_may_pull() called by batadv_get_vid() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free.
--- net/batman-adv/distributed-arp-table.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/batman-adv/distributed-arp-table.c +4 -1
@@ -1758,6 +1758,7 @@ void batadv_dat_snoop_incoming_dhcp_ack(struct ethhdr *ethhdr;__be32 ip_src, yiaddr;unsigned short vid; +int hdr_size_tmp;__be16 proto;u8 *hw_src;
@@ -1774,8 +1775,10 @@ void batadv_dat_snoop_incoming_dhcp_ack(if (!batadv_dat_check_dhcp_ack (skb, proto, &ip_src, chaddr, &yiaddr))return ; + hdr_size_tmp = hdr_size; + vid =batadv_dat_get_vid (skb, &hdr_size_tmp); + ethhdr = (struct ethhdr *)(skb->data + hdr_size); hw_src = ethhdr->h_source; - vid =batadv_dat_get_vid (skb, &hdr_size);batadv_dat_entry_add (bat_priv, yiaddr, chaddr, vid);batadv_dat_entry_add (bat_priv, ip_src, hw_src, vid);
Greg Kroah-Hartman
maintainerPATCH 948/1266 batman-adv: bla: reacquire gw address after skb realloc
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit cdf3b5af2bc4431e58629e8ad2086b1e9185c761 upstream.
The pskb_may_pull() called by batadv_bla_is_backbone_gw() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free.
--- net/batman-adv/routing.c | 1 + 1 file changed, 1 insertion(+)
net/batman-adv/routing.c +1 -0
@@ -1035,6 +1035,7 @@ int batadv_recv_unicast_packet(struct sk hdr_size);batadv_orig_node_put (orig_node_gw);if (is_gw) { + orig_addr_gw =eth_hdr (skb)->h_source;batadv_dbg (BATADV_DBG_BLA , bat_priv,"%s(): Dropped unicast pkt received from another backbone gw %pM.\n" , __func__, orig_addr_gw);
Greg Kroah-Hartman
maintainerPATCH 949/1266 batman-adv: dat: ensure accessible eth_hdr proto field
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 26560c4a03dc4d607331600c187f59ab2df5f341 upstream.
When batadv_get_vid() accesses the proto field of the ethernet header, it is not checking if the data itself is accessible. The caller is responsible for it. But in contrast to other call sites, batadv_dat_get_vid() and its caller didn't make sure this is true. This could have caused an out-of-bounds access.
--- net/batman-adv/distributed-arp-table.c | 23 +++++++++++++++++++++++ net/batman-adv/main.c | 3 +++ 2 files changed, 26 insertions(+)
net/batman-adv/distributed-arp-table.c +23 -0
@@ -1077,6 +1077,9 @@ out:* @skb: the buffer containing the packet to extract the VID from * @hdr_size: the size of the batman-adv header encapsulating the packet * +* The caller must ensure that at least @hdr_size + ETH_HLEN bytes are +* accessible after skb->data. +* * Return: If the packet embedded in the skb is vlan tagged this function * returns the VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS * is returned.
@@ -1159,6 +1162,10 @@ bool batadv_dat_snoop_outgoing_arp_requeif (!atomic_read (&bat_priv->distributed_arp_table)) goto out; +/* first, find out the vid. */ +if (!pskb_may_pull (skb, hdr_size +ETH_HLEN )) + goto out; + vid =batadv_dat_get_vid (skb, &hdr_size); type =batadv_arp_get_type (bat_priv, skb, hdr_size);
@@ -1254,6 +1261,10 @@ bool batadv_dat_snoop_incoming_arp_requeif (!atomic_read (&bat_priv->distributed_arp_table)) goto out; +/* first, find out the vid. */ +if (!pskb_may_pull (skb, hdr_size +ETH_HLEN )) + goto out; + vid =batadv_dat_get_vid (skb, &hdr_size); type =batadv_arp_get_type (bat_priv, skb, hdr_size);
@@ -1316,6 +1327,10 @@ void batadv_dat_snoop_outgoing_arp_replyif (!atomic_read (&bat_priv->distributed_arp_table))return ; +/* first, find out the vid. */ +if (!pskb_may_pull (skb, hdr_size +ETH_HLEN )) +return ; + vid =batadv_dat_get_vid (skb, &hdr_size); type =batadv_arp_get_type (bat_priv, skb, hdr_size);
@@ -1364,6 +1379,10 @@ bool batadv_dat_snoop_incoming_arp_replyif (!atomic_read (&bat_priv->distributed_arp_table)) goto out; +/* first, find out the vid. */ +if (!pskb_may_pull (skb, hdr_size +ETH_HLEN )) + goto out; + vid =batadv_dat_get_vid (skb, &hdr_size); type =batadv_arp_get_type (bat_priv, skb, hdr_size);
@@ -1818,6 +1837,10 @@ bool batadv_dat_drop_broadcast_packet(stif (batadv_forw_packet_is_rebroadcast (forw_packet)) goto out; +/* first, find out the vid. */ +if (!pskb_may_pull (forw_packet->skb, hdr_size +ETH_HLEN )) + goto out; + vid =batadv_dat_get_vid (forw_packet->skb, &hdr_size); type =batadv_arp_get_type (bat_priv, forw_packet->skb, hdr_size);
net/batman-adv/main.c +3 -0
@@ -618,6 +618,9 @@ __be32 batadv_skb_crc32(struct sk_buff ** @skb: the buffer containing the packet * @header_len: length of the batman header preceding the ethernet header * +* The caller must ensure that at least @header_len + ETH_HLEN bytes are +* accessible after skb->data. +* * Return: VID with the BATADV_VLAN_HAS_TAG flag when the packet embedded in the * skb is vlan tagged. Otherwise BATADV_NO_FLAGS. */
Greg Kroah-Hartman
maintainerPATCH 950/1266 batman-adv: dat: fix tie-break for candidate selection
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 98052bdaf6ac1639a63ffc10244eeeab1f62ed2b upstream.
The original version of the candidate selection for DAT attempted to compare both candidate and max_orig_node to identify which has the smaller MAC address. This comparison is required as tie-break when a hash collision happened.
But the used function returned 0 when the function was not equal and a non-zero value when it was equal. As result, the actually selected node was dependent on the order of entries in the orig_hash and not actually on the mac addresses. The last originator in the hash collision would always win.
To have a proper ordering, it must diff the actual MAC address bytes and reject the candidate when the diff is not smaller than 0.
--- net/batman-adv/distributed-arp-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/batman-adv/distributed-arp-table.c +1 -1
@@ -546,7 +546,7 @@ static bool batadv_is_orig_node_eligible* the one with the lowest address */ if (tmp_max == max && max_orig_node && -batadv_compare_eth (candidate->orig, max_orig_node->orig)) +memcmp (candidate->orig, max_orig_node->orig,ETH_ALEN ) >=0 ) goto out; ret = true;
Greg Kroah-Hartman
maintainerPATCH 951/1266 batman-adv: tt: avoid request storms during pending request
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 27c7d40008231ae4140d35501b60087a9de2d2c3 upstream.
batadv_send_tt_request() allocates a tt_req_node when none exists for the destination originator node. This should prevent that a multiple TT requests are send at the same time to an originator.
But if allocation of the send buffer failed, this request must be cleaned up again. But indicator for such a failure is "ret == false". But the actual implementation is checking for "ret == true".
The check must be inverted to not loose the information about the TT request directly after it was attempted to be sent out. This should avoid potential request storms.
--- net/batman-adv/translation-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/batman-adv/translation-table.c +1 -1
@@ -3032,7 +3032,7 @@ static bool batadv_send_tt_request(struc out:batadv_hardif_put (primary_if); -if (ret && tt_req_node) { +if (!ret && tt_req_node) {spin_lock_bh (&bat_priv->tt.req_list_lock);if (!hlist_unhashed (&tt_req_node->list)) {hlist_del_init (&tt_req_node->list);
Greg Kroah-Hartman
maintainerPATCH 952/1266 batman-adv: fix VLAN priority offset
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit fdb3be00ba4dafa313e699d6b5b90d13f22f3f25 upstream.
The batadv_skb_set_priority() receives an SKB with the inner ethernet header at position "offset". When it tries to extract the IPv4 and IPv6 header, it needs to skip the ethernet header to get access to the IP header.
But for VLAN header, it performs the access with the struct vlan_ethhdr. This struct contains both both the ethernet header and the VLAN header. It is therefore incorrect to skip over the whole vlan_ethhdr size to get access to the vlan_ethhdr.
--- net/batman-adv/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/batman-adv/main.c +1 -1
@@ -375,7 +375,7 @@ void batadv_skb_set_priority(struct sk_bswitch (ethhdr->h_proto) {case htons (ETH_P_8021Q ): - vhdr =skb_header_pointer (skb, offset +sizeof (*vhdr), + vhdr =skb_header_pointer (skb, offset,sizeof (*vhdr), &vhdr_tmp);if (!vhdr)return ;
Greg Kroah-Hartman
maintainerPATCH 953/1266 batman-adv: frag: free unfragmentable packet
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 6b628425aed49a1c7a4ffc997583840fc582d32b upstream.
The caller of batadv_frag_send_packet() assume that the skb provided to the function are always consumed. But the pre-check for an empty payload or the zero fragment size returned an error without any further actions.
A failed pre-check must use the same error handling code as the rest of the function.
--- net/batman-adv/fragmentation.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
net/batman-adv/fragmentation.c +4 -2
@@ -522,8 +522,10 @@ int batadv_frag_send_packet(struct sk_bu mtu = min_t(unsigned int, mtu, BATADV_FRAG_MAX_FRAG_SIZE); max_fragment_size = mtu - header_size; -if (skb->len ==0 || max_fragment_size ==0 ) -return -EINVAL ; +if (skb->len ==0 || max_fragment_size ==0 ) { + ret = -EINVAL ; + goto free_skb; + } num_fragments = (skb->len -1 ) / max_fragment_size +1 ; max_fragment_size = (skb->len -1 ) / num_fragments +1 ;
Greg Kroah-Hartman
maintainerPATCH 954/1266 batman-adv: frag: fix primary_if leak on failed linearization
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 353d2c1d5492e53ae34f490a84494124dc3d3531 upstream.
If the skb has a frag_list, it must be linearized before it can be split using skb_split(). But when this step failed, it must not only free the skb but also take care of the reference to the already found primary_if.
--- net/batman-adv/fragmentation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/batman-adv/fragmentation.c +1 -1
@@ -551,7 +551,7 @@ int batadv_frag_send_packet(struct sk_bu*/ if (skb_has_frag_list (skb) &&__skb_linearize (skb)) { ret = -ENOMEM ; - goto free_skb; + goto put_primary_if; }/* Create one header to be copied to all fragments */
Greg Kroah-Hartman
maintainerPATCH 955/1266 batman-adv: tt: prevent TVLV OOB check overflow
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 7a581d9aaba8c82bd6177fa36b2588eea77f6e2b upstream.
A TT unicast TVLV contains the number of VLANs stored in it. This number is an u16 and gets multiplied by the size of the struct batadv_tvlv_tt_vlan_data (8 bytes). The size can therefore overflow the u16 used to store the tt_vlan_len. All additional safety checks to prevent out-of-bounds access of the TVLV buffer are invalid due to this overflow.
Using size_t prevents this overflow and ensures that the safety checks compare against the actual buffer requirements.
--- net/batman-adv/translation-table.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/batman-adv/translation-table.c +2 -1
@@ -4101,7 +4101,8 @@ static int batadv_tt_tvlv_unicast_handle u16 tvlv_value_len) {struct batadv_tvlv_tt_data *tt_data; -u16 tt_vlan_len, tt_num_entries; +u16 tt_num_entries; +size_t tt_vlan_len;char tt_flag;bool ret;
Greg Kroah-Hartman
maintainerPATCH 956/1266 cifs: invalidate cfid on unlink/rename/rmdir
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shyam Prasad N <[email protected]>
commit f71d68d2b473e47db260c27c98212829590d8bae upstream.
Today we do not invalidate the cached_dirent or the entire parent cfid when a dentry in a dir has been removed/moved.
This change invalidates the parent cfid so that we don't serve directory contents from the cache.
--- fs/smb/client/inode.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-)
fs/smb/client/inode.c +29 -1
@@ -119,6 +119,23 @@ cifs_revalidate_cache(struct inode *inodfscache_invalidate (cifs_inode_cookie (inode), &cd,i_size_read (inode),0 ); } +static void cifs_invalidate_cached_dir (struct cifs_tcon *tcon, +struct dentry *parent) +{ +struct cached_fid *parent_cfid =NULL ; + +if (!tcon || !parent) +return ; + +if (!open_cached_dir_by_dentry (tcon, parent, &parent_cfid)) { +mutex_lock (&parent_cfid->dirents.de_mutex); + parent_cfid->dirents.is_valid = false; + parent_cfid->dirents.is_failed = true; +mutex_unlock (&parent_cfid->dirents.de_mutex); +close_cached_dir (parent_cfid); + } +} +/* * copy nlink to the inode, unless it wasn't provided. Provide * sane values if we don't have an existing one and none was provided
@@ -1964,6 +1981,9 @@ psx_del_no_retry:cifs_set_file_info (inode, attrs, xid, full_path, origattr); out_reval: +if (!rc && dentry->d_parent) +cifs_invalidate_cached_dir (tcon, dentry->d_parent); +if (inode) { cifs_inode =CIFS_I (inode); cifs_inode->time =0 ;/* will force revalidate to get info
@@ -2268,13 +2288,14 @@ int cifs_rmdir(struct inode *inode, stru } rc = server->ops->rmdir (xid, tcon, full_path, cifs_sb); -cifs_put_tlink (tlink);if (!rc) {spin_lock (&d_inode (direntry)->i_lock);i_size_write (d_inode (direntry),0 );clear_nlink (d_inode (direntry));spin_unlock (&d_inode (direntry)->i_lock); +if (direntry->d_parent) +cifs_invalidate_cached_dir (tcon, direntry->d_parent); } cifsInode =CIFS_I (d_inode (direntry));
@@ -2290,6 +2311,7 @@ int cifs_rmdir(struct inode *inode, struinode_set_ctime_current (d_inode (direntry));inode_set_mtime_to_ts (inode,inode_set_ctime_current (inode)); +cifs_put_tlink (tlink); rmdir_exit:free_dentry_path (page);
@@ -2512,6 +2534,12 @@ unlink_target: }/* force revalidate to go get info when needed */ +if (!rc) { +cifs_invalidate_cached_dir (tcon, source_dentry->d_parent); +if (target_dentry->d_parent != source_dentry->d_parent) +cifs_invalidate_cached_dir (tcon, target_dentry->d_parent); + } +CIFS_I (source_dir)->time =CIFS_I (target_dir)->time =0 ; cifs_rename_exit:
Greg Kroah-Hartman
maintainerPATCH 957/1266 mfd: tps6586x: Fix OF node refcount
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bartosz Golaszewski <[email protected]>
commit 3001ed2b4e06da2276c42ace6551617065a5b1f9 upstream.
Platform devices created with platform_device_alloc() call platform_device_release() when the last reference to the device's kobject is dropped. This function calls of_node_put() unconditionally. This works fine for devices created with platform_device_register_full() but users of the split approach (platform_device_alloc() + platform_device_add()) must bump the reference of the of_node they assign manually. Add the missing call to of_node_get().
--- drivers/mfd/tps6586x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/mfd/tps6586x.c +1 -1
@@ -395,7 +395,7 @@ static int tps6586x_add_subdevs(struct t pdev->dev.parent = tps6586x->dev; pdev->dev.platform_data = subdev->platform_data; - pdev->dev.of_node = subdev->of_node; + pdev->dev.of_node =of_node_get (subdev->of_node); ret =platform_device_add (pdev);if (ret) {
Greg Kroah-Hartman
maintainerPATCH 958/1266 HID: playstation: validate num_touch_reports in DualShock 4 reports
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benoît Sevens <[email protected]>
commit 82a4fc46330910b4c1d9b189561439d468e3ff11 upstream.
The DualShock 4 HID driver fails to validate the num_touch_reports field received from the device in both USB and Bluetooth input reports. A malicious device could set this field to a value larger than the allocated size of the touch_reports array (3 for USB, 4 for Bluetooth), leading to an out-of-bounds read in dualshock4_parse_report().
This can result in kernel memory disclosure when processing malicious HID reports.
Validate num_touch_reports against the array size for the respective connection types before processing the touch data.
--- drivers/hid/hid-playstation.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/hid/hid-playstation.c +12 -0
@@ -2199,6 +2199,12 @@ static int dualshock4_parse_report(struc size == DS4_INPUT_REPORT_USB_SIZE) {struct dualshock4_input_report_usb *usb = (struct dualshock4_input_report_usb *)data; +if (usb->num_touch_reports >ARRAY_SIZE (usb->touch_reports)) { +hid_err (hdev,"DualShock4 USB input report has invalid num_touch_reports=%d\n" , + usb->num_touch_reports); +return -EINVAL ; + } + ds4_report = &usb->common; num_touch_reports =min_t (u8, usb->num_touch_reports,ARRAY_SIZE (usb->touch_reports));
@@ -2214,6 +2220,12 @@ static int dualshock4_parse_report(strucreturn -EILSEQ ; } +if (bt->num_touch_reports >ARRAY_SIZE (bt->touch_reports)) { +hid_err (hdev,"DualShock4 BT input report has invalid num_touch_reports=%d\n" , + bt->num_touch_reports); +return -EINVAL ; + } + ds4_report = &bt->common; num_touch_reports =min_t (u8, bt->num_touch_reports,ARRAY_SIZE (bt->touch_reports));
Greg Kroah-Hartman
maintainerPATCH 959/1266 Bluetooth: SCO: fix sleeping under spinlock in sco_conn_ready
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <[email protected]>
commit b819db93d73f4593636299e229914052b89e3ef2 upstream.
sco_conn_ready calls sleeping functions under conn->lock spinlock.
The critical section can be reduced: conn->hcon is modified only with hdev->lock held. It is guaranteed to be held in sco_conn_ready, so conn->lock is not needed to guard it.
Move taking conn->lock after lock_sock(parent). This also follows the lock ordering lock_sock() > conn->lock elsewhere in the file.
--- net/bluetooth/sco.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-)
net/bluetooth/sco.c +9 -11
@@ -1314,26 +1314,24 @@ static void sco_conn_ready(struct sco_co sk->sk_state_change (sk);release_sock (sk); }else { -sco_conn_lock (conn); - -if (!conn->hcon) { -sco_conn_unlock (conn); +if (!conn->hcon)return ; - } + +lockdep_assert_held (&conn->hcon->hdev->lock); parent =sco_get_sock_listen (&conn->hcon->src); -if (!parent) { -sco_conn_unlock (conn); +if (!parent)return ; - }lock_sock (parent); +sco_conn_lock (conn); + sk =sco_sock_alloc (sock_net (parent),NULL ,BTPROTO_SCO ,GFP_ATOMIC ,0 );if (!sk) { -release_sock (parent);sco_conn_unlock (conn); +release_sock (parent);return ; }
@@ -1353,9 +1351,9 @@ static void sco_conn_ready(struct sco_co/* Wake up parent */ parent->sk_data_ready (parent); -release_sock (parent); -sco_conn_unlock (conn); + +release_sock (parent); } }
Greg Kroah-Hartman
maintainerPATCH 960/1266 Bluetooth: SCO: hold sk properly in sco_conn_ready
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <[email protected]>
commit 4e37f6452d586b95c346a9abdd2fb80b67794f39 upstream.
sk deref in sco_conn_ready must be done either under conn->lock, or holding a refcount, to avoid concurrent close. conn->sk and parent sk is currently accessed without either, and without checking parent->sk_state:
[Task 1] [Task 2]
sco_sock_release
sco_conn_ready
sk = conn->sk
lock_sock(sk)
conn->sk = NULL
lock_sock(sk)
release_sock(sk)
sco_sock_kill(sk)
UAF on sk deref
and similarly for access to sco_get_sock_listen() return value.
Fix possible UAF by holding sk refcount in sco_conn_ready() and making sco_get_sock_listen() increase refcount. Also recheck after lock_sock that the socket is still valid. Adjust conn->sk locking so it's protected also by lock_sock() of the associated socket if any.
--- net/bluetooth/sco.c | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-)
net/bluetooth/sco.c +32 -12
@@ -409,9 +409,13 @@ static struct sock *sco_get_sock_listen( sk1 = sk; } + sk = sk ? sk : sk1; +if (sk) +sock_hold (sk); +read_unlock (&sco_sk_list.lock); -return sk ? sk : sk1; +return sk; }static void sco_sock_destruct (struct sock *sk)
@@ -451,11 +455,13 @@ static void sco_sock_kill(struct sock *sBT_DBG ("sk %p state %d" , sk, sk->sk_state);/* Sock is dead, so set conn->sk to NULL to avoid possible UAF */ +lock_sock (sk);if (sco_pi (sk)->conn) {sco_conn_lock (sco_pi (sk)->conn);sco_pi (sk)->conn->sk =NULL ;sco_conn_unlock (sco_pi (sk)->conn); } +release_sock (sk);/* Kill poor orphan */ bt_sock_unlink (&sco_sk_list, sk);
@@ -1302,17 +1308,28 @@ static int sco_sock_release(struct sockestatic void sco_conn_ready (struct sco_conn *conn) { -struct sock *parent; -struct sock *sk = conn->sk; +struct sock *parent, *sk; + +sco_conn_lock (conn); + sk =sco_sock_hold (conn); +sco_conn_unlock (conn);BT_DBG ("conn %p" , conn);if (sk) {lock_sock (sk); -sco_sock_clear_timer (sk); - sk->sk_state =BT_CONNECTED ; - sk->sk_state_change (sk); + +/* conn->sk may have become NULL if racing with sk close, but +* due to held hdev->lock, it can't become different sk. +*/ +if (conn->sk) { +sco_sock_clear_timer (sk); + sk->sk_state =BT_CONNECTED ; + sk->sk_state_change (sk); + } +release_sock (sk); +sock_put (sk); }else {if (!conn->hcon)return ;
@@ -1327,13 +1344,15 @@ static void sco_conn_ready(struct sco_cosco_conn_lock (conn); +/* hdev->lock guarantees conn->sk == NULL still here */ + +if (parent->sk_state !=BT_LISTEN ) + goto release; + sk =sco_sock_alloc (sock_net (parent),NULL ,BTPROTO_SCO ,GFP_ATOMIC ,0 ); -if (!sk) { -sco_conn_unlock (conn); -release_sock (parent); -return ; - } +if (!sk) + goto release;sco_sock_init (sk, parent);
@@ -1351,9 +1370,10 @@ static void sco_conn_ready(struct sco_co/* Wake up parent */ parent->sk_data_ready (parent); +release:sco_conn_unlock (conn); -release_sock (parent); +sock_put (parent); } }
Greg Kroah-Hartman
maintainerPATCH 961/1266 jbd2: fix integer underflow in jbd2_journal_initialize_fast_commit()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junrui Luo <[email protected]>
commit 289a2ca0c9b7eae74f93fc213b0b971669b8683d upstream.
jbd2_journal_initialize_fast_commit() validates journal capacity by checking (journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS). Both j_last and num_fc_blks are unsigned, so when num_fc_blks exceeds j_last the subtraction wraps to a large value, bypassing the bounds check.
The resulting underflow corrupts j_last, j_fc_first, and j_free, leading to journal abort.
Fix by checking num_fc_blks against j_last before the subtraction, returning -EFSCORRUPTED.
--- fs/jbd2/journal.c | 2 ++ 1 file changed, 2 insertions(+)
fs/jbd2/journal.c +2 -0
@@ -2303,6 +2303,8 @@ jbd2_journal_initialize_fast_commit(jourunsigned long long num_fc_blks; num_fc_blks =jbd2_journal_get_num_fc_blks (sb); +if (num_fc_blks > journal->j_last) +return -EFSCORRUPTED ;if (journal->j_last - num_fc_blks <JBD2_MIN_JOURNAL_BLOCKS )return -ENOSPC ;
Greg Kroah-Hartman
maintainerPATCH 962/1266 nvdimm/btt: Free arenas on btt_init() error paths
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 1a6b6442a982d0ca5fb6a1a39b6f6dfd760eda57 upstream.
The arenas allocated by discover_arenas() or create_arenas() are not freed on some error paths in btt_init(). This leaks memory when BTT initialization fails.
Call free_arenas() from the affected error paths to release the allocations.
[ as: commit message and log edits ]
--- drivers/nvdimm/btt.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/nvdimm/btt.c +7 -4
@@ -1593,7 +1593,7 @@ static struct btt *btt_init(struct nd_btif (btt->init_state !=INIT_READY && nd_region->ro) {dev_warn (dev,"%s is read-only, unable to init btt metadata\n" ,dev_name (&nd_region->dev)); -return NULL ; + goto err; }else if (btt->init_state !=INIT_READY ) { btt->num_arenas = (rawsize /ARENA_MAX_SIZE ) + ((rawsize %ARENA_MAX_SIZE ) ?1 :0 );
@@ -1603,25 +1603,28 @@ static struct btt *btt_init(struct nd_bt ret =create_arenas (btt);if (ret) {dev_info (dev,"init: create_arenas: %d\n" , ret); -return NULL ; + goto err; } ret =btt_meta_init (btt);if (ret) {dev_err (dev,"init: error in meta_init: %d\n" , ret); -return NULL ; + goto err; } } ret =btt_blk_init (btt);if (ret) {dev_err (dev,"init: error in blk_init: %d\n" , ret); -return NULL ; + goto err; }btt_debugfs_init (btt);return btt; +err: +free_arenas (btt); +return NULL ; }/**
Greg Kroah-Hartman
maintainerPATCH 963/1266 nvdimm/btt: Free arena sub-allocations on discover_arenas() error path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 13fe4cd9ddd0aacb7777812328be525a11ea3fea upstream.
Memory allocated by btt_freelist_init(), btt_rtt_init(), and btt_maplocks_init() is not freed on some discover_arenas() error paths. This leaks memory when arena discovery fails.
Add the missing kfree() calls to release the allocations before returning an error.
[ as: commit message and log edits ]
--- drivers/nvdimm/btt.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/nvdimm/btt.c +3 -0
@@ -923,6 +923,9 @@ static int discover_arenas(struct btt *breturn ret; out: +kfree (arena->freelist); +kfree (arena->rtt); +kfree (arena->map_locks);kfree (arena);free_arenas (btt); out_super:
Greg Kroah-Hartman
maintainerPATCH 964/1266 sunrpc: pin svc_xprt across the asynchronous TLS handshake callback
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 4f988f3a2808fb659f3880c282041ff067acad78 upstream.
svc_tcp_handshake() stores the raw svc_xprt pointer in tls_handshake_args.ta_data and submits the request through tls_server_hello_x509(). The handshake core takes only sock_hold(req->hr_sk); nothing references the embedding struct svc_sock that svc_tcp_handshake_done() reaches via container_of().
Two close races leave the in-flight callback writing through a freed svc_sock. svc_sock_free() calls tls_handshake_cancel() and discards its return value: a false return means handshake_complete() has already set HANDSHAKE_F_REQ_COMPLETED but hp_done() may not have finished, yet svc_sock_free() proceeds to kfree(svsk). The cancel-loser fall-through inside svc_tcp_handshake() itself produces the same window: when wait_for_completion_interruptible_timeout() returns <= 0 (timeout or signal) and tls_handshake_cancel() returns false, the function does not drain, returns, and svc_handle_xprt() calls svc_xprt_received(), which clears XPT_BUSY and can drop the last reference. A concurrent close then runs svc_sock_free() while svc_tcp_handshake_done() is still updating xpt_flags and walking svsk->sk_handshake_done.
The corruption surfaces as set_bit/clear_bit RMW into the freed xpt_flags slab slot and as complete_all() walking and writing the freed wait_queue_head_t list embedded in sk_handshake_done -- a slab-corruption primitive, not a benign read. The path is reachable on any TLS-enabled NFS server whenever a connection close overlaps the tlshd downcall delivery window; the interruptible wait means signal delivery suffices, not just SVC_HANDSHAKE_TO expiry.
Take svc_xprt_get(xprt) immediately before tls_server_hello_x509() so the in-flight callback owns its own reference. Release it on the two edges where the callback is guaranteed not to fire -- submission failure from tls_server_hello_x509() and a successful tls_handshake_cancel() -- and at the tail of svc_tcp_handshake_done() after complete_all().
--- net/sunrpc/svcsock.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/sunrpc/svcsock.c +6 -0
@@ -462,6 +462,7 @@ static void svc_tcp_handshake_done(void }clear_bit (XPT_HANDSHAKE , &xprt->xpt_flags);complete_all (&svsk->sk_handshake_done); +svc_xprt_put (xprt); }/**
@@ -485,9 +486,13 @@ static void svc_tcp_handshake(struct svcclear_bit (XPT_TLS_SESSION , &xprt->xpt_flags);init_completion (&svsk->sk_handshake_done); +/* Pin the transport across the asynchronous handshake callback. */ +svc_xprt_get (xprt); + ret =tls_server_hello_x509 (&args,GFP_KERNEL );if (ret) {trace_svc_tls_not_started (xprt); +svc_xprt_put (xprt); goto out_failed; }
@@ -496,6 +501,7 @@ static void svc_tcp_handshake(struct svcif (ret <=0 ) {if (tls_handshake_cancel (sk)) {trace_svc_tls_timed_out (xprt); +svc_xprt_put (xprt); goto out_close; } }
Greg Kroah-Hartman
maintainerPATCH 965/1266 sunrpc: wait for in-flight TLS handshake callback when cancel loses race
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit d00e32f84ca1a77cb67a3fbf59f58dada95f5a21 upstream.
When wait_for_completion_interruptible_timeout() in svc_tcp_handshake() returns 0 (timeout) or -ERESTARTSYS (signal) and tls_handshake_cancel() then returns false, handshake_complete() has won the cancellation race: it has set HANDSHAKE_F_REQ_COMPLETED and is about to invoke svc_tcp_handshake_done(), but the callback's side effects on xpt_flags and on svsk->sk_handshake_done have not yet committed.
The current code reads xpt_flags immediately to decide whether the session succeeded. Two races result.
If the callback has executed set_bit(XPT_TLS_SESSION) but not yet clear_bit(XPT_HANDSHAKE), svc_tcp_handshake() sees a session, enqueues the transport, and returns. svc_xprt_received() then clears XPT_BUSY, a worker thread picks the transport up, the dispatcher in svc_handle_xprt() observes XPT_HANDSHAKE still set, and xpo_handshake is invoked a second time. That svc_tcp_handshake() calls init_completion(&svsk->sk_handshake_done) while the original callback concurrently calls complete_all() on it, corrupting the embedded swait_queue.
If the callback has set HANDSHAKE_F_REQ_COMPLETED but not yet entered svc_tcp_handshake_done(), svc_tcp_handshake() reads XPT_TLS_SESSION as clear and tears the connection down even though the handshake is about to succeed.
Wait for the callback to commit before inspecting xpt_flags. The completion is guaranteed to fire because handshake_complete() invokes svc_tcp_handshake_done() unconditionally once it has set HANDSHAKE_F_REQ_COMPLETED.
--- net/sunrpc/svcsock.c | 4 ++++ 1 file changed, 4 insertions(+)
net/sunrpc/svcsock.c +4 -0
@@ -504,6 +504,10 @@ static void svc_tcp_handshake(struct svcsvc_xprt_put (xprt); goto out_close; } +/* Cancellation lost to handshake_complete(): the +* callback is in flight and should finish quickly. +*/ +wait_for_completion (&svsk->sk_handshake_done); }if (!test_bit (XPT_TLS_SESSION , &xprt->xpt_flags)) {
Greg Kroah-Hartman
maintainerPATCH 966/1266 lockd: Plug nlm_file leak when nlm_do_fopen() fails
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit f16a1513452edb532fec81e591c64c320866719c upstream.
A client can repeatedly drive nlm_do_fopen() failures by presenting file handles that the underlying export rejects. After kzalloc_obj() succeeds in nlm_lookup_file(), the freshly allocated nlm_file is not yet inserted into nlm_files[]. The nlm_do_fopen() failure path jumps to out_unlock, which releases nlm_file_mutex and returns without freeing the allocation, so each failure leaks one nlm_file.
Route the failure through out_free so kfree() runs before the function returns.
--- fs/lockd/svcsubs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/lockd/svcsubs.c +1 -1
@@ -139,7 +139,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, nfserr =nlm_do_fopen (rqstp, file, mode);if (nfserr) - goto out_unlock; + goto out_free;hlist_add_head (&file->f_list, &nlm_files[hash]);
Greg Kroah-Hartman
maintainerPATCH 967/1266 lockd: Plug nlm_file refcount leak on cached nlm_do_fopen() failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit 70a38f87bed7f0694fd07988b47b2db1e10d8df3 upstream.
The cached-file path in nlm_lookup_file() reaches the found: label unconditionally, even when nlm_do_fopen() fails. At that label *result and file->f_count are updated before the error is returned. The wrappers nlm3svc_lookup_file() and nlm4svc_lookup_file() then bail out of their switch without copying *result back to their caller, so the proc handler's local nlm_file pointer remains NULL and the cleanup path skips nlm_release_file(). The f_count increment is never released, and nlm_traverse_files() can no longer reap the file because its refcount never returns to zero between requests.
Short-circuit the cached path so neither *result nor f_count is touched when nlm_do_fopen() fails on a hashed nlm_file.
--- fs/lockd/svcsubs.c | 2 ++ 1 file changed, 2 insertions(+)
fs/lockd/svcsubs.c +2 -0
@@ -123,6 +123,8 @@ nlm_lookup_file(struct svc_rqst *rqstp,mutex_lock (&file->f_mutex); nfserr =nlm_do_fopen (rqstp, file, mode);mutex_unlock (&file->f_mutex); +if (nfserr) + goto out_unlock; goto found; }nlm_debug_print_fh ("creating file for" , &lock->fh);
Greg Kroah-Hartman
maintainerPATCH 968/1266 SUNRPC: Bound-check xdr_buf_to_bvec() stores before writing
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit 42f5b80dda6b86e424054baf1475df686c403d5c upstream.
xdr_buf_to_bvec() writes a bio_vec into the caller's array before testing whether that slot is in range, and the head branch performs the store with no check at all. When the caller's budget is exactly used up, the next store lands one element past the end of the array. The overflow label returns count - 1, which masks the surplus store but cannot undo it.
rq_bvec, the array passed by nfsd_vfs_write(), is allocated to exactly rq_maxpages entries with no slack. The OOB store can land in adjacent slab memory; the bv_len and bv_offset fields written there are derived from client-supplied RPC payload sizes.
Move the in-range check ahead of the store in the head, page-loop, and tail branches. With the check at the top of each sequence, count is incremented only after a successful store, so the overflow label can return count directly.
--- net/sunrpc/xdr.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
net/sunrpc/xdr.c +9 -5
@@ -180,6 +180,8 @@ unsigned int xdr_buf_to_bvec(struct bio_unsigned int count =0 ;if (head->iov_len) { +if (unlikely (count >= bvec_size)) + goto bvec_overflow;bvec_set_virt (bvec++, head->iov_base, head->iov_len); ++count; }
@@ -193,25 +195,27 @@ unsigned int xdr_buf_to_bvec(struct bio_while (remaining >0 ) { len = min_t(unsigned int, remaining,PAGE_SIZE - offset); +if (unlikely (count >= bvec_size)) + goto bvec_overflow;bvec_set_page (bvec++, *pages++, len, offset); remaining -= len; offset =0 ; -if (unlikely (++count > bvec_size)) - goto bvec_overflow; + ++count; } }if (tail->iov_len) { -bvec_set_virt (bvec, tail->iov_base, tail->iov_len); -if (unlikely (++count > bvec_size)) +if (unlikely (count >= bvec_size)) goto bvec_overflow; +bvec_set_virt (bvec, tail->iov_base, tail->iov_len); + ++count; }return count; bvec_overflow:pr_warn_once ("%s: bio_vec array overflow\n" , __func__); -return count -1 ; +return count; }/**
Greg Kroah-Hartman
maintainerPATCH 969/1266 remoteproc: qcom: Fix leak when custom dump_segments addition fails
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wasim Nazir <[email protected]>
commit ecf9fc18e62c58eae1ceb65dab2bccb8a724de2d upstream.
Free allocated minidump_region 'name' in qcom_add_minidump_segments() when failing before adding the region to 'dump_segments'. Otherwise, the 'name' is not tracked and is never freed by qcom_minidump_cleanup().
Return error when adding to 'dump_segments' fails.
--- drivers/remoteproc/qcom_common.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
drivers/remoteproc/qcom_common.c +10 -4
@@ -108,6 +108,7 @@ static int qcom_add_minidump_segments(ststruct minidump_region __iomem *ptr;struct minidump_region region;int seg_cnt, i; +int ret =0 ;dma_addr_t da;size_t size;char *name;
@@ -128,17 +129,22 @@ static int qcom_add_minidump_segments(stif (le32_to_cpu (region.valid) ==MINIDUMP_REGION_VALID ) { name =kstrndup (region.name,MAX_REGION_NAME_LENGTH -1 ,GFP_KERNEL );if (!name) { -iounmap (ptr); -return -ENOMEM ; + ret = -ENOMEM ; +break ; } da =le64_to_cpu (region.address); size =le64_to_cpu (region.size); -rproc_coredump_add_custom_segment (rproc, da, size, rproc_dumpfn_t, name); + ret =rproc_coredump_add_custom_segment (rproc, da, size, rproc_dumpfn_t, + name); +if (ret) { +kfree (name); +break ; + } } }iounmap (ptr); -return 0 ; +return ret; } void qcom_minidump(struct rproc *rproc, unsigned int minidump_id,
Greg Kroah-Hartman
maintainerPATCH 970/1266 MIPS: ip22-gio: fix gio device memory leak
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 7de9a1b45f5a95b58145653c525c8fb80292d9ab upstream.
The gio device release callback was never wired up so gio devices are not freed when the last reference is dropped.
--- arch/mips/sgi-ip22/ip22-gio.c | 2 ++ 1 file changed, 2 insertions(+)
arch/mips/sgi-ip22/ip22-gio.c +2 -0
@@ -101,6 +101,8 @@ int gio_device_register(struct gio_devic { giodev->dev.bus = &gio_bus_type; giodev->dev.parent = &gio_bus; + giodev->dev.release = gio_release_dev; +return device_register (&giodev->dev); }EXPORT_SYMBOL_GPL (gio_device_register);
Greg Kroah-Hartman
maintainerPATCH 971/1266 MIPS: ip22-gio: fix kfree() of static object
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit c62cdd3e919bdf84c37ec46810f87cdb1736e822 upstream.
The gio bus root device is a statically allocated object which must not be freed by kfree() on failure to register the device or bus.
--- arch/mips/sgi-ip22/ip22-gio.c | 1 - 1 file changed, 1 deletion(-)
arch/mips/sgi-ip22/ip22-gio.c +0 -1
@@ -30,7 +30,6 @@ static struct {static void gio_bus_release (struct device *dev) { -kfree (dev); }static struct device gio_bus = {
Greg Kroah-Hartman
maintainerPATCH 972/1266 MIPS: ip22-gio: fix device reference leak in probe
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit b82930a4c5dbc5c4df39c0f93d968c239f2c6885 upstream.
The gio probe function needlessly takes a device reference which is never released and therefore prevents unbound gio devices from being freed.
--- arch/mips/sgi-ip22/ip22-gio.c | 4 ---- 1 file changed, 4 deletions(-)
arch/mips/sgi-ip22/ip22-gio.c +0 -4
@@ -133,13 +133,9 @@ static int gio_device_probe(struct devicif (!drv->probe)return error; -gio_dev_get (gio_dev); - match =gio_match_device (drv->id_table, gio_dev);if (match) error = drv->probe (gio_dev, match); -if (error) -gio_dev_put (gio_dev);return error; }
Greg Kroah-Hartman
maintainerPATCH 973/1266 MIPS: DEC: Ensure 32-bit stack location for o32 prom_printf()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maciej W. Rozycki <[email protected]>
commit 5ff79e8bdc75db51e30298a75939e2308e7658e0 upstream.
In 64-bit configurations calling any firmware entry points from a kernel thread other than the initial one will result in a situation where the stack has been placed in the XKPHYS 64-bit memory segment.
Consequently the stack pointer is no longer a 32-bit value and when the 32-bit firmware code called uses 32-bit ALU operations to manipulate the stack pointer, the calculated result is incorrect (in fact in the 64-bit MIPS ISA almost all 32-bit ALU operations will produce an unpredictable result when executed on 64-bit data) and control goes astray.
This may happen when no final console driver has been enabled in the configuration and consequently the initial console continues being used late into bootstrap, or with an upcoming change that will switch the zs driver to use a platform device, which in turn will make the console handover happen only after other kernel threads have already been started, and the kernel will hang at:
pid_max: default: 32768 minimum: 301
or somewhat later, but always before:
cblist_init_generic: Setting adjustable number of callback queues.
has been printed.
It seems that only the prom_printf() entry point is affected. Of all the other entry points wired only rex_slot_address() and rex_gettcinfo() are called from a kernel thread other than the initial one, specifically kernel_init(), and they are leaf functions that do no business with the stack, having worked with no issue ever since 64-bit support was added for the platform back in 2002.
To address this issue then, arrange for the stack to be switched in the o32 wrapper as required for prom_printf() only, by supplying call_o32() with a pointer to a chunk of initdata space, which is placed in the CKSEG0 32-bit compatibility segment, observing that prom_printf() is only called from console output handler and therefore with the console lock held, implying no need for this code to be reentrant.
Other firmware entry points may be called with interrupts enabled and no lock held, and may therefore require that call_o32() be reentrant. They trigger no issue at this point and "if it ain't broke, don't fix it," so just leave them alone.
--- arch/mips/dec/prom/init.c | 6 +++++- arch/mips/include/asm/dec/prom.h | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-)
arch/mips/dec/prom/init.c +5 -1
@@ -3,7 +3,7 @@* init.c: PROM library initialisation code. * * Copyright (C) 1998 Harald Koerfgen -* Copyright (C) 2002, 2004 Maciej W. Rozycki +* Copyright (C) 2002, 2004, 2026 Maciej W. Rozycki */ #include <linux/init.h> #include <linux/kernel.h>
@@ -20,6 +20,10 @@#include <asm/dec/prom.h> +#ifdef CONFIG_64BIT +unsigned long o32_stk[O32_STK_SIZE ]__initdata = {0 }; +#endif +int (*__rex_bootinit)(void );int (*__rex_bootread)(void );int (*__rex_getbitmap)(memmap *);
arch/mips/include/asm/dec/prom.h +13 -2
@@ -4,7 +4,7 @@* * DECstation PROM interface. * -* Copyright (C) 2002 Maciej W. Rozycki +* Copyright (C) 2002, 2026 Maciej W. Rozycki * * Based on arch/mips/dec/prom/prom.h by the Anonymous. */
@@ -97,6 +97,17 @@ extern int (*__pmax_close)(int);#ifdef CONFIG_64BIT +#define O32_STK_SIZE 512 +extern unsigned long o32_stk[]; + +/* Switch the stack if outside the 32-bit address space. */ +static inline unsigned long *o32_get_stk (void ) +{ +long fp = (long )__builtin_frame_address (0 ); + +return fp != (int )fp ? o32_stk +O32_STK_SIZE :NULL ; +} +/* * On MIPS64 we have to call PROM functions via a helper * dispatcher to accommodate ABI incompatibilities.
@@ -128,7 +139,7 @@ int __DEC_PROM_O32(_prom_printf, (int (*#define prom_getchar () _prom_getchar(__prom_getchar, NULL)#define prom_getenv (x) _prom_getenv(__prom_getenv, NULL, x) -#define prom_printf(x...) _prom_printf(__prom_printf, NULL, x) +#define prom_printf(x...) _prom_printf(__prom_printf, o32_get_stk(), x)#else /* !CONFIG_64BIT */
Greg Kroah-Hartman
maintainerPATCH 974/1266 power: supply: cpcap-battery: Fix missing nvmem_device_put() causing reference leak
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ma Ke <[email protected]>
commit a2c14ff63e0e02e3c832385e523e9cc81301171c upstream.
In cpcap_battery_detect_battery_type(), the reference to an nvmem device obtained via nvmem_device_find() is not released with nvmem_device_put() on the success or read-failure paths, causing a permanent reference leak. The driver’s retry logic on subsequent battery property reads can compound this leak, preventing the nvmem device from ever being freed.
Found by code review.
--- drivers/power/supply/cpcap-battery.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/power/supply/cpcap-battery.c +7 -4
@@ -415,10 +415,13 @@ static void cpcap_battery_detect_batteryif (IS_ERR_OR_NULL (nvmem)) { ddata->check_nvmem = true;dev_info_once (ddata->dev,"Can not find battery nvmem device. Assuming generic lipo battery\n" ); - }else if (nvmem_device_read (nvmem,2 ,1 , &battery_id) <0 ) { - battery_id =0 ; - ddata->check_nvmem = true; -dev_warn (ddata->dev,"Can not read battery nvmem device. Assuming generic lipo battery\n" ); + }else { +if (nvmem_device_read (nvmem,2 ,1 , &battery_id) <0 ) { + battery_id =0 ; + ddata->check_nvmem = true; +dev_warn (ddata->dev,"Can not read battery nvmem device. Assuming generic lipo battery\n" ); + } +nvmem_device_put (nvmem); }switch (battery_id) {
Greg Kroah-Hartman
maintainerPATCH 975/1266 mm/damon/core: make charge_addr_from aware of end-address exclusivity
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: SeongJae Park <[email protected]>
commit 5a2d162e22bf33eb89d53e802d0fc1ec422e19b6 upstream.
DAMON region end address is exclusive one, but charge_addr_from is assigned assuming the end address is inclusive. As a result, DAMOS action to next up to min_region_sz memory can be skipped. This is quite negligible user impact. But, the bug is a bug that can be very simply fixed. Fix the wrong assignment to respect the exclusiveness of the address.
The issue was discovered [1] by Sashiko.
--- mm/damon/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/damon/core.c +1 -1
@@ -1003,7 +1003,7 @@ static void damos_apply_scheme(struct da quota->charged_sz += sz;if (quota->esz && quota->charged_sz >= quota->esz) { quota->charge_target_from = t; - quota->charge_addr_from = r->ar.end +1 ; + quota->charge_addr_from = r->ar.end; } }if (s->action !=DAMOS_STAT )
Greg Kroah-Hartman
maintainerPATCH 976/1266 fs/ntfs3: fix syncing wrong inode on DIRSYNC cross-directory rename
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhan Xusheng <[email protected]>
commit 932fa0c1496286e39e14e27194c1ee7c2181629f upstream.
In ntfs3_rename(), when IS_DIRSYNC(new_dir) is true, the code syncs the renamed file inode instead of the target directory new_dir:
if (IS_DIRSYNC(new_dir))
ntfs_sync_inode(inode); /* should be new_dir */
DIRSYNC requires that directory metadata changes are written to disk synchronously. Since new_dir was modified (a new directory entry was added), it is new_dir that must be synced to satisfy the guarantee, not the renamed file itself.
This bug has existed since the initial ntfs3 implementation and was carried through the refactoring in commit 78ab59fee07f ("fs/ntfs3: Rework file operations").
Fix by syncing new_dir instead of inode.
--- fs/ntfs3/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/ntfs3/namei.c +1 -1
@@ -341,7 +341,7 @@ static int ntfs_rename(struct mnt_idmapntfs_sync_inode (dir);if (IS_DIRSYNC (new_dir)) -ntfs_sync_inode (inode); +ntfs_sync_inode (new_dir); }if (dir_ni != new_dir_ni)
Greg Kroah-Hartman
maintainerPATCH 977/1266 fs/ntfs3: bound DeleteIndexEntryAllocation memmove length
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Konstantin Komarov <[email protected]>
commit fc4626bb3656362de8b0ecd56605d47a19ec3518 upstream.
In do_action()'s DeleteIndexEntryAllocation case, e->size comes from an on-disk INDEX_BUFFER entry. When e->size makes e + e->size point past hdr + hdr->used,PtrOffset(e1, Add2Ptr(hdr, used)) returns a negative ptrdiff_t that is silently cast to a quasi-infinite size_t when passed to memmove(). The memmove then walks past the destination buffer.
The sibling DeleteIndexEntryRoot case at fslog.c:3540-3543 already carries the corresponding guard:
if (PtrOffset(e1, Add2Ptr(hdr, used)) < esize ||
Add2Ptr(e, esize) > Add2Ptr(lrh, rec_len) ||
used + esize > le32_to_cpu(hdr->total)) {
goto dirty_vol;
}Apply the same shape to the allocation-path case. Also reject esize == 0: memmove(e, e, ...) is a no-op and leaves hdr->used unchanged, hiding a malformed entry from the existing check_index_header() walk.
Reproduced under UML+KASAN on mainline 8d90b09e6741 by mounting a crafted NTFS image: the unguarded memmove takes a length of 0xffffffffffffff00 and the kernel oopses in memmove+0x81/0x1a0 on the do_action+0x36a2 frame.
--- fs/ntfs3/fslog.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
fs/ntfs3/fslog.c +15 -1
@@ -3574,9 +3574,23 @@ move_data: } e1 =Add2Ptr (e, esize); - nsize = esize; used =le32_to_cpu (hdr->used); +/* +* Reject crafted entries whose e->size makes e + esize +* point past the INDEX_HDR's used boundary. Without this, +* PtrOffset(e1, hdr + used) underflows to a quasi-infinite +* size_t when fed to the memmove() below. +* +* Also reject esize == 0: memmove(e, e, ...) is a no-op and +* leaves hdr->used unchanged, masking the crafted entry. +*/ +if (!esize ||Add2Ptr (e, esize) >Add2Ptr (hdr, used) || +PtrOffset (e1,Add2Ptr (hdr, used)) < esize) + goto dirty_vol; + + nsize = esize; +memmove (e, e1,PtrOffset (e1,Add2Ptr (hdr, used))); hdr->used =cpu_to_le32 (used - nsize);
Greg Kroah-Hartman
maintainerPATCH 978/1266 fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis pass
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 5e7b598660cfa8e5af172cf4c65cffc126333307 upstream.
In log_replay()'s analysis pass, after find_dp() returns a valid DIR_PAGE_ENTRY for the (target_attr, target_vcn) tuple, the copy_lcns block walks lrh->lcns_follow further entries:
t16 = le16_to_cpu(lrh->lcns_follow);
for (i = 0; i < t16; i++) {
size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) -
le64_to_cpu(dp->vcn));
dp->page_lcns[j + i] = lrh->page_lcns[i];
}find_dp() only validates that target_vcn falls within [dp->vcn, dp->vcn + dp->lcns_follow), i.e., that the FIRST cluster is covered. The walk through the further entries is not bounded against dp->lcns_follow. For a malformed LRH where target_vcn = dp->vcn + dp->lcns_follow - 1 and lrh->lcns_follow > 1, the i > 0 writes overflow the dp's allocated page_lcns[] array.
Add the missing j + lrh->lcns_follow <= dp->lcns_follow guard.
Reproduced under UML+KASAN on mainline 8d90b09e6741 as a slab-out-of-bounds write of size 8 from log_replay+0x68d4 on the mount path.
This is distinct from Pavitra Jha's 2026-05-02 patch ("fs/ntfs3: validate lcns_follow in log_replay conversion", <[email protected]>) which addresses the separate version-0 dirty-page-table conversion path's memmove(&dp->vcn, ...) call. The two fixes are complementary; both should land.
[[email protected]: clang-formatted the changes, fixed conflicts]
--- fs/ntfs3/fslog.c | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-)
fs/ntfs3/fslog.c +29 -17
@@ -3365,8 +3365,8 @@ move_data:if (run_get_highest_vcn (le64_to_cpu (attr->nres.svcn),attr_run (attr), -le32_to_cpu (attr->size) - -le16_to_cpu (attr->nres.run_off), +le32_to_cpu (attr->size) - +le16_to_cpu (attr->nres.run_off), &t64)) { goto dirty_vol; }
@@ -4581,22 +4581,34 @@ copy_lcns:* whole routine a loop, case Lcns do not fit below. */ t16 =le16_to_cpu (lrh->lcns_follow); - t32 =le32_to_cpu (dp->lcns_follow); -if (le64_to_cpu (lrh->target_vcn) <le64_to_cpu (dp->vcn)) { - err = -EINVAL ; - goto out; - } - -for (i =0 ; i < t16; i++) { -size_t j = (size_t )(le64_to_cpu (lrh->target_vcn) - -le64_to_cpu (dp->vcn)); -if (j >= t32 || i >= t32 - j) { - err = -EINVAL ; - goto out; - } - dp->page_lcns[j + i] = lrh->page_lcns[i]; - } + t32 =le32_to_cpu (dp->lcns_follow); +if (le64_to_cpu (lrh->target_vcn) <le64_to_cpu (dp->vcn)) { + err = -EINVAL ; + goto out; + } + +/* +* find_dp() only validates that target_vcn is the first +* cluster covered by dp. The walk through lrh->lcns_follow +* further entries must stay within the allocated +* dp->page_lcns[] array, which is sized by dp->lcns_follow. +*/ +if (le64_to_cpu (lrh->target_vcn) -le64_to_cpu (dp->vcn) + t16 > +le32_to_cpu (dp->lcns_follow)) { + err = -EINVAL ; + log->set_dirty = true; + goto out; + } +for (i =0 ; i < t16; i++) { +size_t j = (size_t )(le64_to_cpu (lrh->target_vcn) - +le64_to_cpu (dp->vcn)); +if (j >= t32 || i >= t32 - j) { + err = -EINVAL ; + goto out; + } + dp->page_lcns[j + i] = lrh->page_lcns[i]; + } goto next_log_record_analyze;case DeleteDirtyClusters: {
Greg Kroah-Hartman
maintainerPATCH 979/1266 fs/ntfs3: bound attr_off in UpdateResidentValue against data_off
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Konstantin Komarov <[email protected]>
commit d1570c48f49a693974d000251030370ee2e83539 upstream.
In do_action()'s UpdateResidentValue case (fslog.c:3307), lrh->attr_off and lrh->redo_len come from the on-disk LRH. When they satisfy aoff + dlen < attr->res.data_off, the assignment
attr->res.data_size = cpu_to_le32(aoff + dlen - data_off);
underflows to ~4 GiB (e.g. 0xFFFFFFF9 when aoff=0x10, dlen=1, data_off=0x18). Subsequent code that reads attr->res.data_size to walk the resident attribute payload would then read up to 4 GiB past the 1024-byte MFT record allocation.
The existing mi_enum_attr() defense in fs/ntfs3/record.c:287 catches the corrupted data_size on the next attribute walk and fails the mount, but only on the path that walks all attributes. A read site that picks an attribute by name and reads its data_size without re-validating is not covered. Validate aoff against data_off and asize at the source.
Reproduced under UML+KASAN on mainline 8d90b09e6741 via pr_warn-only probe: with aoff=0x10 and data_off=0x18, the post-assignment data_size is 0xfffffff9 (mount then fails at -22 from mi_enum_attr).
--- fs/ntfs3/fslog.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
fs/ntfs3/fslog.c +11 -0
@@ -3313,6 +3313,17 @@ skip_load_parent: nsize =ALIGN (nsize,8 ); data_off =le16_to_cpu (attr->res.data_off); +/* +* aoff comes from the on-disk lrh->attr_off. Forbid +* writes that begin below the resident attribute's +* data_off (which would overwrite the resident header), +* and forbid aoff + dlen < data_off, which would make +* the data_size assignment below underflow to ~4 GiB. +*/ +if (aoff < data_off || aoff + dlen < data_off || + aoff + dlen > asize) + goto dirty_vol; +if (nsize < asize) {memmove (Add2Ptr (attr, aoff), data, dlen); data =NULL ;// To skip below memmove().
Greg Kroah-Hartman
maintainerPATCH 980/1266 fs/ntfs3: validate lcns_follow in log_replay conversion
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Konstantin Komarov <[email protected]>
commit 6a4c53a2e26a865565bd6a460961e8d6fcb32329 upstream.
log_replay() converts DIR_PAGE_ENTRY_32 records into DIR_PAGE_ENTRY records when replaying version 0 restart tables.
During this conversion, the memmove() length is derived directly from the on-disk lcns_follow field:
memmove(&dp->vcn, &dp0->vcn_low, 2 * sizeof(u64) + le32_to_cpu(dp->lcns_follow) * sizeof(u64));
check_rstbl() validates restart table structure, but does not constrain per-entry lcns_follow values relative to the entry size. A malformed filesystem image can provide an oversized lcns_follow value, causing the conversion memmove() to access memory beyond the bounds of the allocated restart table buffer.
The same field is later used to bound iteration over page_lcns[], so validating lcns_follow during conversion also prevents downstream out-of-bounds access from the same malformed metadata.
Compute the maximum valid lcns_follow from the already-validated restart table entry size and reject entries that exceed this bound. Reuse the existing t16/t32 scratch variables already declared in log_replay() to avoid introducing new declarations.
--- fs/ntfs3/fslog.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
fs/ntfs3/fslog.c +16 -3
@@ -4263,13 +4263,26 @@ check_dirty_page_table:if (rst->major_ver) goto end_conv_1; + t16 =le16_to_cpu (dptbl->size); +if (t16 <sizeof (struct DIR_PAGE_ENTRY )) { + log->set_dirty = true; + goto out; + } + + t32 = (t16 -sizeof (struct DIR_PAGE_ENTRY )) /sizeof (u64); + dp =NULL ;while ((dp =enum_rstbl (dptbl, dp))) {struct DIR_PAGE_ENTRY_32 *dp0 = (struct DIR_PAGE_ENTRY_32 *)dp; -// NOTE: Danger. Check for of boundary. +u32 lcns =le32_to_cpu (dp->lcns_follow); + +if (lcns > t32) { + log->set_dirty = true; + goto out; + } +memmove (&dp->vcn, &dp0->vcn_low, -2 *sizeof (u64) + -le32_to_cpu (dp->lcns_follow) *sizeof (u64)); +2 *sizeof (u64) + lcns *sizeof (u64)); } end_conv_1:
Greg Kroah-Hartman
maintainerPATCH 981/1266 fs/ntfs3: add depth limit to indx_find_buffer to prevent stack overflow
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 1ebd684b8f627f75bc3e03f8b2ad8400fd1f02cd upstream.
indx_find_buffer() recursively descends the B+ tree index with no depth limit. A crafted NTFS image with circular index node references causes unbounded recursion, overflowing the kernel stack and panicking the system.
This is reachable by mounting a malicious NTFS filesystem (e.g. from a USB drive via desktop automount) and deleting a file whose index entry triggers the rebalancing fallback path in indx_delete_entry().
Add a depth parameter and bail out with -EINVAL when it reaches the fnd->nodes array bound, matching the constraint already enforced by fnd_push() in indx_find().
The related function indx_find() was previously patched for a similar infinite-loop issue (commit 1732053c8a6b), but indx_find_buffer() was missed.
--- fs/ntfs3/index.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
fs/ntfs3/index.c +12 -3
@@ -2016,13 +2016,21 @@ out1:static struct indx_node *indx_find_buffer (struct ntfs_index *indx,struct ntfs_inode *ni,const struct INDEX_ROOT *root, -__le64 vbn,struct indx_node *n) +__le64 vbn,struct indx_node *n, +int depth) {int err;const struct NTFS_DE *e;struct indx_node *r;const struct INDEX_HDR *hdr = n ? &n->index->ihdr : &root->ihdr; +/* +* Limit recursion depth to prevent stack overflow from crafted +* images. Use the same bound as the fnd->nodes array (20). +*/ +if (depth >ARRAY_SIZE (((struct ntfs_fnd *)NULL )->nodes)) +return ERR_PTR (-EINVAL ); +/* Step 1: Scan one level. */ for (e =hdr_first_de (hdr);; e =hdr_next_de (hdr, e)) {if (!e)
@@ -2043,7 +2051,8 @@ static struct indx_node *indx_find_buffeif (err)return ERR_PTR (err); - r =indx_find_buffer (indx, ni, root, vbn, n); + r =indx_find_buffer (indx, ni, root, vbn, n, + depth +1 );if (r)return r; }
@@ -2448,7 +2457,7 @@ int indx_delete_entry(struct ntfs_indexfnd_clear (fnd); - in =indx_find_buffer (indx, ni, root, sub_vbn,NULL ); + in =indx_find_buffer (indx, ni, root, sub_vbn,NULL ,0 );if (IS_ERR (in)) { err =PTR_ERR (in); goto out;
Greg Kroah-Hartman
maintainerPATCH 982/1266 fs/ntfs3: bound NTFS_DE view.data_off in UpdateRecordData{Root,Allocation}
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 3e127829e57f5190f612412ece4541cb96d5ec7a upstream.
In do_action()'s UpdateRecordDataRoot (fslog.c:3489) and UpdateRecordDataAllocation (fslog.c:3697) cases, the memmove destination is Add2Ptr(e, le16_to_cpu(e->view.data_off)), where e->view.data_off comes from an on-disk NTFS_DE inside an INDEX_ROOT or INDEX_BUFFER. Neither case validates view.data_off + dlen against e->size; the existing check_if_index_root / check_if_alloc_index helpers walk the entry chain and validate the entry's offset, but not its internal view fields.
The neighbouring read sites (e.g., fs/ntfs3/index.c when iterating view entries) check view.data_off + view.data_size <= e->size. Apply the same bound at the two memmove sites.
Reproduced under UML+KASAN on mainline 8d90b09e6741 via pr_warn-only probe instrumentation: with view.data_off forced to 0xFFFC, the memmove writes 32 bytes past the end of the NTFS_DE.
This is similar in shape to Pavitra Jha's 2026-05-02 patch "fs/ntfs3: prevent oob in case UpdateRecordDataRoot" (<[email protected]>) which proposes calling ntfs3_bad_de_range(); that helper does not exist in mainline. This patch uses inline checks.
--- fs/ntfs3/fslog.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
fs/ntfs3/fslog.c +18 -0
@@ -3512,6 +3512,18 @@ move_data: e =Add2Ptr (attr,le16_to_cpu (lrh->attr_off)); +/* +* e->view.data_off and dlen come from the on-disk +* INDEX_ROOT entry / LRH. The neighbouring read sites +* (e.g. fs/ntfs3/index.c) check that +* view.data_off + view.data_size <= e->size; mirror that +* bound here so the memmove cannot reach past the entry. +*/ +if (le16_to_cpu (e->view.data_off) >le16_to_cpu (e->size) || +le16_to_cpu (e->view.data_off) + dlen > +le16_to_cpu (e->size)) + goto dirty_vol; +memmove (Add2Ptr (e,le16_to_cpu (e->view.data_off)), data, dlen); mi->dirty = true;
@@ -3718,6 +3730,12 @@ move_data: goto dirty_vol; } +/* See UpdateRecordDataRoot for the rationale. */ +if (le16_to_cpu (e->view.data_off) >le16_to_cpu (e->size) || +le16_to_cpu (e->view.data_off) + dlen > +le16_to_cpu (e->size)) + goto dirty_vol; +memmove (Add2Ptr (e,le16_to_cpu (e->view.data_off)), data, dlen); a_dirty = true;
Greg Kroah-Hartman
maintainerPATCH 983/1266 ntfs3: cap RESTART_TABLE free-chain walker at rt->used
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 9611f644302c07d21bc8af97e3e06a3d30064253 upstream.
A crafted NTFS3 disk image triggers an in-kernel infinite loop at mount time, hanging the mounting thread and firing the soft-lockup watchdog within ~22s on multi-CPU hosts (panic with kernel.softlockup_panic=1). The bug is reachable from desktop USB auto-mount on distributions where udisks2 routes the NTFS signature to the in-tree ntfs3 driver (Arch family and an increasing fraction of Fedora / openSUSE / RHEL deployments); CAP_SYS_ADMIN-class manual mount elsewhere.
check_rstbl()'s second walker iterates the free-entry singly-linked list headed by rt->first_free with no upper bound on iteration count:
for (off = ff; off;) {
if (off == RESTART_ENTRY_ALLOCATED)
return false;
off = le32_to_cpu(*(__le32 *)Add2Ptr(rt, off));
if (off > ts - sizeof(__le32))
return false;
}The existing guards cover three exits: end-of-list (off == 0), the in-use marker (off == RESTART_ENTRY_ALLOCATED), and out-of-bounds (off > ts - sizeof(__le32)). None of the three prevents an in-bounds cycle.
A crafted on-disk RESTART_TABLE whose free chain contains a self-loop or A->B->A cycle whose offsets satisfy:
- in range [sizeof(struct RESTART_TABLE), ts - sizeof(__le32)]
- (off - sizeof(struct RESTART_TABLE)) % rsize == 0
passes all existing guards and spins the mount-time thread forever. Reproduced in UML by hand-forging a 2 MB NTFS3 image whose journal RESTART_TABLE first_free = 0x18 and whose entry at offset 0x18 stores 0x18 as its next pointer; mount of the forged image with the in-tree ntfs3 driver never returns.
Bound the walker by rt->used. Each entry on a legitimate free chain is unique, and the total slot count is ne = le16_to_cpu (rt->used). A traversal that visits more than ne slots is by construction malformed; reject it as a corrupt RESTART_TABLE.
After this patch, mount of the forged image returns with -EINVAL and a log_replay failure message, and mkntfs-produced legitimate images mount cleanly (verified in the same UML harness).
--- fs/ntfs3/fslog.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
fs/ntfs3/fslog.c +12 -1
@@ -764,8 +764,19 @@ static bool check_rstbl(const struct RES/* * Walk through the list headed by the first entry to make * sure none of the entries are currently being used. +* +* Bound traversal by ne (rt->used) to defeat a crafted on-disk +* cycle in the free chain. Each entry in a legitimate free +* list is unique, so a chain that visits more than ne slots +* is malformed. Without this guard, an attacker-controlled +* RESTART_TABLE with a self-loop or A->B->A cycle whose +* offsets satisfy the existing alignment + in-bounds guards +* spins forever at mount time. */ -for (off = ff; off;) { +for (off = ff, i =0 ; off; i++) { +if (i > ne) +return false; +if (off ==RESTART_ENTRY_ALLOCATED )return false;
Greg Kroah-Hartman
maintainerPATCH 984/1266 ntfs3: bound to_move in indx_insert_into_root before hdr_insert_head
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 9b6926ac9c970ae0b2c2fe6289b16e9aa10b6a67 upstream.
indx_insert_into_root() promotes a full resident $INDEX_ROOT into $INDEX_ALLOCATION and copies all non-last resident root entries into a newly allocated INDEX_BUFFER via hdr_insert_head(). The source byte count 'to_move' is summed from the on-disk resident entry sizes and is independent of the destination buffer size, which comes from root->index_block_size (via indx->index_bits).
A crafted NTFS image that keeps a valid, full resident root but shrinks root->index_block_size down to 512 after the root has been populated makes hdr_insert_head() memcpy attacker-controlled resident entry bytes past the end of the kmalloc(1u << indx->index_bits) allocation returned by indx_new(). For a 512-byte destination and a resident root whose non-last entries total 560 bytes, the memcpy overruns by 120 bytes and a following memmove extends the highest written offset to 136 bytes past the allocation. The overflow bytes are a direct copy of on-disk entries (via kmemdup), so they are fully attacker-controlled.
The write is reachable from unprivileged open(O_CREAT) on a mounted crafted NTFS image: a single sufficiently long create in a directory whose resident root is already full forces root promotion and triggers the copy.
This is a controlled out-of-bounds write of 120-136 bytes past a kmalloc(index_block_size) allocation, with attacker-controlled content. It is a bounded adjacent-heap corruption primitive; it is not an arbitrary-address write. Successful exploitation into a named victim object depends on the surrounding slab layout.
Reject the copy at the sink. The destination's INDEX_HDR already reports hdr_total (the payload capacity of the new buffer) and hdr_used (the bytes already consumed by the terminal END entry installed by indx_new()); require that to_move fits in the remaining payload before calling hdr_insert_head(). On mismatch, fail with -EINVAL and mark the filesystem as having a detected on-disk inconsistency, which is the same behaviour as the surrounding validation in this function.
--- fs/ntfs3/index.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
fs/ntfs3/index.c +16 -0
@@ -1745,6 +1745,22 @@ static int indx_insert_into_root(struct hdr_used =le32_to_cpu (hdr->used); hdr_total =le32_to_cpu (hdr->total); +/* +* The destination INDEX_BUFFER has 'hdr_total' bytes of payload +* available after the header, of which 'hdr_used' are already +* consumed by the single terminal END entry installed by +* indx_new(). A crafted image can present a resident root whose +* non-last entries (summing to 'to_move') exceed what fits in +* this buffer; copying them unchecked would overrun the +* kmalloc(1u << indx->index_bits) allocation backing the new +* buffer. Reject the copy in that case. +*/ +if (to_move > hdr_total - hdr_used) { + err = -EINVAL ; +ntfs_set_state (sbi,NTFS_DIRTY_ERROR ); + goto out_put_n; + } +/* Copy root entries into new buffer. */ hdr_insert_head (hdr, re, to_move);
Greg Kroah-Hartman
maintainerPATCH 985/1266 ntfs3: validate split-point offset in indx_insert_into_buffer
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit f1df9d771df47aa40de6d70949c28720ae1e430d upstream.
indx_insert_into_buffer() computes
used = used1 - to_copy - sp_size;
memmove(de_t, Add2Ptr(sp, sp_size), used - le32_to_cpu(hdr1->de_off));where sp and sp_size come from hdr_find_split(). hdr_find_split() walks entries by le16_to_cpu(e->size) without validating that each step stays within hdr->used or that the size field is at least sizeof(struct NTFS_DE). index_hdr_check(), the on-load gatekeeper, only validates header-level fields (used, total, de_off) and does not walk per-entry sizes.
A crafted NTFS image whose leaf INDEX_HDR reports used == total but contains one interior NTFS_DE with size = 0xFFF0 therefore passes validation, descends to indx_insert_into_buffer() through the ntfs_create() -> indx_insert_entry() path, and makes hdr_find_split() return an sp whose sp_size (0xFFF0) greatly exceeds the remaining bytes in the buffer. The u32 subtraction underflows and the memmove count becomes a near-4-GiB value, producing an out-of-bounds kernel write that corrupts adjacent allocations and panics the kernel.
Reproduced on 7.0.0-rc7 with UML + KASAN via a crafted image and a single 'touch' inside the mounted directory; crash site resolves to fs/ntfs3/index.c at the memmove. Trigger requires only local mount of an attacker-supplied filesystem image (USB, loopback, or removable media auto-mount).
Reject the split whenever the chosen sp plus its declared size already extends past hdr1->used. This is the minimal fix; it preserves the existing hdr_find_split() contract and relies on the same out: cleanup path as the pre-existing error returns.
A prior OOB read in the very same indx_insert_into_buffer() memmove was fixed in commit b8c44949044e ("fs/ntfs3: Fix OOB read in indx_insert_into_buffer") by tightening hdr_find_e(), but that fix does not cover the split-point size field path addressed here: sp is returned by hdr_find_split(), not hdr_find_e(), and the underflow is driven by sp->size rather than hdr->used exceeding hdr->total.
--- fs/ntfs3/index.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
fs/ntfs3/index.c +14 -0
@@ -1865,6 +1865,20 @@ indx_insert_into_buffer(struct ntfs_indememcpy (up_e, sp, sp_size); used1 =le32_to_cpu (hdr1->used); + +/* +* hdr_find_split does not validate per-entry sizes, so a crafted +* NTFS_DE whose le16 size field is out of range can place sp such +* that (PtrOffset(hdr1, sp) + sp_size) exceeds used1. Without this +* guard the u32 'used = used1 - to_copy - sp_size' underflows and +* the subsequent memmove count becomes a near-4-GiB value, +* triggering an out-of-bounds kernel write. +*/ +if (PtrOffset (hdr1, sp) + sp_size > used1) { + err = -EINVAL ; + goto out; + } + hdr1_saved =kmemdup (hdr1, used1,GFP_NOFS );if (!hdr1_saved) { err = -ENOMEM ;
Greg Kroah-Hartman
maintainerPATCH 986/1266 ntfs3: fix out-of-bounds read in decompress_lznt
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tristan Madani <[email protected]>
commit 7160a57192fb16d7a6fa9b7f5c7ac341d2444a89 upstream.
decompress_lznt() does not validate array index bounds before accessing the decompression table. A corrupted NTFS3 image with invalid compressed data can trigger an out-of-bounds read.
Add index bounds checking to prevent the OOB access.
--- fs/ntfs3/lznt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/ntfs3/lznt.c +1 -1
@@ -240,7 +240,7 @@ static inline ssize_t decompress_chunk(uif (up - unc >LZNT_CHUNK_SIZE )return -EINVAL ;/* Correct index */ -while (unc + s_max_off[index] < up) +while (index <ARRAY_SIZE (s_max_off) -1 && unc + s_max_off[index] < up) index +=1 ;/* Check the current flag for zero. */
Greg Kroah-Hartman
maintainerPATCH 987/1266 power: supply: charger-manager: fix refcount leak in is_full_charged()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: WenTao Liang <[email protected]>
commit 4373cfa38ead58f980362c841b0d0bdf8c4d956c upstream.
In is_full_charged(), power_supply_get_by_name() is called to obtain a reference to the fuel_gauge power supply. If the voltage check (uV >= desc->fullbatt_uV) succeeds, the function returns true directly without releasing the reference, leaking the refcount.
Fix this by setting a flag and jumping to the out label where power_supply_put() properly drops the reference.
--- drivers/power/supply/charger-manager.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/power/supply/charger-manager.c +4 -2
@@ -302,8 +302,10 @@ static bool is_full_charged(struct chargif (cm->battery_status ==POWER_SUPPLY_STATUS_FULL && desc->fullbatt_vchkdrop_uV) uV += desc->fullbatt_vchkdrop_uV; -if (uV >= desc->fullbatt_uV) -return true; +if (uV >= desc->fullbatt_uV) { + is_full = true; + goto out; + } } }
Greg Kroah-Hartman
maintainerPATCH 988/1266 mips: sched: Fix CPUMASK_OFFSTACK memory corruption
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aaron Tomlin <[email protected]>
commit 98e37db4a34d3af3fb2f4648295c25b5e40b20e3 upstream.
This patch addresses a critical memory management flaw. When CONFIG_CPUMASK_OFFSTACK is enabled, cpumask_var_t is a pointer. Consequently, sizeof(new_mask) evaluates to the pointer size, causing copy_from_user() to clobber the mask pointer. Furthermore, the old logic performed copy_from_user() before allocating the mask.
Fix this by allocating new_mask first. To handle variable-sized user masks correctly, use cpumask_size() to truncate overly large user masks or pad undersized masks with zeros before copying the data directly into the allocated buffer.
--- arch/mips/kernel/mips-mt-fpaff.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-)
arch/mips/kernel/mips-mt-fpaff.c +15 -13
@@ -70,11 +70,16 @@ asmlinkage long mipsmt_sys_sched_setaffistruct task_struct *p;int retval; -if (len <sizeof (new_mask)) -return -EINVAL ; - -if (copy_from_user (&new_mask, user_mask_ptr,sizeof (new_mask))) -return -EFAULT ; +if (!alloc_cpumask_var (&new_mask,GFP_KERNEL )) +return -ENOMEM ; +if (len <cpumask_size ()) +cpumask_clear (new_mask); +else if (len >cpumask_size ()) + len =cpumask_size (); +if (copy_from_user (new_mask, user_mask_ptr, len)) { + retval = -EFAULT ; + goto out_free_new_mask; + }cpus_read_lock ();rcu_read_lock ();
@@ -83,7 +88,8 @@ asmlinkage long mipsmt_sys_sched_setaffiif (!p) {rcu_read_unlock ();cpus_read_unlock (); -return -ESRCH ; + retval = -ESRCH ; + goto out_free_new_mask; }/* Prevent p going away */
@@ -94,13 +100,9 @@ asmlinkage long mipsmt_sys_sched_setaffi retval = -ENOMEM ; goto out_put_task; } -if (!alloc_cpumask_var (&new_mask,GFP_KERNEL )) { - retval = -ENOMEM ; - goto out_free_cpus_allowed; - }if (!alloc_cpumask_var (&effective_mask,GFP_KERNEL )) { retval = -ENOMEM ; - goto out_free_new_mask; + goto out_free_cpus_allowed; }if (!check_same_owner (p) && !capable (CAP_SYS_NICE )) { retval = -EPERM ;
@@ -141,13 +143,13 @@ asmlinkage long mipsmt_sys_sched_setaffi } out_unlock:free_cpumask_var (effective_mask); -out_free_new_mask: -free_cpumask_var (new_mask); out_free_cpus_allowed:free_cpumask_var (cpus_allowed); out_put_task:put_task_struct (p);cpus_read_unlock (); +out_free_new_mask: +free_cpumask_var (new_mask);return retval; }
Greg Kroah-Hartman
maintainerPATCH 989/1266 riscv: cacheinfo: Fix node reference leak in populate_cache_leaves
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zishun Yi <[email protected]>
commit bf4a195f063b0a0805c1417f6aad1dd32ea48f0f upstream.
Currently, the while loop drops the reference to prev in each iteration. If the loop terminates early due to a break, the final of_node_put(np) correctly drops the reference to the current node.
However, if the loop terminates naturally because np == NULL, calling of_node_put(np) is a no-op. This leaves the last valid node stored in prev without its reference dropped, resulting in a node reference leak.
Fix this by changing the final of_node_put(np) to of_node_put(prev).
--- arch/riscv/kernel/cacheinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/riscv/kernel/cacheinfo.c +1 -1
@@ -128,7 +128,7 @@ int populate_cache_leaves(unsigned int cci_leaf_init (this_leaf++,CACHE_TYPE_DATA , level); levels = level; } -of_node_put (np); +of_node_put (prev);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 990/1266 mm/damon/sysfs-schemes: fix dir put orders in access_pattern_add_dirs()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: SeongJae Park <[email protected]>
commit d58fdbe37a829fd2e5803dd4e5a72992dd8c5368 upstream.
Patch series "mm/damon/sysfs-schemes: fix wrong directories put orders in error paths".
Error paths of damon_sysfs_access_pattern_add_dirs() and damon_sysfs_scheme_add_dirs() functions put references to directories in wrong orders. As a result, uninitialized memory dereference and/or memory leak can happen. Fix those.
This patch (of 2):
In access_pattern_add_dirs(), error handling path puts references starting from setup failed directories. If the failure happpened from the initial allication in the setup functions, uninitialized memory dereference happen. The allocation failures will not commonly happen, but the consequence is quite bad. Fix the wrong reference put orders.
The issue was discovered [1] by Sashiko.
--- mm/damon/sysfs-schemes.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)
mm/damon/sysfs-schemes.c +3 -6
@@ -1077,22 +1077,19 @@ static int damon_sysfs_access_pattern_ad err =damon_sysfs_access_pattern_add_range_dir (access_pattern, &access_pattern->sz,"sz" );if (err) - goto put_sz_out; +return err; err =damon_sysfs_access_pattern_add_range_dir (access_pattern, &access_pattern->nr_accesses,"nr_accesses" );if (err) - goto put_nr_accesses_sz_out; + goto put_sz_out; err =damon_sysfs_access_pattern_add_range_dir (access_pattern, &access_pattern->age,"age" );if (err) - goto put_age_nr_accesses_sz_out; + goto put_nr_accesses_sz_out;return 0 ; -put_age_nr_accesses_sz_out: -kobject_put (&access_pattern->age->kobj); - access_pattern->age =NULL ; put_nr_accesses_sz_out:kobject_put (&access_pattern->nr_accesses->kobj); access_pattern->nr_accesses =NULL ;
Greg Kroah-Hartman
maintainerPATCH 991/1266 mm/damon/sysfs-schemes: put stats for scheme_add_dirs() internal error
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: SeongJae Park <[email protected]>
commit 05ea83ee88ca70f8932906d9f2617ff996f45b50 upstream.
damon_sysfs_scheme_add_dirs() setup the tried_regions directory after the stats directory setup is completed. When the tried_regions directory setup is failed, the setup function ensures the reference for the tried regions directory is released. Hence the error path should put references on setup succeeded directory objects, starting from the stats directory. However, the error path is putting the tried_regions directory instead of the stats directory.
As a direct result, the stats directory object is leaked. Worse yet, if the tried_regions directory setup failed from the initial allocation, the scheme->tried_regions field remains uninitialized. The following kobject_put(&scheme->tried_regions->kobj) call in the error path will dereference the uninitialized memory. The setup failures should not be common. But once it happens, the consequence is quite bad.
Fix this issue by correctly putting the stats directory instead of the tried_regions directory.
The issue was discovered [1] by Sashiko.
--- mm/damon/sysfs-schemes.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
mm/damon/sysfs-schemes.c +4 -4
@@ -1303,12 +1303,12 @@ static int damon_sysfs_scheme_add_dirs(s goto put_filters_watermarks_quotas_access_pattern_out; err =damon_sysfs_scheme_set_tried_regions (scheme);if (err) - goto put_tried_regions_out; + goto put_stats_out;return 0 ; -put_tried_regions_out: -kobject_put (&scheme->tried_regions->kobj); - scheme->tried_regions =NULL ; +put_stats_out: +kobject_put (&scheme->stats->kobj); + scheme->stats =NULL ; put_filters_watermarks_quotas_access_pattern_out:kobject_put (&scheme->filters->kobj); scheme->filters =NULL ;
Greg Kroah-Hartman
maintainerPATCH 992/1266 proc: only bump parent nlink when registering directories
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Wilczyński <[email protected]>
commit 16b02eb4b9b272c221255c20d34ccd5db53a3ed3 upstream.
proc_register() increments the parent directory's link count for every entry it registers, while remove_proc_entry() and remove_proc_subtree() decrement it only when the removed entry is a directory. Regular files thus inflate the parent's count while they exist, and leak one link permanently on every create and remove cycle.
For example, /proc/bus/pci/00 with twenty-two device files and no subdirectories reports nlink 24 instead of 2, and SR-IOV VF enable and disable cycles, each creating and removing the VF config space entries under /proc/bus/pci/<bus>, inflate the link count of that directory without bound.
Before commit e06689bf5701 ("proc: change ->nlink under proc_subdir_lock"), the increment lived in proc_mkdir_data() and proc_create_mount_point(), and was therefore applied only to directories. Moving it into proc_register() to bring it under proc_subdir_lock dropped the S_ISDIR check.
Thus, move the nlink accounting into pde_subdir_insert() and pde_erase(), only updating it for directories in both, so the link count is always changed together with the directory entry itself.
--- fs/proc/generic.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
fs/proc/generic.c +4 -5
@@ -112,6 +112,8 @@ static bool pde_subdir_insert(struct pro/* Add new node and rebalance tree. */ rb_link_node (&de->subdir_node, parent, new);rb_insert_color (&de->subdir_node, root); +if (S_ISDIR (de->mode)) + dir->nlink++;return true; }
@@ -399,7 +401,6 @@ struct proc_dir_entry *proc_register(strwrite_unlock (&proc_subdir_lock); goto out_free_inum; } - dir->nlink++;write_unlock (&proc_subdir_lock);return dp;
@@ -698,6 +699,8 @@ static void pde_erase(struct proc_dir_en {rb_erase (&pde->subdir_node, &parent->subdir);RB_CLEAR_NODE (&pde->subdir_node); +if (S_ISDIR (pde->mode)) + parent->nlink--; }/*
@@ -723,8 +726,6 @@ void remove_proc_entry(const char *name, de =NULL ; }else {pde_erase (de, parent); -if (S_ISDIR (de->mode)) - parent->nlink--; } }write_unlock (&proc_subdir_lock);
@@ -783,8 +784,6 @@ int remove_proc_subtree(const char *namecontinue ; } next = de->parent; -if (S_ISDIR (de->mode)) - next->nlink--;write_unlock (&proc_subdir_lock);proc_entry_rundown (de);
Greg Kroah-Hartman
maintainerPATCH 993/1266 mm/mm_init: fix uninitialized struct pages for ZONE_DEVICE
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muchun Song <[email protected]>
commit cd681403a87085562499d60325b7b45d3be11217 upstream.
If DAX memory is hotplugged into an unoccupied subsection of an early section, section_activate() reuses the unoptimized boot memmap. However, compound_nr_pages() still assumes that vmemmap optimization is in effect and initializes only the reduced number of struct pages. As a result, the remaining tail struct pages are left uninitialized, which can later lead to unexpected behavior or crashes.
Fix this by treating early sections as unoptimized when calculating how many struct pages to initialize.
--- mm/mm_init.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
mm/mm_init.c +10 -3
@@ -1024,10 +1024,17 @@ static void __ref __init_zone_device_pag* of how the sparse_vmemmap internals handle compound pages in the lack * of an altmap. See vmemmap_populate_compound_pages(). */ -static inline unsigned long compound_nr_pages (struct vmem_altmap *altmap, +static inline unsigned long compound_nr_pages (unsigned long pfn, +struct vmem_altmap *altmap,struct dev_pagemap *pgmap) { -if (!vmemmap_can_optimize (altmap, pgmap)) +/* +* If DAX memory is hot-plugged into an unoccupied subsection +* of an early section, the unoptimized boot memmap is reused. +* See section_activate(). +*/ +if (early_section (__pfn_to_section (pfn)) || + !vmemmap_can_optimize (altmap, pgmap))return pgmap_vmemmap_nr (pgmap);return VMEMMAP_RESERVE_NR * (PAGE_SIZE /sizeof (struct page ));
@@ -1095,7 +1102,7 @@ void __ref memmap_init_zone_device(struccontinue ;memmap_init_compound (page, pfn, zone_idx, nid, pgmap, -compound_nr_pages (altmap, pgmap)); +compound_nr_pages (pfn, altmap, pgmap)); } pr_debug("%s initialised %lu pages in %ums\n", __func__,
Greg Kroah-Hartman
maintainerPATCH 994/1266 mtd: slram: remove failed entries from the device list
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ruoyu Wang <[email protected]>
commit 36f1648644d769c496a8e47e53603e863e358d73 upstream.
register_device() links a new slram_mtdlist entry before allocating all of the state needed by the entry. If a later allocation, memremap(), or mtd_device_register() fails, the partially initialized entry remains on the global list. A later cleanup can then dereference or free invalid state from that failed entry.
Unwind the partially initialized entry and clear the list tail on each failure path after the entry has been linked.
--- drivers/mtd/devices/slram.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-)
drivers/mtd/devices/slram.c +16 -6
@@ -129,6 +129,7 @@ static int slram_write(struct mtd_info *static int register_device (char *name,unsigned long start,unsigned long length) {slram_mtd_list_t **curmtd; +int ret = -ENOMEM ; curmtd = &slram_mtdlist;while (*curmtd) {
@@ -155,14 +156,15 @@ static int register_device(char *name, uif (!(*curmtd)->mtdinfo) {E ("slram: Cannot allocate new MTD device.\n" ); -return (-ENOMEM ); + goto err_free_list; }if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start =memremap (start, length,MEMREMAP_WB |MEMREMAP_WT |MEMREMAP_WC ))) {E ("slram: memremap failed\n" ); -return -EIO ; + ret = -EIO ; + goto err_free_priv; } ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end = ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start + length;
@@ -183,10 +185,8 @@ static int register_device(char *name, uif (mtd_device_register ((*curmtd)->mtdinfo,NULL ,0 )) {E ("slram: Failed to register new device\n" ); -memunmap (((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); -kfree ((*curmtd)->mtdinfo->priv); -kfree ((*curmtd)->mtdinfo); -return (-EAGAIN ); + ret = -EAGAIN ; + goto err_unmap; }T ("slram: Registered device %s from %luKiB to %luKiB\n" , name, (start /1024 ), ((start + length) /1024 ));
@@ -194,6 +194,16 @@ static int register_device(char *name, u ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start, ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end);return (0 ); + +err_unmap: +memunmap (((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); +err_free_priv: +kfree ((*curmtd)->mtdinfo->priv); +err_free_list: +kfree ((*curmtd)->mtdinfo); +kfree (*curmtd); + *curmtd =NULL ; +return ret; }static void unregister_devices (void )
Greg Kroah-Hartman
maintainerPATCH 995/1266 9p: skip nlink update in cacheless mode to fix WARN_ON
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
commit 574aa0b4799470ac814479f1138d19efe6262255 upstream.
v9fs_dec_count() unconditionally calls drop_nlink() on regular files, even when the inode's nlink is already zero. In cacheless mode the client refetches inode metadata from the server (the source of truth) on every operation, so by the time v9fs_remove() returns, the locally cached nlink may already reflect the post-unlink value:
1. Client initiates unlink, server processes it and sets nlink to 0
2. Client refetches inode metadata (nlink=0) before unlink returns
3. Client's v9fs_remove() completes successfully
4. Client calls v9fs_dec_count() which calls drop_nlink() on nlink=0
This race is easily triggered under heavy unlink workloads, such as stress-ng's unlink stressor, producing the following warning:
WARNING: fs/inode.c:417 at drop_nlink+0x4c/0xc8 Call trace: drop_nlink+0x4c/0xc8 v9fs_remove+0x1e0/0x250 [9p] v9fs_vfs_unlink+0x20/0x38 [9p] vfs_unlink+0x13c/0x258 ...
In cacheless mode the server is authoritative and the inode is on its way out, so locally adjusting nlink buys nothing. Skip v9fs_dec_count() entirely when neither CACHE_META nor CACHE_LOOSE is set, which both avoids the warning and removes a class of nlink races (two concurrent unlinkers observing nlink > 0 and both calling drop_nlink()) that an nlink == 0 guard alone would only narrow rather than close.
Message-ID: <[email protected]>
--- fs/9p/vfs_inode.c | 9 +++++++++ 1 file changed, 9 insertions(+)
fs/9p/vfs_inode.c +9 -0
@@ -521,10 +521,19 @@ static int v9fs_at_to_dotl_flags(int fla* - ext4 (with dir_nlink feature enabled) sets nlink to 1 if a dir has more * than EXT4_LINK_MAX (65000) links. * +* In cacheless mode the server is the source of truth for nlink and the +* inode is going away immediately, so locally adjusting i_nlink buys +* nothing and races with concurrent metadata fetches that may already +* have observed the post-unlink value (nlink == 0). +* * @inode: inode whose nlink is being dropped */ static void v9fs_dec_count (struct inode *inode) { +struct v9fs_session_info *v9ses =v9fs_inode2v9ses (inode); + +if (!(v9ses->cache & (CACHE_META |CACHE_LOOSE ))) +return ;if (!S_ISDIR (inode->i_mode) || inode->i_nlink >2 )drop_nlink (inode); }
Greg Kroah-Hartman
maintainerPATCH 996/1266 scsi: smartpqi: Use shost_to_hba() in pqi_scan_finished()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Martin Wilck <[email protected]>
commit 57db1307afb1f83d45f5ff53b93f8d040100d13e upstream.
shost_to_hba() is used everywhere except to obtain pqi_ctrl_info from shosti, except in pqi_scan_finished(), where shost_priv() is used. This causes one pointer dereference to be missed, as shost->hostdata is a pointer in smartpqi. Fix it.
--- drivers/scsi/smartpqi/smartpqi_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/smartpqi/smartpqi_init.c +1 -1
@@ -2613,7 +2613,7 @@ static int pqi_scan_finished(struct Scsi {struct pqi_ctrl_info *ctrl_info; - ctrl_info =shost_priv (shost); + ctrl_info =shost_to_hba (shost);return !mutex_is_locked (&ctrl_info->scan_mutex); }
Greg Kroah-Hartman
maintainerPATCH 997/1266 scsi: sas: Skip opt_sectors when DMA reports no real optimization hint
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ionut Nechita <[email protected]>
commit be8fcd4a8217a916344c88a4b1b84f5736dda17e upstream.
sas_host_setup() unconditionally sets shost->opt_sectors from dma_opt_mapping_size().
When the IOMMU is disabled or in passthrough mode and no DMA ops provide an opt_mapping_size callback, dma_opt_mapping_size() returns min(dma_max_mapping_size(), SIZE_MAX) which equals dma_max_mapping_size() — a hard upper bound, not an optimization hint.
On a Dell PowerEdge R750 with mpt3sas (Broadcom SAS3816, FW 33.15.00.00) and intel_iommu=off the following values are observed:
dma_opt_mapping_size() = dma_max_mapping_size() (no real hint)
shost->max_sectors = 32767
opt_sectors = min(32767, huge >> 9) = 32767
optimal_io_size = 32767 << 9 = 16776704
→ round_down(16776704, 4096) = 16773120The SAS disk (SAMSUNG MZILT800HBHQ0D3) does not report an Optimal Transfer Length in VPD page B0, so sdkp->opt_xfer_blocks remains 0.
sd_revalidate_disk() then uses min_not_zero(0, opt_sectors) = opt_sectors, propagating the bogus value into the block device's optimal_io_size (visible as OPT-IO = 16773120 in lsblk --topology).
mkfs.xfs picks up optimal_io_size and minimum_io_size and computes:
swidth = 16773120 / 4096 = 4095
sunit = 8192 / 4096 = 2
Since 4095 % 2 != 0, XFS rejects the geometry:
SB stripe unit sanity check failed
This makes it impossible to create XFS filesystems (e.g. for /var/lib/docker) during system bootstrap.
Fix this by introducing a sas_dma_setup_opt_sectors() helper that sets opt_sectors only when dma_opt_mapping_size() is strictly less than dma_max_mapping_size(), indicating a genuine DMA optimization constraint.
The helper computes min(opt_sectors, max_sectors) first, then rounds down to a power of two so that filesystem geometry calculations always produce clean results.
When the two DMA values are equal, no backend provided a real hint, so opt_sectors stays at 0 ("no preference").
[mkp: implemented hch's suggestion]
--- drivers/scsi/scsi_transport_sas.c | 41 +++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-)
drivers/scsi/scsi_transport_sas.c +36 -5
@@ -27,6 +27,7 @@#include <linux/module.h> #include <linux/jiffies.h> #include <linux/err.h> +#include <linux/log2.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/blkdev.h>
@@ -220,12 +221,45 @@ static int sas_bsg_initialize(struct Scs* SAS host attributes */ +/* +* Set shost->opt_sectors from the DMA optimal mapping size, but only +* when dma_opt_mapping_size() is strictly less than dma_max_mapping_size(), +* indicating a genuine optimization hint from an IOMMU or DMA backend. +* When the two are equal (e.g. IOMMU disabled / passthrough), no real +* hint exists, so leave opt_sectors at 0 to avoid bogus optimal_io_size +* values that break filesystem geometry (e.g. mkfs.xfs stripe alignment). +*/ +static void sas_dma_setup_opt_sectors (struct Scsi_Host *shost) +{ +struct device *dma_dev = shost->dma_dev; +size_t opt =dma_opt_mapping_size (dma_dev); +size_t max =dma_max_mapping_size (dma_dev); +unsigned int opt_sectors; + +/* opt >= max means no real hint was provided by the DMA layer */ +if (opt >= max) +return ; + +/* Clamp to max_sectors to avoid overflow in sector arithmetic */ + opt_sectors = min_t(unsigned int, opt >> SECTOR_SHIFT, + shost->max_sectors); + +/* Guard against zero before rounddown_pow_of_two() */ +if (!opt_sectors) +return ; + +/* +* Round down to power-of-two so filesystem geometry calculations +* (e.g. XFS stripe width/unit) always produce clean divisors. +*/ + shost->opt_sectors =rounddown_pow_of_two (opt_sectors); +} +static int sas_host_setup (struct transport_container *tc,struct device *dev,struct device *cdev) {struct Scsi_Host *shost =dev_to_shost (dev);struct sas_host_attrs *sas_host =to_sas_host_attrs (shost); -struct device *dma_dev = shost->dma_dev;INIT_LIST_HEAD (&sas_host->rphy_list);mutex_init (&sas_host->lock);
@@ -237,10 +271,7 @@ static int sas_host_setup(struct transpodev_printk (KERN_ERR , dev,"fail to a bsg device %d\n" , shost->host_no); -if (dma_dev->dma_mask) { - shost->opt_sectors = min_t(unsigned int, shost->max_sectors, -dma_opt_mapping_size (dma_dev) >>SECTOR_SHIFT ); - } +sas_dma_setup_opt_sectors (shost);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 998/1266 ocfs2: use kzalloc for quota recovery bitmap allocation
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tristan Madani <[email protected]>
commit 93c8c6ea90be9e9df8fe14048ad4e3caad0770a6 upstream.
ocfs2 quota recovery allocates a bitmap buffer with kmalloc and does not fully initialize it. This can lead to use of uninitialized bits during quota recovery from a corrupted filesystem image.
Use kzalloc instead to ensure the bitmap is zero-initialized.
--- fs/ocfs2/quota_local.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/ocfs2/quota_local.c +1 -1
@@ -302,7 +302,7 @@ static int ocfs2_add_recovery_chunk(struif (!rc)return -ENOMEM ; rc->rc_chunk = chunk; - rc->rc_bitmap =kmalloc (sb->s_blocksize,GFP_NOFS ); + rc->rc_bitmap =kzalloc (sb->s_blocksize,GFP_NOFS );if (!rc->rc_bitmap) {kfree (rc);return -ENOMEM ;
Greg Kroah-Hartman
maintainerPATCH 999/1266 mtd: rawnand: pl353: fix probe resource allocation
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bastien Curutchet <[email protected]>
commit 19ed11aee966d91beebdef9d32ce926474872f79 upstream.
During probe(), the devm_ioremap() is called with the parent device instead of the current one. So when the module is unloaded, the register area isn't released.
Target the pl35x device in the devm_ioremap() instead of its parent.
--- drivers/mtd/nand/raw/pl35x-nand-controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/mtd/nand/raw/pl35x-nand-controller.c +1 -1
@@ -1153,7 +1153,7 @@ static int pl35x_nand_probe(struct platf nfc->controller.ops = &pl35x_nandc_ops;INIT_LIST_HEAD (&nfc->chips); - nfc->conf_regs =devm_ioremap_resource (&smc_amba->dev, &smc_amba->res); + nfc->conf_regs =devm_ioremap_resource (nfc->dev, &smc_amba->res);if (IS_ERR (nfc->conf_regs))return PTR_ERR (nfc->conf_regs);
Greg Kroah-Hartman
maintainerPATCH 1000/1266 net/9p: fix infinite loop in p9_client_rpc on fatal signal
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasiliy Kovalev <[email protected]>
commit 6b4f48728faa8bb514368f7eacda05565dea8696 upstream.
When p9_client_rpc() is called with type P9_TFLUSH and the transport has no peer (e.g. fd transport backed by pipes with no 9p server), a fatal signal causes an infinite loop:
again:
err = io_wait_event_killable(req->wq, ...)
/* SIGKILL wakes the task, returns -ERESTARTSYS */
if (err == -ERESTARTSYS && c->status == Connected &&
type == P9_TFLUSH) {
sigpending = 1;
clear_thread_flag(TIF_SIGPENDING);
goto again;
}clear_thread_flag() clears TIF_SIGPENDING before jumping back to io_wait_event_killable(). signal_pending_state() checks TIF_SIGPENDING, finds it zero, and the task goes to sleep again. The task can only wake on the next signal delivery that calls signal_wake_up() and sets TIF_SIGPENDING again. When that happens the loop repeats, clears TIF_SIGPENDING, and sleeps again indefinitely.
This is triggered in practice by coredump_wait(): when a thread in a multi-threaded process causes a coredump (e.g. via SIGSYS from Syscall User Dispatch), coredump_wait() sends SIGKILL to all other threads and waits for them to call mm_release(). If one of those threads is blocked in p9_client_rpc() over an fd transport with no peer, it enters the P9_TFLUSH loop and never calls mm_release(), so coredump_wait() stalls forever:
INFO: task syz.0.18:676 blocked for more than 143 seconds.
Not tainted 6.12.77+ #1
task:syz.0.18 state:D stack:27600 pid:676 tgid:673 ppid:630 flags:0x00000004
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5344 [inline]
__schedule+0xcb4/0x5d50 kernel/sched/core.c:6724
__schedule_loop kernel/sched/core.c:6801 [inline]
schedule+0xe5/0x350 kernel/sched/core.c:6816
schedule_timeout+0x253/0x290 kernel/time/timer.c:2593
do_wait_for_common kernel/sched/completion.c:95 [inline]
__wait_for_common+0x409/0x600 kernel/sched/completion.c:116
wait_for_common kernel/sched/completion.c:127 [inline]
wait_for_completion_state+0x1d/0x40 kernel/sched/completion.c:264
coredump_wait fs/coredump.c:448 [inline]
do_coredump+0x854/0x4350 fs/coredump.c:629
get_signal+0x1425/0x2730 kernel/signal.c:2903
arch_do_signal_or_restart+0x81/0x880 arch/x86/kernel/signal.c:337
exit_to_user_mode_loop kernel/entry/common.c:111 [inline]
exit_to_user_mode_prepare include/linux/entry-common.h:328 [inline]
__syscall_exit_to_user_mode_work kernel/entry/common.c:207 [inline]
syscall_exit_to_user_mode+0xf9/0x160 kernel/entry/common.c:218
do_syscall_64+0x102/0x220 arch/x86/entry/common.c:84
entry_SYSCALL_64_after_hwframe+0x77/0x7f
</TASK>Fix: check fatal_signal_pending() before clearing TIF_SIGPENDING in the P9_TFLUSH retry loop. At that point TIF_SIGPENDING is still set, so fatal_signal_pending() works correctly. If a fatal signal is pending, jump to recalc_sigpending to restore TIF_SIGPENDING and return -ERESTARTSYS to the caller.
The same defect is present in stable kernels back to 5.4. On those kernels the infinite loop is broken earlier by a second SIGKILL from the parent process (e.g. kill_and_wait() retrying after a timeout), resulting in a zombie process and a shutdown delay rather than a permanent D-state hang, but the underlying flaw is the same.
Found by Linux Verification Center (linuxtesting.org) with Syzkaller.
Message-ID: <[email protected]>
--- net/9p/client.c | 2 ++ 1 file changed, 2 insertions(+)
net/9p/client.c +2 -0
@@ -722,6 +722,8 @@ again:if (err == -ERESTARTSYS && c->status == Connected && type ==P9_TFLUSH ) { +if (fatal_signal_pending (current)) + goto recalc_sigpending; sigpending =1 ;clear_thread_flag (TIF_SIGPENDING ); goto again;
Greg Kroah-Hartman
maintainerPATCH 1001/1266 mtd: rawnand: fix condition in nand_select_target()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arseniy Krasnov <[email protected]>
commit 8507c2cc9e4fa402401819f44d1e8a5ef4d11d8b upstream.
'cs' here must be in range [0:nanddev_ntargets[.
--- drivers/mtd/nand/raw/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/mtd/nand/raw/nand_base.c +1 -1
@@ -175,7 +175,7 @@ void nand_select_target(struct nand_chip* cs should always lie between 0 and nanddev_ntargets(), when that's * not the case it's a bug and the caller should be fixed. */ -if (WARN_ON (cs >nanddev_ntargets (&chip->base))) +if (WARN_ON (cs >=nanddev_ntargets (&chip->base)))return ; chip->cur_cs = cs;
Greg Kroah-Hartman
maintainerPATCH 1002/1266 ocfs2: avoid moving extents to occupied clusters
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
commit 22920541c35a9f23f219038ba5874c843a7c4419 upstream.
For non-auto OCFS2_IOC_MOVE_EXT operations, userspace supplies a physical me_goal. ocfs2_move_extent() initializes new_phys_cpos from that goal and expects ocfs2_probe_alloc_group() to replace it with a free run in the target block group.
The probe currently leaves *phys_cpos unchanged if the scan reaches the end of the group without finding a free run. An occupied goal at the last bit can therefore survive the probe and be passed to __ocfs2_move_extent(), which copies file data into a cluster still owned by another inode before the bitmap is updated.
When the probe does find a free run, it also subtracts move_len from the ending bit. The start of an N-bit run ending at i is i - N + 1, so the current calculation can report the bit immediately before the free run.
Clear *phys_cpos before scanning and use the correct free-run start. Callers already treat a zero result as -ENOSPC, so failed probes no longer continue with an occupied caller-controlled goal.
--- fs/ocfs2/move_extents.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/ocfs2/move_extents.c +3 -1
@@ -534,6 +534,8 @@ static void ocfs2_probe_alloc_group(struu32 base_cpos =ocfs2_blocks_to_clusters (inode->i_sb,le64_to_cpu (gd->bg_blkno)); + *phys_cpos =0 ; +for (i = base_bit; i <le16_to_cpu (gd->bg_bits); i++) { used =ocfs2_test_bit (i, (unsigned long *)gd->bg_bitmap);
@@ -555,7 +557,7 @@ static void ocfs2_probe_alloc_group(stru last_free_bits++;if (last_free_bits == move_len) { - i -= move_len; + i = i - move_len +1 ; *goal_bit = i; *phys_cpos = base_cpos + i;break ;
Greg Kroah-Hartman
maintainerPATCH 1003/1266 ocfs2: fix NULL h_transaction deref in ocfs2_assure_trans_credits
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ian Bridges <[email protected]>
commit f9ab30c96b0f00c20c6dac93681bdae3a033d229 upstream.
[BUG]
A direct write over unwritten extents can panic the kernel in ocfs2_assure_trans_credits() when the journal aborts during DIO completion. The crash is a general protection fault from a NULL pointer dereference.
[CAUSE]ocfs2_dio_end_io_write() loops over a direct write's unwritten extents, marking each written under a single journal handle. If the journal aborts (for example after an I/O error) while the extent tree is being updated, the handle is left aborted with its transaction pointer cleared. The extent merge treats that failure as not critical and reports success, so the loop keeps using the handle. ocfs2_assure_trans_credits() reads the handle's remaining credits without first checking whether the handle is aborted, and that read dereferences the cleared transaction pointer.
[FIX]
A journal abort is recorded in the handle itself, so callers are expected to test the handle rather than rely on a returned error. Make ocfs2_assure_trans_credits() do that, as the other ocfs2 journal helpers already do, and return -EROFS when the handle is aborted.
--- fs/ocfs2/journal.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
fs/ocfs2/journal.c +5 -1
@@ -476,8 +476,12 @@ bail:*/ int ocfs2_assure_trans_credits (handle_t *handle,int nblocks) { -int old_nblks =jbd2_handle_buffer_credits (handle); +int old_nblks; +if (is_handle_aborted (handle)) +return -EROFS ; + + old_nblks =jbd2_handle_buffer_credits (handle);trace_ocfs2_assure_trans_credits (old_nblks);if (old_nblks >= nblocks)return 0 ;
Greg Kroah-Hartman
maintainerPATCH 1004/1266 ocfs2: add journal NULL check in ocfs2_checkpoint_inode()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joseph Qi <[email protected]>
commit a291c77c034b7a81849ce9b71cc9ecda9e587d89 upstream.
During unmount, ocfs2_journal_shutdown() frees the journal and sets osb->journal to NULL. Later, when VFS evicts remaining cached inodes, ocfs2_evict_inode() -> ocfs2_clear_inode() -> ocfs2_checkpoint_inode() -> ocfs2_ci_fully_checkpointed() dereferences osb->journal, causing a NULL pointer dereference.
Fix this by adding a NULL check for osb->journal in ocfs2_checkpoint_inode(). If the journal is NULL, it has already been fully flushed and destroyed during shutdown, so there is nothing to checkpoint.
--- fs/ocfs2/journal.h | 3 +++ 1 file changed, 3 insertions(+)
fs/ocfs2/journal.h +3 -0
@@ -196,6 +196,9 @@ static inline void ocfs2_checkpoint_inodif (ocfs2_mount_local (osb))return ; +if (!osb->journal) +return ; +if (!ocfs2_ci_fully_checkpointed (INODE_CACHE (inode))) {/* WARNING: This only kicks off a single * checkpoint. If someone races you and adds more
Greg Kroah-Hartman
maintainerPATCH 1005/1266 ocfs2: reject dinodes with non-canonical i_mode type
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 5366a017099c6a3c443be908a05f26fd72af12a1 upstream.
Patch series "ocfs2: harden inode validators against forged metadata", v2.
This series adds three structural checks to OCFS2 dinode validation so malformed on-disk fields are rejected before ocfs2_populate_inode() copies them into the in-core inode.
The checks cover:
- i_mode values whose type bits do not name a canonical POSIX file
type;
- non-device dinodes whose id1.dev1.i_rdev field is non-zero; and
- non-inline dinodes that claim non-zero i_size while i_clusters is zero, covering directories unconditionally and regular files on non-sparse volumes.
The normal read path reports these through ocfs2_error(), matching the existing suballoc-slot, inline-data, chain-list, and refcount checks. The online filecheck path uses the same structural predicates but keeps its own reporting contract, returning OCFS2_FILECHECK_ERR_INVALIDINO instead of calling ocfs2_error().
This patch (of 3):
ocfs2_validate_inode_block() currently accepts any non-zero i_mode value. ocfs2_populate_inode() then copies that mode verbatim into inode->i_mode and dispatches on i_mode & S_IFMT to the file/dir/symlink/special_file iops; an unrecognised type falls through to ocfs2_special_file_iops and init_special_inode().
Reject dinodes whose type bits do not name one of the seven canonical POSIX file types. Use fs_umode_to_ftype(), the same generic file-type conversion helper OCFS2 already uses for directory entries, so the accepted inode type set matches the kernel file-type vocabulary instead of open-coding a local switch.
Apply the same structural check to the online filecheck read path. filecheck keeps its own error namespace, so it reports malformed i_mode through the filecheck logger and OCFS2_FILECHECK_ERR_INVALIDINO instead of calling ocfs2_error(), but it must not allow a malformed dinode to proceed into ocfs2_populate_inode().
--- fs/ocfs2/inode.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-)
fs/ocfs2/inode.c +33 -2
@@ -66,7 +66,12 @@ static int ocfs2_filecheck_read_inode_blstatic int ocfs2_filecheck_validate_inode_block (struct super_block *sb,struct buffer_head *bh);static int ocfs2_filecheck_repair_inode_block (struct super_block *sb, -struct buffer_head *bh); +struct buffer_head *bh); + +static bool ocfs2_valid_inode_mode (umode_t mode) +{ +return fs_umode_to_ftype (mode) !=FT_UNKNOWN ; +}void ocfs2_set_inode_flags (struct inode *inode) {
@@ -1419,6 +1424,24 @@ int ocfs2_validate_inode_block(struct su goto bail; } +/* +* Reject dinodes whose i_mode does not name one of the seven +* canonical POSIX file types. ocfs2_populate_inode() copies +* i_mode verbatim into inode->i_mode and then dispatches via +* switch (mode & S_IFMT) to file/dir/symlink/special_file iops; +* an unrecognised type falls into ocfs2_special_file_iops with +* init_special_inode(), which interprets i_rdev. Constrain the +* type here so the dispatch only ever sees a value mkfs.ocfs2 / +* VFS can produce. +*/ +if (!ocfs2_valid_inode_mode (le16_to_cpu (di->i_mode))) { + rc =ocfs2_error (sb, +"Invalid dinode #%llu: mode 0%o has unknown file type\n" , + (unsigned long long )bh->b_blocknr, +le16_to_cpu (di->i_mode)); + goto bail; + } +if (le16_to_cpu (di->i_dyn_features) &OCFS2_INLINE_DATA_FL ) {struct ocfs2_inline_data *data = &di->id2.i_data;
@@ -1515,6 +1538,15 @@ static int ocfs2_filecheck_validate_inod (unsigned long long )bh->b_blocknr, le32_to_cpu(di->i_fs_generation)); rc = -OCFS2_FILECHECK_ERR_GENERATION ; + goto bail; + } + +if (!ocfs2_valid_inode_mode (le16_to_cpu (di->i_mode))) { +mlog (ML_ERROR , +"Filecheck: invalid dinode #%llu: mode 0%o has unknown file type\n" , + (unsigned long long )bh->b_blocknr, +le16_to_cpu (di->i_mode)); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO ; } bail:
@@ -1690,4 +1722,3 @@ const struct ocfs2_caching_operations oc .co_io_lock = ocfs2_inode_cache_io_lock, .co_io_unlock = ocfs2_inode_cache_io_unlock, }; -