tcp: md5: use kmalloc() backed scratch areas
authorEric Dumazet <edumazet@google.com>
Mon, 27 Jun 2016 16:51:53 +0000 (18:51 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 1 Jul 2016 08:02:55 +0000 (04:02 -0400)
Some arches have virtually mapped kernel stacks, or will soon have.

tcp_md5_hash_header() uses an automatic variable to copy tcp header
before mangling th->check and calling crypto function, which might
be problematic on such arches.

David says that using percpu storage is also problematic on non SMP
builds.

Just use kmalloc() to allocate scratch areas.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Andy Lutomirski <luto@amacapital.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/tcp.h
net/ipv4/tcp.c
net/ipv4/tcp_ipv4.c
net/ipv6/tcp_ipv6.c

index d825858fe4f1f8d1f778345143ab9046b3fcf5dc..c00e7d51bb18d77f47ef9fb016ef432f5db4f7be 100644 (file)
@@ -1384,7 +1384,7 @@ union tcp_md5sum_block {
 /* - pool: digest algorithm, hash description and scratch buffer */
 struct tcp_md5sig_pool {
        struct ahash_request    *md5_req;
-       union tcp_md5sum_block  md5_blk;
+       void                    *scratch;
 };
 
 /* - functions */
@@ -1420,7 +1420,6 @@ static inline void tcp_put_md5sig_pool(void)
        local_bh_enable();
 }
 
-int tcp_md5_hash_header(struct tcp_md5sig_pool *, const struct tcphdr *);
 int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff *,
                          unsigned int header_len);
 int tcp_md5_hash_key(struct tcp_md5sig_pool *hp,
index 108ef2a6665c9157aae22c7ff0ed3359129c754a..032a96d78c99deda3b3298a305298f92776e2500 100644 (file)
@@ -3026,8 +3026,18 @@ static void __tcp_alloc_md5sig_pool(void)
                return;
 
        for_each_possible_cpu(cpu) {
+               void *scratch = per_cpu(tcp_md5sig_pool, cpu).scratch;
                struct ahash_request *req;
 
+               if (!scratch) {
+                       scratch = kmalloc_node(sizeof(union tcp_md5sum_block) +
+                                              sizeof(struct tcphdr),
+                                              GFP_KERNEL,
+                                              cpu_to_node(cpu));
+                       if (!scratch)
+                               return;
+                       per_cpu(tcp_md5sig_pool, cpu).scratch = scratch;
+               }
                if (per_cpu(tcp_md5sig_pool, cpu).md5_req)
                        continue;
 
index 3708de2a66833cf1d4a221a2b6ce3923bde978c4..32b048e524d6773538918eca175b3f422f9c2aa7 100644 (file)
@@ -1018,27 +1018,28 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
                              GFP_KERNEL);
 }
 
-static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
-                                       __be32 daddr, __be32 saddr, int nbytes)
+static int tcp_v4_md5_hash_headers(struct tcp_md5sig_pool *hp,
+                                  __be32 daddr, __be32 saddr,
+                                  const struct tcphdr *th, int nbytes)
 {
        struct tcp4_pseudohdr *bp;
        struct scatterlist sg;
+       struct tcphdr *_th;
 
-       bp = &hp->md5_blk.ip4;
-
-       /*
-        * 1. the TCP pseudo-header (in the order: source IP address,
-        * destination IP address, zero-padded protocol number, and
-        * segment length)
-        */
+       bp = hp->scratch;
        bp->saddr = saddr;
        bp->daddr = daddr;
        bp->pad = 0;
        bp->protocol = IPPROTO_TCP;
        bp->len = cpu_to_be16(nbytes);
 
-       sg_init_one(&sg, bp, sizeof(*bp));
-       ahash_request_set_crypt(hp->md5_req, &sg, NULL, sizeof(*bp));
+       _th = (struct tcphdr *)(bp + 1);
+       memcpy(_th, th, sizeof(*th));
+       _th->check = 0;
+
+       sg_init_one(&sg, bp, sizeof(*bp) + sizeof(*th));
+       ahash_request_set_crypt(hp->md5_req, &sg, NULL,
+                               sizeof(*bp) + sizeof(*th));
        return crypto_ahash_update(hp->md5_req);
 }
 
@@ -1055,9 +1056,7 @@ static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
 
        if (crypto_ahash_init(req))
                goto clear_hash;
-       if (tcp_v4_md5_hash_pseudoheader(hp, daddr, saddr, th->doff << 2))
-               goto clear_hash;
-       if (tcp_md5_hash_header(hp, th))
+       if (tcp_v4_md5_hash_headers(hp, daddr, saddr, th, th->doff << 2))
                goto clear_hash;
        if (tcp_md5_hash_key(hp, key))
                goto clear_hash;
@@ -1101,9 +1100,7 @@ int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
        if (crypto_ahash_init(req))
                goto clear_hash;
 
-       if (tcp_v4_md5_hash_pseudoheader(hp, daddr, saddr, skb->len))
-               goto clear_hash;
-       if (tcp_md5_hash_header(hp, th))
+       if (tcp_v4_md5_hash_headers(hp, daddr, saddr, th, skb->len))
                goto clear_hash;
        if (tcp_md5_hash_skb_data(hp, skb, th->doff << 2))
                goto clear_hash;
index 2255d2bf5f6bdb532c51eff6792b3024b3eeefa1..37cf91323319332d6256615d367bbede62066ae9 100644 (file)
@@ -526,26 +526,33 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, char __user *optval,
                              AF_INET6, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
 }
 
-static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
-                                       const struct in6_addr *daddr,
-                                       const struct in6_addr *saddr, int nbytes)
+static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp,
+                                  const struct in6_addr *daddr,
+                                  const struct in6_addr *saddr,
+                                  const struct tcphdr *th, int nbytes)
 {
        struct tcp6_pseudohdr *bp;
        struct scatterlist sg;
+       struct tcphdr *_th;
 
-       bp = &hp->md5_blk.ip6;
+       bp = hp->scratch;
        /* 1. TCP pseudo-header (RFC2460) */
        bp->saddr = *saddr;
        bp->daddr = *daddr;
        bp->protocol = cpu_to_be32(IPPROTO_TCP);
        bp->len = cpu_to_be32(nbytes);
 
-       sg_init_one(&sg, bp, sizeof(*bp));
-       ahash_request_set_crypt(hp->md5_req, &sg, NULL, sizeof(*bp));
+       _th = (struct tcphdr *)(bp + 1);
+       memcpy(_th, th, sizeof(*th));
+       _th->check = 0;
+
+       sg_init_one(&sg, bp, sizeof(*bp) + sizeof(*th));
+       ahash_request_set_crypt(hp->md5_req, &sg, NULL,
+                               sizeof(*bp) + sizeof(*th));
        return crypto_ahash_update(hp->md5_req);
 }
 
-static int tcp_v6_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
+static int tcp_v6_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
                               const struct in6_addr *daddr, struct in6_addr *saddr,
                               const struct tcphdr *th)
 {
@@ -559,9 +566,7 @@ static int tcp_v6_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
 
        if (crypto_ahash_init(req))
                goto clear_hash;
-       if (tcp_v6_md5_hash_pseudoheader(hp, daddr, saddr, th->doff << 2))
-               goto clear_hash;
-       if (tcp_md5_hash_header(hp, th))
+       if (tcp_v6_md5_hash_headers(hp, daddr, saddr, th, th->doff << 2))
                goto clear_hash;
        if (tcp_md5_hash_key(hp, key))
                goto clear_hash;
@@ -606,9 +611,7 @@ static int tcp_v6_md5_hash_skb(char *md5_hash,
        if (crypto_ahash_init(req))
                goto clear_hash;
 
-       if (tcp_v6_md5_hash_pseudoheader(hp, daddr, saddr, skb->len))
-               goto clear_hash;
-       if (tcp_md5_hash_header(hp, th))
+       if (tcp_v6_md5_hash_headers(hp, daddr, saddr, th, skb->len))
                goto clear_hash;
        if (tcp_md5_hash_skb_data(hp, skb, th->doff << 2))
                goto clear_hash;