diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2010-11-30 03:49:02 -0500 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2010-11-30 03:49:02 -0500 |
commit | 0f6bb83cb12e4617e696ffa566f3fc6c092686e2 (patch) | |
tree | c871ab8acedb25ba19de73be427620af8475236d /crypto/algif_skcipher.c | |
parent | 7451708f39db19a8303bb7fb95f00aca9f673cb5 (diff) |
crypto: algif_skcipher - Fixed overflow when sndbuf is page aligned
When sk_sndbuf is not a multiple of PAGE_SIZE, the limit tests
in sendmsg fail as the limit variable becomes negative and we're
using an unsigned comparison.
The same thing can happen if sk_sndbuf is lowered after a sendmsg
call.
This patch fixes this by always taking the signed maximum of limit
and 0 before we perform the comparison.
It also rounds the value of sk_sndbuf down to a multiple of PAGE_SIZE
so that we don't end up allocating a page only to use a small number
of bytes in it because we're bound by sk_sndbuf.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'crypto/algif_skcipher.c')
-rw-r--r-- | crypto/algif_skcipher.c | 32 |
1 files changed, 11 insertions, 21 deletions
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 9b2f440e88a6..1f33480e3260 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c | |||
@@ -52,12 +52,18 @@ struct skcipher_ctx { | |||
52 | #define MAX_SGL_ENTS ((PAGE_SIZE - sizeof(struct skcipher_sg_list)) / \ | 52 | #define MAX_SGL_ENTS ((PAGE_SIZE - sizeof(struct skcipher_sg_list)) / \ |
53 | sizeof(struct scatterlist) - 1) | 53 | sizeof(struct scatterlist) - 1) |
54 | 54 | ||
55 | static inline bool skcipher_writable(struct sock *sk) | 55 | static inline int skcipher_sndbuf(struct sock *sk) |
56 | { | 56 | { |
57 | struct alg_sock *ask = alg_sk(sk); | 57 | struct alg_sock *ask = alg_sk(sk); |
58 | struct skcipher_ctx *ctx = ask->private; | 58 | struct skcipher_ctx *ctx = ask->private; |
59 | 59 | ||
60 | return ctx->used + PAGE_SIZE <= max_t(int, sk->sk_sndbuf, PAGE_SIZE); | 60 | return max_t(int, max_t(int, sk->sk_sndbuf & PAGE_MASK, PAGE_SIZE) - |
61 | ctx->used, 0); | ||
62 | } | ||
63 | |||
64 | static inline bool skcipher_writable(struct sock *sk) | ||
65 | { | ||
66 | return PAGE_SIZE <= skcipher_sndbuf(sk); | ||
61 | } | 67 | } |
62 | 68 | ||
63 | static int skcipher_alloc_sgl(struct sock *sk) | 69 | static int skcipher_alloc_sgl(struct sock *sk) |
@@ -245,7 +251,6 @@ static int skcipher_sendmsg(struct kiocb *unused, struct socket *sock, | |||
245 | struct af_alg_control con = {}; | 251 | struct af_alg_control con = {}; |
246 | long copied = 0; | 252 | long copied = 0; |
247 | bool enc = 0; | 253 | bool enc = 0; |
248 | int limit; | ||
249 | int err; | 254 | int err; |
250 | int i; | 255 | int i; |
251 | 256 | ||
@@ -281,9 +286,6 @@ static int skcipher_sendmsg(struct kiocb *unused, struct socket *sock, | |||
281 | memcpy(ctx->iv, con.iv->iv, ivsize); | 286 | memcpy(ctx->iv, con.iv->iv, ivsize); |
282 | } | 287 | } |
283 | 288 | ||
284 | limit = max_t(int, sk->sk_sndbuf, PAGE_SIZE); | ||
285 | limit -= ctx->used; | ||
286 | |||
287 | while (size) { | 289 | while (size) { |
288 | struct scatterlist *sg; | 290 | struct scatterlist *sg; |
289 | unsigned long len = size; | 291 | unsigned long len = size; |
@@ -309,20 +311,16 @@ static int skcipher_sendmsg(struct kiocb *unused, struct socket *sock, | |||
309 | ctx->used += len; | 311 | ctx->used += len; |
310 | copied += len; | 312 | copied += len; |
311 | size -= len; | 313 | size -= len; |
312 | limit -= len; | ||
313 | continue; | 314 | continue; |
314 | } | 315 | } |
315 | 316 | ||
316 | if (limit < PAGE_SIZE) { | 317 | if (!skcipher_writable(sk)) { |
317 | err = skcipher_wait_for_wmem(sk, msg->msg_flags); | 318 | err = skcipher_wait_for_wmem(sk, msg->msg_flags); |
318 | if (err) | 319 | if (err) |
319 | goto unlock; | 320 | goto unlock; |
320 | |||
321 | limit = max_t(int, sk->sk_sndbuf, PAGE_SIZE); | ||
322 | limit -= ctx->used; | ||
323 | } | 321 | } |
324 | 322 | ||
325 | len = min_t(unsigned long, len, limit); | 323 | len = min_t(unsigned long, len, skcipher_sndbuf(sk)); |
326 | 324 | ||
327 | err = skcipher_alloc_sgl(sk); | 325 | err = skcipher_alloc_sgl(sk); |
328 | if (err) | 326 | if (err) |
@@ -352,7 +350,6 @@ static int skcipher_sendmsg(struct kiocb *unused, struct socket *sock, | |||
352 | ctx->used += plen; | 350 | ctx->used += plen; |
353 | copied += plen; | 351 | copied += plen; |
354 | size -= plen; | 352 | size -= plen; |
355 | limit -= plen; | ||
356 | sgl->cur++; | 353 | sgl->cur++; |
357 | } while (len && sgl->cur < MAX_SGL_ENTS); | 354 | } while (len && sgl->cur < MAX_SGL_ENTS); |
358 | 355 | ||
@@ -380,7 +377,6 @@ static ssize_t skcipher_sendpage(struct socket *sock, struct page *page, | |||
380 | struct skcipher_ctx *ctx = ask->private; | 377 | struct skcipher_ctx *ctx = ask->private; |
381 | struct skcipher_sg_list *sgl; | 378 | struct skcipher_sg_list *sgl; |
382 | int err = -EINVAL; | 379 | int err = -EINVAL; |
383 | int limit; | ||
384 | 380 | ||
385 | lock_sock(sk); | 381 | lock_sock(sk); |
386 | if (!ctx->more && ctx->used) | 382 | if (!ctx->more && ctx->used) |
@@ -389,16 +385,10 @@ static ssize_t skcipher_sendpage(struct socket *sock, struct page *page, | |||
389 | if (!size) | 385 | if (!size) |
390 | goto done; | 386 | goto done; |
391 | 387 | ||
392 | limit = max_t(int, sk->sk_sndbuf, PAGE_SIZE); | 388 | if (!skcipher_writable(sk)) { |
393 | limit -= ctx->used; | ||
394 | |||
395 | if (limit < PAGE_SIZE) { | ||
396 | err = skcipher_wait_for_wmem(sk, flags); | 389 | err = skcipher_wait_for_wmem(sk, flags); |
397 | if (err) | 390 | if (err) |
398 | goto unlock; | 391 | goto unlock; |
399 | |||
400 | limit = max_t(int, sk->sk_sndbuf, PAGE_SIZE); | ||
401 | limit -= ctx->used; | ||
402 | } | 392 | } |
403 | 393 | ||
404 | err = skcipher_alloc_sgl(sk); | 394 | err = skcipher_alloc_sgl(sk); |