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 | |
| 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')
| -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); |
