mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
net: wwan: t7xx: validate port_count against message length in t7xx_port_enum_msg_handler
t7xx_port_enum_msg_handler() uses the modem-supplied port_count field as
a loop bound over port_msg->data[] without checking that the message buffer
contains sufficient data. A modem sending port_count=65535 in a 12-byte
buffer triggers a slab-out-of-bounds read of up to 262140 bytes.
Add a sizeof(*port_msg) check before accessing the port message header
fields to guard against undersized messages.
Add a struct_size() check after extracting port_count and before the loop.
In t7xx_parse_host_rt_data(), guard the rt_feature header read with a
remaining-buffer check before accessing data_len, validate feat_data_len
against the actual remaining buffer to prevent OOB reads and signed
integer overflow on offset.
Pass msg_len from both call sites: skb->len at the DPMAIF path after
skb_pull(), and the validated feat_data_len at the handshake path.
Fixes: da45d2566a ("net: wwan: t7xx: Add control port")
Cc: stable@vger.kernel.org
Signed-off-by: Pavitra Jha <jhapavitra98@gmail.com>
Link: https://patch.msgid.link/20260501110713.145563-1-jhapavitra98@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
f83e07b292
commit
0e7c074cfc
|
|
@ -457,8 +457,20 @@ static int t7xx_parse_host_rt_data(struct t7xx_fsm_ctl *ctl, struct t7xx_sys_inf
|
|||
|
||||
offset = sizeof(struct feature_query);
|
||||
for (i = 0; i < FEATURE_COUNT && offset < data_length; i++) {
|
||||
size_t remaining = data_length - offset;
|
||||
size_t feat_data_len, feat_total;
|
||||
|
||||
if (remaining < sizeof(*rt_feature))
|
||||
break;
|
||||
|
||||
rt_feature = data + offset;
|
||||
offset += sizeof(*rt_feature) + le32_to_cpu(rt_feature->data_len);
|
||||
feat_data_len = le32_to_cpu(rt_feature->data_len);
|
||||
|
||||
if (feat_data_len > remaining - sizeof(*rt_feature))
|
||||
break;
|
||||
|
||||
feat_total = sizeof(*rt_feature) + feat_data_len;
|
||||
offset += feat_total;
|
||||
|
||||
ft_spt_cfg = FIELD_GET(FEATURE_MSK, core->feature_set[i]);
|
||||
if (ft_spt_cfg != MTK_FEATURE_MUST_BE_SUPPORTED)
|
||||
|
|
@ -468,8 +480,10 @@ static int t7xx_parse_host_rt_data(struct t7xx_fsm_ctl *ctl, struct t7xx_sys_inf
|
|||
if (ft_spt_st != MTK_FEATURE_MUST_BE_SUPPORTED)
|
||||
return -EINVAL;
|
||||
|
||||
if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM)
|
||||
t7xx_port_enum_msg_handler(ctl->md, rt_feature->data);
|
||||
if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM) {
|
||||
t7xx_port_enum_msg_handler(ctl->md, rt_feature->data,
|
||||
feat_data_len);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ static int fsm_ee_message_handler(struct t7xx_port *port, struct t7xx_fsm_ctl *c
|
|||
* t7xx_port_enum_msg_handler() - Parse the port enumeration message to create/remove nodes.
|
||||
* @md: Modem context.
|
||||
* @msg: Message.
|
||||
* @msg_len: Length of @msg in bytes.
|
||||
*
|
||||
* Used to control create/remove device node.
|
||||
*
|
||||
|
|
@ -124,12 +125,18 @@ static int fsm_ee_message_handler(struct t7xx_port *port, struct t7xx_fsm_ctl *c
|
|||
* * 0 - Success.
|
||||
* * -EFAULT - Message check failure.
|
||||
*/
|
||||
int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg)
|
||||
int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg, size_t msg_len)
|
||||
{
|
||||
struct device *dev = &md->t7xx_dev->pdev->dev;
|
||||
unsigned int version, port_count, i;
|
||||
struct port_msg *port_msg = msg;
|
||||
|
||||
if (msg_len < sizeof(*port_msg)) {
|
||||
dev_err(dev, "Port enum msg too short for header: need %zu, have %zu\n",
|
||||
sizeof(*port_msg), msg_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
version = FIELD_GET(PORT_MSG_VERSION, le32_to_cpu(port_msg->info));
|
||||
if (version != PORT_ENUM_VER ||
|
||||
le32_to_cpu(port_msg->head_pattern) != PORT_ENUM_HEAD_PATTERN ||
|
||||
|
|
@ -141,6 +148,13 @@ int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg)
|
|||
}
|
||||
|
||||
port_count = FIELD_GET(PORT_MSG_PRT_CNT, le32_to_cpu(port_msg->info));
|
||||
|
||||
if (msg_len < struct_size(port_msg, data, port_count)) {
|
||||
dev_err(dev, "Port enum msg too short: need %zu, have %zu\n",
|
||||
struct_size(port_msg, data, port_count), msg_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < port_count; i++) {
|
||||
u32 port_info = le32_to_cpu(port_msg->data[i]);
|
||||
unsigned int ch_id;
|
||||
|
|
@ -191,7 +205,7 @@ static int control_msg_handler(struct t7xx_port *port, struct sk_buff *skb)
|
|||
|
||||
case CTL_ID_PORT_ENUM:
|
||||
skb_pull(skb, sizeof(*ctrl_msg_h));
|
||||
ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data);
|
||||
ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data, skb->len);
|
||||
if (!ret)
|
||||
ret = port_ctl_send_msg_to_md(port, CTL_ID_PORT_ENUM, 0);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ void t7xx_port_proxy_reset(struct port_proxy *port_prox);
|
|||
void t7xx_port_proxy_uninit(struct port_proxy *port_prox);
|
||||
int t7xx_port_proxy_init(struct t7xx_modem *md);
|
||||
void t7xx_port_proxy_md_status_notify(struct port_proxy *port_prox, unsigned int state);
|
||||
int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg);
|
||||
int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg, size_t msg_len);
|
||||
int t7xx_port_proxy_chl_enable_disable(struct port_proxy *port_prox, unsigned int ch_id,
|
||||
bool en_flag);
|
||||
void t7xx_port_proxy_set_cfg(struct t7xx_modem *md, enum port_cfg_id cfg_id);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user