rose: Add length checks to CALL_REQUEST parsing, CVE-2011-1493
[linux-flexiantxendom0-natty.git] / net / rose / rose_route.c
index ea2e723..f8beb5f 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/string.h>
 #include <linux/sockios.h>
 #include <linux/net.h>
+#include <linux/slab.h>
 #include <net/ax25.h>
 #include <linux/inet.h>
 #include <linux/netdevice.h>
@@ -77,8 +78,9 @@ static int __must_check rose_add_node(struct rose_route_struct *rose_route,
 
        rose_neigh = rose_neigh_list;
        while (rose_neigh != NULL) {
-               if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0
-                   && rose_neigh->dev == dev)
+               if (ax25cmp(&rose_route->neighbour,
+                           &rose_neigh->callsign) == 0 &&
+                   rose_neigh->dev == dev)
                        break;
                rose_neigh = rose_neigh->next;
        }
@@ -107,7 +109,9 @@ static int __must_check rose_add_node(struct rose_route_struct *rose_route,
                init_timer(&rose_neigh->t0timer);
 
                if (rose_route->ndigis != 0) {
-                       if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) {
+                       rose_neigh->digipeat =
+                               kmalloc(sizeof(ax25_digi), GFP_ATOMIC);
+                       if (rose_neigh->digipeat == NULL) {
                                kfree(rose_neigh);
                                res = -ENOMEM;
                                goto out;
@@ -234,6 +238,8 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh)
 
        if ((s = rose_neigh_list) == rose_neigh) {
                rose_neigh_list = rose_neigh->next;
+               if (rose_neigh->ax25)
+                       ax25_cb_put(rose_neigh->ax25);
                kfree(rose_neigh->digipeat);
                kfree(rose_neigh);
                return;
@@ -242,6 +248,8 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh)
        while (s != NULL && s->next != NULL) {
                if (s->next == rose_neigh) {
                        s->next = rose_neigh->next;
+                       if (rose_neigh->ax25)
+                               ax25_cb_put(rose_neigh->ax25);
                        kfree(rose_neigh->digipeat);
                        kfree(rose_neigh);
                        return;
@@ -311,8 +319,9 @@ static int rose_del_node(struct rose_route_struct *rose_route,
 
        rose_neigh = rose_neigh_list;
        while (rose_neigh != NULL) {
-               if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0
-                   && rose_neigh->dev == dev)
+               if (ax25cmp(&rose_route->neighbour,
+                           &rose_neigh->callsign) == 0 &&
+                   rose_neigh->dev == dev)
                        break;
                rose_neigh = rose_neigh->next;
        }
@@ -810,6 +819,7 @@ void rose_link_failed(ax25_cb *ax25, int reason)
 
        if (rose_neigh != NULL) {
                rose_neigh->ax25 = NULL;
+               ax25_cb_put(ax25);
 
                rose_del_route_by_neigh(rose_neigh);
                rose_kill_by_neigh(rose_neigh);
@@ -847,7 +857,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
        unsigned int lci, new_lci;
        unsigned char cause, diagnostic;
        struct net_device *dev;
-       int len, res = 0;
+       int res = 0;
        char buf[11];
 
 #if 0
@@ -855,10 +865,17 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
                return res;
 #endif
 
+       if (skb->len < ROSE_MIN_LEN)
+               return res;
        frametype = skb->data[2];
        lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
-       src_addr  = (rose_address *)(skb->data + 9);
-       dest_addr = (rose_address *)(skb->data + 4);
+       if (frametype == ROSE_CALL_REQUEST &&
+           (skb->len <= ROSE_CALL_REQ_FACILITIES_OFF ||
+            skb->data[ROSE_CALL_REQ_ADDR_LEN_OFF] !=
+            ROSE_CALL_REQ_ADDR_LEN_VAL))
+               return res;
+       src_addr  = (rose_address *)(skb->data + ROSE_CALL_REQ_SRC_ADDR_OFF);
+       dest_addr = (rose_address *)(skb->data + ROSE_CALL_REQ_DEST_ADDR_OFF);
 
        spin_lock_bh(&rose_neigh_list_lock);
        spin_lock_bh(&rose_route_list_lock);
@@ -996,12 +1013,11 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
                goto out;
        }
 
-       len  = (((skb->data[3] >> 4) & 0x0F) + 1) >> 1;
-       len += (((skb->data[3] >> 0) & 0x0F) + 1) >> 1;
-
        memset(&facilities, 0x00, sizeof(struct rose_facilities_struct));
 
-       if (!rose_parse_facilities(skb->data + len + 4, &facilities)) {
+       if (!rose_parse_facilities(skb->data + ROSE_CALL_REQ_FACILITIES_OFF,
+                                  skb->len - ROSE_CALL_REQ_FACILITIES_OFF,
+                                  &facilities)) {
                rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76);
                goto out;
        }