Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

can: gw: ensure DLC boundaries after CAN frame modification

Muyu Yu provided a POC where user root with CAP_NET_ADMIN can create a CAN
frame modification rule that makes the data length code a higher value than
the available CAN frame data size. In combination with a configured checksum
calculation where the result is stored relatively to the end of the data
(e.g. cgw_csum_xor_rel) the tail of the skb (e.g. frag_list pointer in
skb_shared_info) can be rewritten which finally can cause a system crash.

Michael Kubecek suggested to drop frames that have a DLC exceeding the
available space after the modification process and provided a patch that can
handle CAN FD frames too. Within this patch we also limit the length for the
checksum calculations to the maximum of Classic CAN data length (8).

CAN frames that are dropped by these additional checks are counted with the
CGW_DELETED counter which indicates misconfigurations in can-gw rules.

This fixes CVE-2019-3701.

Reported-by: Muyu Yu <ieatmuttonchuan@gmail.com>
Reported-by: Marcus Meissner <meissner@suse.de>
Suggested-by: Michal Kubecek <mkubecek@suse.cz>
Tested-by: Muyu Yu <ieatmuttonchuan@gmail.com>
Tested-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Cc: linux-stable <stable@vger.kernel.org> # >= v3.2
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Oliver Hartkopp and committed by
David S. Miller
0aaa8137 01cd364a

+28 -4
+28 -4
net/can/gw.c
··· 416 416 while (modidx < MAX_MODFUNCTIONS && gwj->mod.modfunc[modidx]) 417 417 (*gwj->mod.modfunc[modidx++])(cf, &gwj->mod); 418 418 419 - /* check for checksum updates when the CAN frame has been modified */ 419 + /* Has the CAN frame been modified? */ 420 420 if (modidx) { 421 - if (gwj->mod.csumfunc.crc8) 422 - (*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8); 421 + /* get available space for the processed CAN frame type */ 422 + int max_len = nskb->len - offsetof(struct can_frame, data); 423 423 424 - if (gwj->mod.csumfunc.xor) 424 + /* dlc may have changed, make sure it fits to the CAN frame */ 425 + if (cf->can_dlc > max_len) 426 + goto out_delete; 427 + 428 + /* check for checksum updates in classic CAN length only */ 429 + if (gwj->mod.csumfunc.crc8) { 430 + if (cf->can_dlc > 8) 431 + goto out_delete; 432 + 433 + (*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8); 434 + } 435 + 436 + if (gwj->mod.csumfunc.xor) { 437 + if (cf->can_dlc > 8) 438 + goto out_delete; 439 + 425 440 (*gwj->mod.csumfunc.xor)(cf, &gwj->mod.csum.xor); 441 + } 426 442 } 427 443 428 444 /* clear the skb timestamp if not configured the other way */ ··· 450 434 gwj->dropped_frames++; 451 435 else 452 436 gwj->handled_frames++; 437 + 438 + return; 439 + 440 + out_delete: 441 + /* delete frame due to misconfiguration */ 442 + gwj->deleted_frames++; 443 + kfree_skb(nskb); 444 + return; 453 445 } 454 446 455 447 static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj)