xorl %eax, %eax

CVE-2011-2898: Linux kernel AF_PACKET Information Leak

with 2 comments

This is another common kernel information leak reported by Eric Dumazet. The bug was part of include/linux/if_packet.h header file where the following two strcutures were defined.

struct tpacket_auxdata {
        __u32           tp_status;
        __u32           tp_len;
        __u32           tp_snaplen;
        __u16           tp_mac;
        __u16           tp_net;
        __u16           tp_vlan_tci;
};
   ...
struct tpacket2_hdr {
        __u32           tp_status;
        __u32           tp_len;
        __u32           tp_snaplen;
        __u16           tp_mac;
        __u16           tp_net;
        __u32           tp_sec;
        __u32           tp_nsec;
        __u16           tp_vlan_tci;
};

So, in both cases structures have two additional Bytes for alignment. However, since it was not specified, a user could obtain these padding Bytes from the contents of the memory after each structure. The two routines that could be used for this purpose are part of net/packet/af_packet.c source code file and are shown below.

static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
                       struct packet_type *pt, struct net_device *orig_dev)
{
        struct sock *sk;
        struct packet_sock *po;
        struct sockaddr_ll *sll;
        union {
                struct tpacket_hdr *h1;
                struct tpacket2_hdr *h2;
                void *raw;
        } h;
        u8 *skb_head = skb->data;
        int skb_len = skb->len;
    ...
        case TPACKET_V2
                h.h2->tp_len = skb->len;
                h.h2->tp_snaplen = snaplen;
                h.h2->tp_mac = macoff;
                h.h2->tp_net = netoff;
                if ((po->tp_tstamp & SOF_TIMESTAMPING_SYS_HARDWARE)
                                && shhwtstamps->syststamp.tv64)
                        ts = ktime_to_timespec(shhwtstamps->syststamp);
                else if ((po->tp_tstamp & SOF_TIMESTAMPING_RAW_HARDWARE)
                                && shhwtstamps->hwtstamp.tv64)
                        ts = ktime_to_timespec(shhwtstamps->hwtstamp);
                else if (skb->tstamp.tv64)
                        ts = ktime_to_timespec(skb->tstamp);
                else
                        getnstimeofday(&ts);
                h.h2->tp_sec = ts.tv_sec;
                h.h2->tp_nsec = ts.tv_nsec;
                h.h2->tp_vlan_tci = vlan_tx_tag_get(skb);
                hdrlen = sizeof(*h.h2);
                break;
        default:
                BUG();
        }
    ...
        goto drop_n_restore;
}
    ...
/*
 *      Pull a packet from our receive queue and hand it to the user.
 *      If necessary we block.
 */

static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
                          struct msghdr *msg, size_t len, int flags)
{
        struct sock *sk = sock->sk;
        struct sk_buff *skb;
        int copied, err;
        struct sockaddr_ll *sll;
        int vnet_hdr_len = 0;
    ...
        if (pkt_sk(sk)->auxdata) {
                struct tpacket_auxdata aux;

                aux.tp_status = TP_STATUS_USER;
                if (skb->ip_summed == CHECKSUM_PARTIAL)
                        aux.tp_status |= TP_STATUS_CSUMNOTREADY;
                aux.tp_len = PACKET_SKB_CB(skb)->origlen;
                aux.tp_snaplen = skb->len;
                aux.tp_mac = 0;
                aux.tp_net = skb_network_offset(skb);
                aux.tp_vlan_tci = vlan_tx_tag_get(skb);

                put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux);
        }

        /*
         *      Free or return the buffer as appropriate. Again this
         *      hides all the races and re-entrancy issues from us.
         */
        err = vnet_hdr_len + ((flags&MSG_TRUNC) ? skb->len : copied);

out_free:
        skb_free_datagram(sk, skb);
out:
        return err;
}

And of course, the fix was to add the padding as part of the two structures…

 	__u16		tp_mac;
 	__u16		tp_net;
 	__u16		tp_vlan_tci;
+	__u16		tp_padding;
 };
 
 /* Rx ring - header status */
@@ -101,6 +102,7 @@ struct tpacket2_hdr {
 	__u32		tp_sec;
 	__u32		tp_nsec;
 	__u16		tp_vlan_tci;
+	__u16		tp_padding;
 };

And also update the aforementioned functions to zero out the padding too.

@@ -804,6 +804,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
 		} else {
 			h.h2->tp_vlan_tci = 0;
 		}
+		h.h2->tp_padding = 0;
 		hdrlen = sizeof(*h.h2);
 		break;
 	default:
@@ -1736,6 +1737,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
 		} else {
 			aux.tp_vlan_tci = 0;
 		}
+		aux.tp_padding = 0;
 		put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux);
 	}

Written by xorl

October 7, 2011 at 21:39

Posted in bugs, linux

2 Responses

Subscribe to comments with RSS.

  1. I’m not sure that I got it. For tpacket2, how would I get hold of the extra bytes? The quoted function only fills the components…

    Martin

    October 8, 2011 at 19:22

  2. For alignment both structures have more Bytes (specifically 16 bits each). This space is not initialized as you mentioned (“only fills the components”) but the user is allowed to read this structure (including the additional alignment Bytes), the alignment/padding Bytes will contain “garbage” from the memory when copied back to the user leading to kernel information leak.

    xorl

    October 9, 2011 at 00:03


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s