can: bcm: validate frame length in bcm_rx_setup() for RTR replies

bcm_tx_setup() validates cf->len against the CAN/CAN FD DLC limits
before installing frames for TX_SETUP, but bcm_rx_setup() never did
the same for the RTR-reply frame configured via RX_SETUP with
RX_RTR_FRAME.

Fixes: ffd980f976 ("[CAN]: Add broadcast manager (bcm) protocol")
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Link: https://patch.msgid.link/20260714-bcm_fixes-v15-7-562f7e3e42da@hartkopp.net
Cc: stable@kernel.org
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Oliver Hartkopp 2026-07-14 18:55:29 +02:00 committed by Marc Kleine-Budde
parent 12ce799f7a
commit 62ec41f364

View File

@ -1246,22 +1246,37 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
return err;
}
static void bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head,
struct bcm_op *op, void *new_frames)
static int bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head,
struct bcm_op *op, void *new_frames)
{
struct canfd_frame *frame0 = new_frames;
if (!(msg_head->flags & RX_RTR_FRAME))
return 0;
/* this frame is sent out as-is by bcm_can_tx() whenever a matching
* remote request is received, so validate its length the same way
* bcm_tx_setup() validates TX_SETUP frames before installing it
*/
if (msg_head->flags & CAN_FD_FRAME) {
if (frame0->len > 64)
return -EINVAL;
} else {
if (frame0->len > 8)
return -EINVAL;
}
/* funny feature in RX(!)_SETUP only for RTR-mode:
* copy can_id into frame BUT without RTR-flag to
* prevent a full-load-loopback-test ... ;-]
* normalize this on the staged buffer, before it is
* ever installed into op->frames.
*/
if (msg_head->flags & RX_RTR_FRAME) {
struct canfd_frame *frame0 = new_frames;
if ((msg_head->flags & TX_CP_CAN_ID) ||
frame0->can_id == op->can_id)
frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
if ((msg_head->flags & TX_CP_CAN_ID) ||
frame0->can_id == op->can_id)
frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
}
return 0;
}
/*
@ -1324,7 +1339,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
return err;
}
bcm_rx_setup_rtr_check(msg_head, op, new_frames);
err = bcm_rx_setup_rtr_check(msg_head, op, new_frames);
if (err < 0) {
kfree(new_frames);
return err;
}
}
spin_lock_bh(&op->bcm_rx_update_lock);
@ -1397,16 +1416,12 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
if (msg_head->nframes) {
err = memcpy_from_msg(op->frames, msg,
msg_head->nframes * op->cfsiz);
if (err < 0) {
if (op->frames != &op->sframe)
kfree(op->frames);
if (op->last_frames != &op->last_sframe)
kfree(op->last_frames);
kfree(op);
return err;
}
if (err < 0)
goto free_op;
bcm_rx_setup_rtr_check(msg_head, op, op->frames);
err = bcm_rx_setup_rtr_check(msg_head, op, op->frames);
if (err < 0)
goto free_op;
}
/* bcm_can_tx / bcm_tx_timeout_handler needs this */
@ -1505,6 +1520,14 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
}
return msg_head->nframes * op->cfsiz + MHSIZ;
free_op:
if (op->frames != &op->sframe)
kfree(op->frames);
if (op->last_frames != &op->last_sframe)
kfree(op->last_frames);
kfree(op);
return err;
}
/*