| From: Eric Biggers <ebiggers@google.com> |
| Date: Thu, 6 Dec 2018 15:55:41 -0800 |
| Subject: crypto: user - support incremental algorithm dumps |
| |
| commit 0ac6b8fb23c724b015d9ca70a89126e8d1563166 upstream. |
| |
| CRYPTO_MSG_GETALG in NLM_F_DUMP mode sometimes doesn't return all |
| registered crypto algorithms, because it doesn't support incremental |
| dumps. crypto_dump_report() only permits itself to be called once, yet |
| the netlink subsystem allocates at most ~64 KiB for the skb being dumped |
| to. Thus only the first recvmsg() returns data, and it may only include |
| a subset of the crypto algorithms even if the user buffer passed to |
| recvmsg() is large enough to hold all of them. |
| |
| Fix this by using one of the arguments in the netlink_callback structure |
| to keep track of the current position in the algorithm list. Then |
| userspace can do multiple recvmsg() on the socket after sending the dump |
| request. This is the way netlink dumps work elsewhere in the kernel; |
| it's unclear why this was different (probably just an oversight). |
| |
| Also fix an integer overflow when calculating the dump buffer size hint. |
| |
| Fixes: a38f7907b926 ("crypto: Add userspace configuration API") |
| Signed-off-by: Eric Biggers <ebiggers@google.com> |
| Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> |
| [bwh: Backported to 3.16: adjust filename] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| crypto/crypto_user.c | 37 ++++++++++++++++++++----------------- |
| 1 file changed, 20 insertions(+), 17 deletions(-) |
| |
| --- a/crypto/crypto_user.c |
| +++ b/crypto/crypto_user.c |
| @@ -226,30 +226,33 @@ static int crypto_report(struct sk_buff |
| |
| static int crypto_dump_report(struct sk_buff *skb, struct netlink_callback *cb) |
| { |
| - struct crypto_alg *alg; |
| + const size_t start_pos = cb->args[0]; |
| + size_t pos = 0; |
| struct crypto_dump_info info; |
| - int err; |
| - |
| - if (cb->args[0]) |
| - goto out; |
| - |
| - cb->args[0] = 1; |
| + struct crypto_alg *alg; |
| + int res; |
| |
| info.in_skb = cb->skb; |
| info.out_skb = skb; |
| info.nlmsg_seq = cb->nlh->nlmsg_seq; |
| info.nlmsg_flags = NLM_F_MULTI; |
| |
| + down_read(&crypto_alg_sem); |
| list_for_each_entry(alg, &crypto_alg_list, cra_list) { |
| - err = crypto_report_alg(alg, &info); |
| - if (err) |
| - goto out_err; |
| + if (pos >= start_pos) { |
| + res = crypto_report_alg(alg, &info); |
| + if (res == -EMSGSIZE) |
| + break; |
| + if (res) |
| + goto out; |
| + } |
| + pos++; |
| } |
| - |
| + cb->args[0] = pos; |
| + res = skb->len; |
| out: |
| - return skb->len; |
| -out_err: |
| - return err; |
| + up_read(&crypto_alg_sem); |
| + return res; |
| } |
| |
| static int crypto_dump_report_done(struct netlink_callback *cb) |
| @@ -478,7 +481,7 @@ static int crypto_user_rcv_msg(struct sk |
| if ((type == (CRYPTO_MSG_GETALG - CRYPTO_MSG_BASE) && |
| (nlh->nlmsg_flags & NLM_F_DUMP))) { |
| struct crypto_alg *alg; |
| - u16 dump_alloc = 0; |
| + unsigned long dump_alloc = 0; |
| |
| if (link->dump == NULL) |
| return -EINVAL; |
| @@ -486,16 +489,16 @@ static int crypto_user_rcv_msg(struct sk |
| down_read(&crypto_alg_sem); |
| list_for_each_entry(alg, &crypto_alg_list, cra_list) |
| dump_alloc += CRYPTO_REPORT_MAXSIZE; |
| + up_read(&crypto_alg_sem); |
| |
| { |
| struct netlink_dump_control c = { |
| .dump = link->dump, |
| .done = link->done, |
| - .min_dump_alloc = dump_alloc, |
| + .min_dump_alloc = min(dump_alloc, 65535UL), |
| }; |
| err = netlink_dump_start(crypto_nlsk, skb, nlh, &c); |
| } |
| - up_read(&crypto_alg_sem); |
| |
| return err; |
| } |