]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
can: bcm: validate frame length in bcm_rx_setup() for RTR replies
authorOliver Hartkopp <socketcan@hartkopp.net>
Tue, 14 Jul 2026 16:55:29 +0000 (18:55 +0200)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Thu, 16 Jul 2026 08:02:01 +0000 (10:02 +0200)
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: ffd980f976e7 ("[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>
net/can/bcm.c

index 68a62f605432f5ed6e273280cf4944af86e1aa0c..2d9c9cd7453669731abc48c8acb1f21ef1a92836 100644 (file)
@@ -1246,22 +1246,37 @@ free_op:
        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;
 }
 
 /*