can: add sanity checks
authorOliver Hartkopp <oliver.hartkopp@volkswagen.de>
Sun, 6 Jul 2008 06:38:43 +0000 (23:38 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sun, 6 Jul 2008 06:38:43 +0000 (23:38 -0700)
Even though the CAN netlayer only deals with CAN netdevices, the
netlayer interface to the userspace and to the device layer should
perform some sanity checks.

This patch adds several sanity checks that mainly prevent userspace apps
to send broken content into the system that may be misinterpreted by
some other userspace application.

Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
Acked-by: Andre Naujoks <nautsch@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/can/af_can.c
net/can/bcm.c
net/can/raw.c

index 7e8ca2836452a5053c4695691ceba78a9d4605d1..484bbf6dd0322790f6f42cee71c3e1f86f6ace41 100644 (file)
@@ -205,12 +205,19 @@ static int can_create(struct net *net, struct socket *sock, int protocol)
  *  -ENOBUFS on full driver queue (see net_xmit_errno())
  *  -ENOMEM when local loopback failed at calling skb_clone()
  *  -EPERM when trying to send on a non-CAN interface
+ *  -EINVAL when the skb->data does not contain a valid CAN frame
  */
 int can_send(struct sk_buff *skb, int loop)
 {
        struct sk_buff *newskb = NULL;
+       struct can_frame *cf = (struct can_frame *)skb->data;
        int err;
 
+       if (skb->len != sizeof(struct can_frame) || cf->can_dlc > 8) {
+               kfree_skb(skb);
+               return -EINVAL;
+       }
+
        if (skb->dev->type != ARPHRD_CAN) {
                kfree_skb(skb);
                return -EPERM;
@@ -605,6 +612,7 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
                   struct packet_type *pt, struct net_device *orig_dev)
 {
        struct dev_rcv_lists *d;
+       struct can_frame *cf = (struct can_frame *)skb->data;
        int matches;
 
        if (dev->type != ARPHRD_CAN || dev_net(dev) != &init_net) {
@@ -612,6 +620,8 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
                return 0;
        }
 
+       BUG_ON(skb->len != sizeof(struct can_frame) || cf->can_dlc > 8);
+
        /* update statistics */
        can_stats.rx_frames++;
        can_stats.rx_frames_delta++;
index d9a3a9d13bedbb5b11cc1850aa604adccf47ad1c..72c2ce904f83ab9f92109e33c6ed34f947a80a73 100644 (file)
@@ -298,7 +298,7 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head,
 
        if (head->nframes) {
                /* can_frames starting here */
-               firstframe = (struct can_frame *) skb_tail_pointer(skb);
+               firstframe = (struct can_frame *)skb_tail_pointer(skb);
 
                memcpy(skb_put(skb, datalen), frames, datalen);
 
@@ -826,6 +826,10 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
                for (i = 0; i < msg_head->nframes; i++) {
                        err = memcpy_fromiovec((u8 *)&op->frames[i],
                                               msg->msg_iov, CFSIZ);
+
+                       if (op->frames[i].can_dlc > 8)
+                               err = -EINVAL;
+
                        if (err < 0)
                                return err;
 
@@ -858,6 +862,10 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
                for (i = 0; i < msg_head->nframes; i++) {
                        err = memcpy_fromiovec((u8 *)&op->frames[i],
                                               msg->msg_iov, CFSIZ);
+
+                       if (op->frames[i].can_dlc > 8)
+                               err = -EINVAL;
+
                        if (err < 0) {
                                if (op->frames != &op->sframe)
                                        kfree(op->frames);
@@ -1164,9 +1172,12 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk)
 
        skb->dev = dev;
        skb->sk  = sk;
-       can_send(skb, 1); /* send with loopback */
+       err = can_send(skb, 1); /* send with loopback */
        dev_put(dev);
 
+       if (err)
+               return err;
+
        return CFSIZ + MHSIZ;
 }
 
@@ -1185,6 +1196,10 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,
        if (!bo->bound)
                return -ENOTCONN;
 
+       /* check for valid message length from userspace */
+       if (size < MHSIZ || (size - MHSIZ) % CFSIZ)
+               return -EINVAL;
+
        /* check for alternative ifindex for this bcm_op */
 
        if (!ifindex && msg->msg_name) {
@@ -1259,8 +1274,8 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,
                break;
 
        case TX_SEND:
-               /* we need at least one can_frame */
-               if (msg_head.nframes < 1)
+               /* we need exactly one can_frame behind the msg head */
+               if ((msg_head.nframes != 1) || (size != CFSIZ + MHSIZ))
                        ret = -EINVAL;
                else
                        ret = bcm_tx_send(msg, ifindex, sk);
index 69877b8e7e9c772be9479bced40b6b8273662322..3e46ee36a1aaf819b8ed4a8d7c6afcb7b8614948 100644 (file)
@@ -632,6 +632,9 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
        } else
                ifindex = ro->ifindex;
 
+       if (size != sizeof(struct can_frame))
+               return -EINVAL;
+
        dev = dev_get_by_index(&init_net, ifindex);
        if (!dev)
                return -ENXIO;