xfrm: prevent ipcomp scratch buffer race condition

In ipcomp_compress(), sortirq is enabled too early, allowing the
per-cpu scratch buffer to be rewritten by ipcomp_decompress()
(called on the same CPU in softirq context) between populating
the buffer and copying the compressed data to the skb.

v2: as pointed out by Steffen Klassert, if we also move the
local_bh_disable() before reading the per-cpu pointers, we can
get rid of get_cpu()/put_cpu().

v3: removed ipcomp_decompress part (as explained by Herbert Xu,
it cannot be called from process context), get rid of cpu
variable (thanks to Eric Dumazet)

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
This commit is contained in:
Michal Kubecek 2013-10-17 15:07:40 +02:00 committed by Steffen Klassert
parent e9e4ea74f0
commit 12e3594698

View File

@ -141,14 +141,14 @@ static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
const int plen = skb->len; const int plen = skb->len;
int dlen = IPCOMP_SCRATCH_SIZE; int dlen = IPCOMP_SCRATCH_SIZE;
u8 *start = skb->data; u8 *start = skb->data;
const int cpu = get_cpu(); struct crypto_comp *tfm;
u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu); u8 *scratch;
struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu);
int err; int err;
local_bh_disable(); local_bh_disable();
scratch = *this_cpu_ptr(ipcomp_scratches);
tfm = *this_cpu_ptr(ipcd->tfms);
err = crypto_comp_compress(tfm, start, plen, scratch, &dlen); err = crypto_comp_compress(tfm, start, plen, scratch, &dlen);
local_bh_enable();
if (err) if (err)
goto out; goto out;
@ -158,13 +158,13 @@ static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
} }
memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen); memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen);
put_cpu(); local_bh_enable();
pskb_trim(skb, dlen + sizeof(struct ip_comp_hdr)); pskb_trim(skb, dlen + sizeof(struct ip_comp_hdr));
return 0; return 0;
out: out:
put_cpu(); local_bh_enable();
return err; return err;
} }