From dd504589577d8e8e70f51f997ad487a4cb6c026f Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 25 Dec 2015 15:40:05 +0800 Subject: crypto: algif_skcipher - Require setkey before accept(2) Some cipher implementations will crash if you try to use them without calling setkey first. This patch adds a check so that the accept(2) call will fail with -ENOKEY if setkey hasn't been done on the socket yet. Cc: stable@vger.kernel.org Reported-by: Dmitry Vyukov Signed-off-by: Herbert Xu Tested-by: Dmitry Vyukov --- crypto/algif_skcipher.c | 48 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) (limited to 'crypto') diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 5c756b30e79b..f4431bc1ce43 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -31,6 +31,11 @@ struct skcipher_sg_list { struct scatterlist sg[0]; }; +struct skcipher_tfm { + struct crypto_skcipher *skcipher; + bool has_key; +}; + struct skcipher_ctx { struct list_head tsgl; struct af_alg_sgl rsgl; @@ -750,17 +755,41 @@ static struct proto_ops algif_skcipher_ops = { static void *skcipher_bind(const char *name, u32 type, u32 mask) { - return crypto_alloc_skcipher(name, type, mask); + struct skcipher_tfm *tfm; + struct crypto_skcipher *skcipher; + + tfm = kzalloc(sizeof(*tfm), GFP_KERNEL); + if (!tfm) + return ERR_PTR(-ENOMEM); + + skcipher = crypto_alloc_skcipher(name, type, mask); + if (IS_ERR(skcipher)) { + kfree(tfm); + return ERR_CAST(skcipher); + } + + tfm->skcipher = skcipher; + + return tfm; } static void skcipher_release(void *private) { - crypto_free_skcipher(private); + struct skcipher_tfm *tfm = private; + + crypto_free_skcipher(tfm->skcipher); + kfree(tfm); } static int skcipher_setkey(void *private, const u8 *key, unsigned int keylen) { - return crypto_skcipher_setkey(private, key, keylen); + struct skcipher_tfm *tfm = private; + int err; + + err = crypto_skcipher_setkey(tfm->skcipher, key, keylen); + tfm->has_key = !err; + + return err; } static void skcipher_wait(struct sock *sk) @@ -792,20 +821,25 @@ static int skcipher_accept_parent(void *private, struct sock *sk) { struct skcipher_ctx *ctx; struct alg_sock *ask = alg_sk(sk); - unsigned int len = sizeof(*ctx) + crypto_skcipher_reqsize(private); + struct skcipher_tfm *tfm = private; + struct crypto_skcipher *skcipher = tfm->skcipher; + unsigned int len = sizeof(*ctx) + crypto_skcipher_reqsize(skcipher); + + if (!tfm->has_key) + return -ENOKEY; ctx = sock_kmalloc(sk, len, GFP_KERNEL); if (!ctx) return -ENOMEM; - ctx->iv = sock_kmalloc(sk, crypto_skcipher_ivsize(private), + ctx->iv = sock_kmalloc(sk, crypto_skcipher_ivsize(skcipher), GFP_KERNEL); if (!ctx->iv) { sock_kfree_s(sk, ctx, len); return -ENOMEM; } - memset(ctx->iv, 0, crypto_skcipher_ivsize(private)); + memset(ctx->iv, 0, crypto_skcipher_ivsize(skcipher)); INIT_LIST_HEAD(&ctx->tsgl); ctx->len = len; @@ -818,7 +852,7 @@ static int skcipher_accept_parent(void *private, struct sock *sk) ask->private = ctx; - skcipher_request_set_tfm(&ctx->req, private); + skcipher_request_set_tfm(&ctx->req, skcipher); skcipher_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG, af_alg_complete, &ctx->completion); -- cgit v1.2.3 From c840ac6af3f8713a71b4d2363419145760bd6044 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 30 Dec 2015 11:47:53 +0800 Subject: crypto: af_alg - Disallow bind/setkey/... after accept(2) Each af_alg parent socket obtained by socket(2) corresponds to a tfm object once bind(2) has succeeded. An accept(2) call on that parent socket creates a context which then uses the tfm object. Therefore as long as any child sockets created by accept(2) exist the parent socket must not be modified or freed. This patch guarantees this by using locks and a reference count on the parent socket. Any attempt to modify the parent socket will fail with EBUSY. Cc: stable@vger.kernel.org Reported-by: Dmitry Vyukov Signed-off-by: Herbert Xu --- crypto/af_alg.c | 35 ++++++++++++++++++++++++++++++++--- include/crypto/if_alg.h | 8 +++----- 2 files changed, 35 insertions(+), 8 deletions(-) (limited to 'crypto') diff --git a/crypto/af_alg.c b/crypto/af_alg.c index a8e7aa3e257b..7b5b5926c767 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -125,6 +125,23 @@ int af_alg_release(struct socket *sock) } EXPORT_SYMBOL_GPL(af_alg_release); +void af_alg_release_parent(struct sock *sk) +{ + struct alg_sock *ask = alg_sk(sk); + bool last; + + sk = ask->parent; + ask = alg_sk(sk); + + lock_sock(sk); + last = !--ask->refcnt; + release_sock(sk); + + if (last) + sock_put(sk); +} +EXPORT_SYMBOL_GPL(af_alg_release_parent); + static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { const u32 forbidden = CRYPTO_ALG_INTERNAL; @@ -133,6 +150,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) struct sockaddr_alg *sa = (void *)uaddr; const struct af_alg_type *type; void *private; + int err; if (sock->state == SS_CONNECTED) return -EINVAL; @@ -160,16 +178,22 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) return PTR_ERR(private); } + err = -EBUSY; lock_sock(sk); + if (ask->refcnt) + goto unlock; swap(ask->type, type); swap(ask->private, private); + err = 0; + +unlock: release_sock(sk); alg_do_release(type, private); - return 0; + return err; } static int alg_setkey(struct sock *sk, char __user *ukey, @@ -202,11 +226,15 @@ static int alg_setsockopt(struct socket *sock, int level, int optname, struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); const struct af_alg_type *type; - int err = -ENOPROTOOPT; + int err = -EBUSY; lock_sock(sk); + if (ask->refcnt) + goto unlock; + type = ask->type; + err = -ENOPROTOOPT; if (level != SOL_ALG || !type) goto unlock; @@ -264,7 +292,8 @@ int af_alg_accept(struct sock *sk, struct socket *newsock) sk2->sk_family = PF_ALG; - sock_hold(sk); + if (!ask->refcnt++) + sock_hold(sk); alg_sk(sk2)->parent = sk; alg_sk(sk2)->type = type; diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h index 018afb264ac2..589716f2ee8a 100644 --- a/include/crypto/if_alg.h +++ b/include/crypto/if_alg.h @@ -30,6 +30,8 @@ struct alg_sock { struct sock *parent; + unsigned int refcnt; + const struct af_alg_type *type; void *private; }; @@ -67,6 +69,7 @@ int af_alg_register_type(const struct af_alg_type *type); int af_alg_unregister_type(const struct af_alg_type *type); int af_alg_release(struct socket *sock); +void af_alg_release_parent(struct sock *sk); int af_alg_accept(struct sock *sk, struct socket *newsock); int af_alg_make_sg(struct af_alg_sgl *sgl, struct iov_iter *iter, int len); @@ -83,11 +86,6 @@ static inline struct alg_sock *alg_sk(struct sock *sk) return (struct alg_sock *)sk; } -static inline void af_alg_release_parent(struct sock *sk) -{ - sock_put(alg_sk(sk)->parent); -} - static inline void af_alg_init_completion(struct af_alg_completion *completion) { init_completion(&completion->completion); -- cgit v1.2.3 From a383292c86663bbc31ac62cc0c04fc77504636a6 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 30 Dec 2015 20:24:17 +0800 Subject: crypto: af_alg - Fix socket double-free when accept fails When we fail an accept(2) call we will end up freeing the socket twice, once due to the direct sk_free call and once again through newsock. This patch fixes this by removing the sk_free call. Cc: stable@vger.kernel.org Reported-by: Dmitry Vyukov Signed-off-by: Herbert Xu --- crypto/af_alg.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'crypto') diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 7b5b5926c767..eaf98e287d89 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -285,10 +285,8 @@ int af_alg_accept(struct sock *sk, struct socket *newsock) security_sk_clone(sk, sk2); err = type->accept(ask->private, sk2); - if (err) { - sk_free(sk2); + if (err) goto unlock; - } sk2->sk_family = PF_ALG; -- cgit v1.2.3 From 37766586c965d63758ad542325a96d5384f4a8c9 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 4 Jan 2016 13:35:18 +0900 Subject: crypto: af_alg - Add nokey compatibility path This patch adds a compatibility path to support old applications that do acept(2) before setkey. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/af_alg.c | 13 ++++++++++++- include/crypto/if_alg.h | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'crypto') diff --git a/crypto/af_alg.c b/crypto/af_alg.c index eaf98e287d89..6566d2eb0142 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -76,6 +76,8 @@ int af_alg_register_type(const struct af_alg_type *type) goto unlock; type->ops->owner = THIS_MODULE; + if (type->ops_nokey) + type->ops_nokey->owner = THIS_MODULE; node->type = type; list_add(&node->list, &alg_types); err = 0; @@ -267,6 +269,7 @@ int af_alg_accept(struct sock *sk, struct socket *newsock) const struct af_alg_type *type; struct sock *sk2; int err; + bool nokey; lock_sock(sk); type = ask->type; @@ -285,12 +288,17 @@ int af_alg_accept(struct sock *sk, struct socket *newsock) security_sk_clone(sk, sk2); err = type->accept(ask->private, sk2); + + nokey = err == -ENOKEY; + if (nokey && type->accept_nokey) + err = type->accept_nokey(ask->private, sk2); + if (err) goto unlock; sk2->sk_family = PF_ALG; - if (!ask->refcnt++) + if (nokey || !ask->refcnt++) sock_hold(sk); alg_sk(sk2)->parent = sk; alg_sk(sk2)->type = type; @@ -298,6 +306,9 @@ int af_alg_accept(struct sock *sk, struct socket *newsock) newsock->ops = type->ops; newsock->state = SS_CONNECTED; + if (nokey) + newsock->ops = type->ops_nokey; + err = 0; unlock: diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h index 589716f2ee8a..df8284415c56 100644 --- a/include/crypto/if_alg.h +++ b/include/crypto/if_alg.h @@ -52,9 +52,11 @@ struct af_alg_type { void (*release)(void *private); int (*setkey)(void *private, const u8 *key, unsigned int keylen); int (*accept)(void *private, struct sock *sk); + int (*accept_nokey)(void *private, struct sock *sk); int (*setauthsize)(void *private, unsigned int authsize); struct proto_ops *ops; + struct proto_ops *ops_nokey; struct module *owner; char name[14]; }; -- cgit v1.2.3 From a0fa2d037129a9849918a92d91b79ed6c7bd2818 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 4 Jan 2016 13:36:12 +0900 Subject: crypto: algif_skcipher - Add nokey compatibility path This patch adds a compatibility path to support old applications that do acept(2) before setkey. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/algif_skcipher.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 5 deletions(-) (limited to 'crypto') diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index f4431bc1ce43..110bab499e43 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -753,6 +753,99 @@ static struct proto_ops algif_skcipher_ops = { .poll = skcipher_poll, }; +static int skcipher_check_key(struct socket *sock) +{ + int err; + struct sock *psk; + struct alg_sock *pask; + struct skcipher_tfm *tfm; + struct sock *sk = sock->sk; + struct alg_sock *ask = alg_sk(sk); + + if (ask->refcnt) + return 0; + + psk = ask->parent; + pask = alg_sk(ask->parent); + tfm = pask->private; + + err = -ENOKEY; + lock_sock(psk); + if (!tfm->has_key) + goto unlock; + + if (!pask->refcnt++) + sock_hold(psk); + + ask->refcnt = 1; + sock_put(psk); + + err = 0; + +unlock: + release_sock(psk); + + return err; +} + +static int skcipher_sendmsg_nokey(struct socket *sock, struct msghdr *msg, + size_t size) +{ + int err; + + err = skcipher_check_key(sock); + if (err) + return err; + + return skcipher_sendmsg(sock, msg, size); +} + +static ssize_t skcipher_sendpage_nokey(struct socket *sock, struct page *page, + int offset, size_t size, int flags) +{ + int err; + + err = skcipher_check_key(sock); + if (err) + return err; + + return skcipher_sendpage(sock, page, offset, size, flags); +} + +static int skcipher_recvmsg_nokey(struct socket *sock, struct msghdr *msg, + size_t ignored, int flags) +{ + int err; + + err = skcipher_check_key(sock); + if (err) + return err; + + return skcipher_recvmsg(sock, msg, ignored, flags); +} + +static struct proto_ops algif_skcipher_ops_nokey = { + .family = PF_ALG, + + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .getname = sock_no_getname, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .getsockopt = sock_no_getsockopt, + .mmap = sock_no_mmap, + .bind = sock_no_bind, + .accept = sock_no_accept, + .setsockopt = sock_no_setsockopt, + + .release = af_alg_release, + .sendmsg = skcipher_sendmsg_nokey, + .sendpage = skcipher_sendpage_nokey, + .recvmsg = skcipher_recvmsg_nokey, + .poll = skcipher_poll, +}; + static void *skcipher_bind(const char *name, u32 type, u32 mask) { struct skcipher_tfm *tfm; @@ -802,7 +895,7 @@ static void skcipher_wait(struct sock *sk) msleep(100); } -static void skcipher_sock_destruct(struct sock *sk) +static void skcipher_sock_destruct_common(struct sock *sk) { struct alg_sock *ask = alg_sk(sk); struct skcipher_ctx *ctx = ask->private; @@ -814,10 +907,33 @@ static void skcipher_sock_destruct(struct sock *sk) skcipher_free_sgl(sk); sock_kzfree_s(sk, ctx->iv, crypto_skcipher_ivsize(tfm)); sock_kfree_s(sk, ctx, ctx->len); +} + +static void skcipher_sock_destruct(struct sock *sk) +{ + skcipher_sock_destruct_common(sk); af_alg_release_parent(sk); } -static int skcipher_accept_parent(void *private, struct sock *sk) +static void skcipher_release_parent_nokey(struct sock *sk) +{ + struct alg_sock *ask = alg_sk(sk); + + if (!ask->refcnt) { + sock_put(ask->parent); + return; + } + + af_alg_release_parent(sk); +} + +static void skcipher_sock_destruct_nokey(struct sock *sk) +{ + skcipher_sock_destruct_common(sk); + skcipher_release_parent_nokey(sk); +} + +static int skcipher_accept_parent_common(void *private, struct sock *sk) { struct skcipher_ctx *ctx; struct alg_sock *ask = alg_sk(sk); @@ -825,9 +941,6 @@ static int skcipher_accept_parent(void *private, struct sock *sk) struct crypto_skcipher *skcipher = tfm->skcipher; unsigned int len = sizeof(*ctx) + crypto_skcipher_reqsize(skcipher); - if (!tfm->has_key) - return -ENOKEY; - ctx = sock_kmalloc(sk, len, GFP_KERNEL); if (!ctx) return -ENOMEM; @@ -861,12 +974,38 @@ static int skcipher_accept_parent(void *private, struct sock *sk) return 0; } +static int skcipher_accept_parent(void *private, struct sock *sk) +{ + struct skcipher_tfm *tfm = private; + + if (!tfm->has_key) + return -ENOKEY; + + return skcipher_accept_parent_common(private, sk); +} + +static int skcipher_accept_parent_nokey(void *private, struct sock *sk) +{ + int err; + + err = skcipher_accept_parent_common(private, sk); + if (err) + goto out; + + sk->sk_destruct = skcipher_sock_destruct_nokey; + +out: + return err; +} + static const struct af_alg_type algif_type_skcipher = { .bind = skcipher_bind, .release = skcipher_release, .setkey = skcipher_setkey, .accept = skcipher_accept_parent, + .accept_nokey = skcipher_accept_parent_nokey, .ops = &algif_skcipher_ops, + .ops_nokey = &algif_skcipher_ops_nokey, .name = "skcipher", .owner = THIS_MODULE }; -- cgit v1.2.3 From a5596d6332787fd383b3b5427b41f94254430827 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 8 Jan 2016 21:28:26 +0800 Subject: crypto: hash - Add crypto_ahash_has_setkey This patch adds a way for ahash users to determine whether a key is required by a crypto_ahash transform. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/ahash.c | 5 ++++- crypto/shash.c | 4 +++- include/crypto/hash.h | 6 ++++++ 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'crypto') diff --git a/crypto/ahash.c b/crypto/ahash.c index 9c1dc8d6106a..d19b52324cf5 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -451,6 +451,7 @@ static int crypto_ahash_init_tfm(struct crypto_tfm *tfm) struct ahash_alg *alg = crypto_ahash_alg(hash); hash->setkey = ahash_nosetkey; + hash->has_setkey = false; hash->export = ahash_no_export; hash->import = ahash_no_import; @@ -463,8 +464,10 @@ static int crypto_ahash_init_tfm(struct crypto_tfm *tfm) hash->finup = alg->finup ?: ahash_def_finup; hash->digest = alg->digest; - if (alg->setkey) + if (alg->setkey) { hash->setkey = alg->setkey; + hash->has_setkey = true; + } if (alg->export) hash->export = alg->export; if (alg->import) diff --git a/crypto/shash.c b/crypto/shash.c index ecb1e3d39bf0..88a27de79848 100644 --- a/crypto/shash.c +++ b/crypto/shash.c @@ -355,8 +355,10 @@ int crypto_init_shash_ops_async(struct crypto_tfm *tfm) crt->finup = shash_async_finup; crt->digest = shash_async_digest; - if (alg->setkey) + if (alg->setkey) { crt->setkey = shash_async_setkey; + crt->has_setkey = true; + } if (alg->export) crt->export = shash_async_export; if (alg->import) diff --git a/include/crypto/hash.h b/include/crypto/hash.h index 3d69c93d50e8..6361892ea737 100644 --- a/include/crypto/hash.h +++ b/include/crypto/hash.h @@ -204,6 +204,7 @@ struct crypto_ahash { unsigned int keylen); unsigned int reqsize; + bool has_setkey; struct crypto_tfm base; }; @@ -375,6 +376,11 @@ static inline void *ahash_request_ctx(struct ahash_request *req) int crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key, unsigned int keylen); +static inline bool crypto_ahash_has_setkey(struct crypto_ahash *tfm) +{ + return tfm->has_setkey; +} + /** * crypto_ahash_finup() - update and finalize message digest * @req: reference to the ahash_request handle that holds all information -- cgit v1.2.3 From 6de62f15b581f920ade22d758f4c338311c2f0d4 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 8 Jan 2016 21:31:04 +0800 Subject: crypto: algif_hash - Require setkey before accept(2) Hash implementations that require a key may crash if you use them without setting a key. This patch adds the necessary checks so that if you do attempt to use them without a key that we return -ENOKEY instead of proceeding. This patch also adds a compatibility path to support old applications that do acept(2) before setkey. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/algif_hash.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 193 insertions(+), 8 deletions(-) (limited to 'crypto') diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c index b4c24fe3dcfb..46637bed4db8 100644 --- a/crypto/algif_hash.c +++ b/crypto/algif_hash.c @@ -34,6 +34,11 @@ struct hash_ctx { struct ahash_request req; }; +struct algif_hash_tfm { + struct crypto_ahash *hash; + bool has_key; +}; + static int hash_sendmsg(struct socket *sock, struct msghdr *msg, size_t ignored) { @@ -235,22 +240,151 @@ static struct proto_ops algif_hash_ops = { .accept = hash_accept, }; +static int hash_check_key(struct socket *sock) +{ + int err; + struct sock *psk; + struct alg_sock *pask; + struct algif_hash_tfm *tfm; + struct sock *sk = sock->sk; + struct alg_sock *ask = alg_sk(sk); + + if (ask->refcnt) + return 0; + + psk = ask->parent; + pask = alg_sk(ask->parent); + tfm = pask->private; + + err = -ENOKEY; + lock_sock(psk); + if (!tfm->has_key) + goto unlock; + + if (!pask->refcnt++) + sock_hold(psk); + + ask->refcnt = 1; + sock_put(psk); + + err = 0; + +unlock: + release_sock(psk); + + return err; +} + +static int hash_sendmsg_nokey(struct socket *sock, struct msghdr *msg, + size_t size) +{ + int err; + + err = hash_check_key(sock); + if (err) + return err; + + return hash_sendmsg(sock, msg, size); +} + +static ssize_t hash_sendpage_nokey(struct socket *sock, struct page *page, + int offset, size_t size, int flags) +{ + int err; + + err = hash_check_key(sock); + if (err) + return err; + + return hash_sendpage(sock, page, offset, size, flags); +} + +static int hash_recvmsg_nokey(struct socket *sock, struct msghdr *msg, + size_t ignored, int flags) +{ + int err; + + err = hash_check_key(sock); + if (err) + return err; + + return hash_recvmsg(sock, msg, ignored, flags); +} + +static int hash_accept_nokey(struct socket *sock, struct socket *newsock, + int flags) +{ + int err; + + err = hash_check_key(sock); + if (err) + return err; + + return hash_accept(sock, newsock, flags); +} + +static struct proto_ops algif_hash_ops_nokey = { + .family = PF_ALG, + + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .getname = sock_no_getname, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .getsockopt = sock_no_getsockopt, + .mmap = sock_no_mmap, + .bind = sock_no_bind, + .setsockopt = sock_no_setsockopt, + .poll = sock_no_poll, + + .release = af_alg_release, + .sendmsg = hash_sendmsg_nokey, + .sendpage = hash_sendpage_nokey, + .recvmsg = hash_recvmsg_nokey, + .accept = hash_accept_nokey, +}; + static void *hash_bind(const char *name, u32 type, u32 mask) { - return crypto_alloc_ahash(name, type, mask); + struct algif_hash_tfm *tfm; + struct crypto_ahash *hash; + + tfm = kzalloc(sizeof(*tfm), GFP_KERNEL); + if (!tfm) + return ERR_PTR(-ENOMEM); + + hash = crypto_alloc_ahash(name, type, mask); + if (IS_ERR(hash)) { + kfree(tfm); + return ERR_CAST(hash); + } + + tfm->hash = hash; + + return tfm; } static void hash_release(void *private) { - crypto_free_ahash(private); + struct algif_hash_tfm *tfm = private; + + crypto_free_ahash(tfm->hash); + kfree(tfm); } static int hash_setkey(void *private, const u8 *key, unsigned int keylen) { - return crypto_ahash_setkey(private, key, keylen); + struct algif_hash_tfm *tfm = private; + int err; + + err = crypto_ahash_setkey(tfm->hash, key, keylen); + tfm->has_key = !err; + + return err; } -static void hash_sock_destruct(struct sock *sk) +static void hash_sock_destruct_common(struct sock *sk) { struct alg_sock *ask = alg_sk(sk); struct hash_ctx *ctx = ask->private; @@ -258,15 +392,40 @@ static void hash_sock_destruct(struct sock *sk) sock_kzfree_s(sk, ctx->result, crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req))); sock_kfree_s(sk, ctx, ctx->len); +} + +static void hash_sock_destruct(struct sock *sk) +{ + hash_sock_destruct_common(sk); af_alg_release_parent(sk); } -static int hash_accept_parent(void *private, struct sock *sk) +static void hash_release_parent_nokey(struct sock *sk) +{ + struct alg_sock *ask = alg_sk(sk); + + if (!ask->refcnt) { + sock_put(ask->parent); + return; + } + + af_alg_release_parent(sk); +} + +static void hash_sock_destruct_nokey(struct sock *sk) +{ + hash_sock_destruct_common(sk); + hash_release_parent_nokey(sk); +} + +static int hash_accept_parent_common(void *private, struct sock *sk) { struct hash_ctx *ctx; struct alg_sock *ask = alg_sk(sk); - unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(private); - unsigned ds = crypto_ahash_digestsize(private); + struct algif_hash_tfm *tfm = private; + struct crypto_ahash *hash = tfm->hash; + unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(hash); + unsigned ds = crypto_ahash_digestsize(hash); ctx = sock_kmalloc(sk, len, GFP_KERNEL); if (!ctx) @@ -286,7 +445,7 @@ static int hash_accept_parent(void *private, struct sock *sk) ask->private = ctx; - ahash_request_set_tfm(&ctx->req, private); + ahash_request_set_tfm(&ctx->req, hash); ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG, af_alg_complete, &ctx->completion); @@ -295,12 +454,38 @@ static int hash_accept_parent(void *private, struct sock *sk) return 0; } +static int hash_accept_parent(void *private, struct sock *sk) +{ + struct algif_hash_tfm *tfm = private; + + if (!tfm->has_key && crypto_ahash_has_setkey(tfm->hash)) + return -ENOKEY; + + return hash_accept_parent_common(private, sk); +} + +static int hash_accept_parent_nokey(void *private, struct sock *sk) +{ + int err; + + err = hash_accept_parent_common(private, sk); + if (err) + goto out; + + sk->sk_destruct = hash_sock_destruct_nokey; + +out: + return err; +} + static const struct af_alg_type algif_type_hash = { .bind = hash_bind, .release = hash_release, .setkey = hash_setkey, .accept = hash_accept_parent, + .accept_nokey = hash_accept_parent_nokey, .ops = &algif_hash_ops, + .ops_nokey = &algif_hash_ops_nokey, .name = "hash", .owner = THIS_MODULE }; -- cgit v1.2.3 From a1383cd86a062fc798899ab20f0ec2116cce39cb Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 11 Jan 2016 21:26:50 +0800 Subject: crypto: skcipher - Add crypto_skcipher_has_setkey This patch adds a way for skcipher users to determine whether a key is required by a transform. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/skcipher.c | 2 ++ include/crypto/skcipher.h | 7 +++++++ 2 files changed, 9 insertions(+) (limited to 'crypto') diff --git a/crypto/skcipher.c b/crypto/skcipher.c index 7591928be7ca..d199c0b1751c 100644 --- a/crypto/skcipher.c +++ b/crypto/skcipher.c @@ -118,6 +118,7 @@ static int crypto_init_skcipher_ops_blkcipher(struct crypto_tfm *tfm) skcipher->decrypt = skcipher_decrypt_blkcipher; skcipher->ivsize = crypto_blkcipher_ivsize(blkcipher); + skcipher->has_setkey = calg->cra_blkcipher.max_keysize; return 0; } @@ -210,6 +211,7 @@ static int crypto_init_skcipher_ops_ablkcipher(struct crypto_tfm *tfm) skcipher->ivsize = crypto_ablkcipher_ivsize(ablkcipher); skcipher->reqsize = crypto_ablkcipher_reqsize(ablkcipher) + sizeof(struct ablkcipher_request); + skcipher->has_setkey = calg->cra_ablkcipher.max_keysize; return 0; } diff --git a/include/crypto/skcipher.h b/include/crypto/skcipher.h index d8dd41fb034f..fd8742a40ff3 100644 --- a/include/crypto/skcipher.h +++ b/include/crypto/skcipher.h @@ -61,6 +61,8 @@ struct crypto_skcipher { unsigned int ivsize; unsigned int reqsize; + bool has_setkey; + struct crypto_tfm base; }; @@ -305,6 +307,11 @@ static inline int crypto_skcipher_setkey(struct crypto_skcipher *tfm, return tfm->setkey(tfm, key, keylen); } +static inline bool crypto_skcipher_has_setkey(struct crypto_skcipher *tfm) +{ + return tfm->has_setkey; +} + /** * crypto_skcipher_reqtfm() - obtain cipher handle from request * @req: skcipher_request out of which the cipher handle is to be obtained -- cgit v1.2.3 From 6e8d8ecf438792ecf7a3207488fb4eebc4edb040 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 11 Jan 2016 21:29:41 +0800 Subject: crypto: algif_skcipher - Add key check exception for cipher_null This patch adds an exception to the key check so that cipher_null users may continue to use algif_skcipher without setting a key. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/algif_skcipher.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crypto') diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 110bab499e43..4a5bdb69fd49 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -978,7 +978,7 @@ static int skcipher_accept_parent(void *private, struct sock *sk) { struct skcipher_tfm *tfm = private; - if (!tfm->has_key) + if (!tfm->has_key && crypto_skcipher_has_setkey(tfm->skcipher)) return -ENOKEY; return skcipher_accept_parent_common(private, sk); -- cgit v1.2.3 From 6a935170a980024dd29199e9dbb5c4da4767a1b9 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 13 Jan 2016 14:59:03 +0800 Subject: crypto: af_alg - Allow af_af_alg_release_parent to be called on nokey path This patch allows af_alg_release_parent to be called even for nokey sockets. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/af_alg.c | 9 ++++++++- include/crypto/if_alg.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'crypto') diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 6566d2eb0142..e7cb8367771d 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -133,6 +133,12 @@ void af_alg_release_parent(struct sock *sk) bool last; sk = ask->parent; + + if (ask->nokey_refcnt && !ask->refcnt) { + sock_put(sk); + return; + } + ask = alg_sk(sk); lock_sock(sk); @@ -268,8 +274,8 @@ int af_alg_accept(struct sock *sk, struct socket *newsock) struct alg_sock *ask = alg_sk(sk); const struct af_alg_type *type; struct sock *sk2; + unsigned int nokey; int err; - bool nokey; lock_sock(sk); type = ask->type; @@ -302,6 +308,7 @@ int af_alg_accept(struct sock *sk, struct socket *newsock) sock_hold(sk); alg_sk(sk2)->parent = sk; alg_sk(sk2)->type = type; + alg_sk(sk2)->nokey_refcnt = nokey; newsock->ops = type->ops; newsock->state = SS_CONNECTED; diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h index df8284415c56..a2bfd7843f18 100644 --- a/include/crypto/if_alg.h +++ b/include/crypto/if_alg.h @@ -31,6 +31,7 @@ struct alg_sock { struct sock *parent; unsigned int refcnt; + unsigned int nokey_refcnt; const struct af_alg_type *type; void *private; -- cgit v1.2.3 From f1d84af1835846a5a2b827382c5848faf2bb0e75 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 13 Jan 2016 15:00:36 +0800 Subject: crypto: algif_hash - Remove custom release parent function This patch removes the custom release parent function as the generic af_alg_release_parent now works for nokey sockets too. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/algif_hash.c | 43 +++---------------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) (limited to 'crypto') diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c index 46637bed4db8..3653ab60ec79 100644 --- a/crypto/algif_hash.c +++ b/crypto/algif_hash.c @@ -384,7 +384,7 @@ static int hash_setkey(void *private, const u8 *key, unsigned int keylen) return err; } -static void hash_sock_destruct_common(struct sock *sk) +static void hash_sock_destruct(struct sock *sk) { struct alg_sock *ask = alg_sk(sk); struct hash_ctx *ctx = ask->private; @@ -392,33 +392,10 @@ static void hash_sock_destruct_common(struct sock *sk) sock_kzfree_s(sk, ctx->result, crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req))); sock_kfree_s(sk, ctx, ctx->len); -} - -static void hash_sock_destruct(struct sock *sk) -{ - hash_sock_destruct_common(sk); - af_alg_release_parent(sk); -} - -static void hash_release_parent_nokey(struct sock *sk) -{ - struct alg_sock *ask = alg_sk(sk); - - if (!ask->refcnt) { - sock_put(ask->parent); - return; - } - af_alg_release_parent(sk); } -static void hash_sock_destruct_nokey(struct sock *sk) -{ - hash_sock_destruct_common(sk); - hash_release_parent_nokey(sk); -} - -static int hash_accept_parent_common(void *private, struct sock *sk) +static int hash_accept_parent_nokey(void *private, struct sock *sk) { struct hash_ctx *ctx; struct alg_sock *ask = alg_sk(sk); @@ -461,21 +438,7 @@ static int hash_accept_parent(void *private, struct sock *sk) if (!tfm->has_key && crypto_ahash_has_setkey(tfm->hash)) return -ENOKEY; - return hash_accept_parent_common(private, sk); -} - -static int hash_accept_parent_nokey(void *private, struct sock *sk) -{ - int err; - - err = hash_accept_parent_common(private, sk); - if (err) - goto out; - - sk->sk_destruct = hash_sock_destruct_nokey; - -out: - return err; + return hash_accept_parent_nokey(private, sk); } static const struct af_alg_type algif_type_hash = { -- cgit v1.2.3 From d7b65aee1e7b4c87922b0232eaba56a8a143a4a0 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 13 Jan 2016 15:01:06 +0800 Subject: crypto: algif_skcipher - Remove custom release parent function This patch removes the custom release parent function as the generic af_alg_release_parent now works for nokey sockets too. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/algif_skcipher.c | 43 +++---------------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) (limited to 'crypto') diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 4a5bdb69fd49..1f99d2d47715 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -895,7 +895,7 @@ static void skcipher_wait(struct sock *sk) msleep(100); } -static void skcipher_sock_destruct_common(struct sock *sk) +static void skcipher_sock_destruct(struct sock *sk) { struct alg_sock *ask = alg_sk(sk); struct skcipher_ctx *ctx = ask->private; @@ -907,33 +907,10 @@ static void skcipher_sock_destruct_common(struct sock *sk) skcipher_free_sgl(sk); sock_kzfree_s(sk, ctx->iv, crypto_skcipher_ivsize(tfm)); sock_kfree_s(sk, ctx, ctx->len); -} - -static void skcipher_sock_destruct(struct sock *sk) -{ - skcipher_sock_destruct_common(sk); - af_alg_release_parent(sk); -} - -static void skcipher_release_parent_nokey(struct sock *sk) -{ - struct alg_sock *ask = alg_sk(sk); - - if (!ask->refcnt) { - sock_put(ask->parent); - return; - } - af_alg_release_parent(sk); } -static void skcipher_sock_destruct_nokey(struct sock *sk) -{ - skcipher_sock_destruct_common(sk); - skcipher_release_parent_nokey(sk); -} - -static int skcipher_accept_parent_common(void *private, struct sock *sk) +static int skcipher_accept_parent_nokey(void *private, struct sock *sk) { struct skcipher_ctx *ctx; struct alg_sock *ask = alg_sk(sk); @@ -981,21 +958,7 @@ static int skcipher_accept_parent(void *private, struct sock *sk) if (!tfm->has_key && crypto_skcipher_has_setkey(tfm->skcipher)) return -ENOKEY; - return skcipher_accept_parent_common(private, sk); -} - -static int skcipher_accept_parent_nokey(void *private, struct sock *sk) -{ - int err; - - err = skcipher_accept_parent_common(private, sk); - if (err) - goto out; - - sk->sk_destruct = skcipher_sock_destruct_nokey; - -out: - return err; + return skcipher_accept_parent_nokey(private, sk); } static const struct af_alg_type algif_type_skcipher = { -- cgit v1.2.3 From a6a48c565f6f112c6983e2a02b1602189ed6e26e Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 13 Jan 2016 15:03:32 +0800 Subject: crypto: af_alg - Forbid bind(2) when nokey child sockets are present This patch forbids the calling of bind(2) when there are child sockets created by accept(2) in existence, even if they are created on the nokey path. This is needed as those child sockets have references to the tfm object which bind(2) will destroy. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/af_alg.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'crypto') diff --git a/crypto/af_alg.c b/crypto/af_alg.c index e7cb8367771d..f5e18c2a4852 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -130,19 +130,16 @@ EXPORT_SYMBOL_GPL(af_alg_release); void af_alg_release_parent(struct sock *sk) { struct alg_sock *ask = alg_sk(sk); - bool last; + unsigned int nokey = ask->nokey_refcnt; + bool last = nokey && !ask->refcnt; sk = ask->parent; - - if (ask->nokey_refcnt && !ask->refcnt) { - sock_put(sk); - return; - } - ask = alg_sk(sk); lock_sock(sk); - last = !--ask->refcnt; + ask->nokey_refcnt -= nokey; + if (!last) + last = !--ask->refcnt; release_sock(sk); if (last) @@ -188,7 +185,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) err = -EBUSY; lock_sock(sk); - if (ask->refcnt) + if (ask->refcnt | ask->nokey_refcnt) goto unlock; swap(ask->type, type); @@ -306,6 +303,7 @@ int af_alg_accept(struct sock *sk, struct socket *newsock) if (nokey || !ask->refcnt++) sock_hold(sk); + ask->nokey_refcnt += nokey; alg_sk(sk2)->parent = sk; alg_sk(sk2)->type = type; alg_sk(sk2)->nokey_refcnt = nokey; -- cgit v1.2.3 From ad46d7e33219218605ea619e32553daf4f346b9f Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 15 Jan 2016 22:01:08 +0800 Subject: crypto: algif_hash - Fix race condition in hash_check_key We need to lock the child socket in hash_check_key as otherwise two simultaneous calls can cause the parent socket to be freed. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/algif_hash.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'crypto') diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c index 3653ab60ec79..608a7562839d 100644 --- a/crypto/algif_hash.c +++ b/crypto/algif_hash.c @@ -242,22 +242,23 @@ static struct proto_ops algif_hash_ops = { static int hash_check_key(struct socket *sock) { - int err; + int err = 0; struct sock *psk; struct alg_sock *pask; struct algif_hash_tfm *tfm; struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); + lock_sock(sk); if (ask->refcnt) - return 0; + goto unlock_child; psk = ask->parent; pask = alg_sk(ask->parent); tfm = pask->private; err = -ENOKEY; - lock_sock(psk); + lock_sock_nested(psk, SINGLE_DEPTH_NESTING); if (!tfm->has_key) goto unlock; @@ -271,6 +272,8 @@ static int hash_check_key(struct socket *sock) unlock: release_sock(psk); +unlock_child: + release_sock(sk); return err; } -- cgit v1.2.3 From 1822793a523e5d5730b19cc21160ff1717421bc8 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 15 Jan 2016 22:02:20 +0800 Subject: crypto: algif_skcipher - Fix race condition in skcipher_check_key We need to lock the child socket in skcipher_check_key as otherwise two simultaneous calls can cause the parent socket to be freed. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/algif_skcipher.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'crypto') diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 1f99d2d47715..dfff8b0b56df 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -755,22 +755,23 @@ static struct proto_ops algif_skcipher_ops = { static int skcipher_check_key(struct socket *sock) { - int err; + int err = 0; struct sock *psk; struct alg_sock *pask; struct skcipher_tfm *tfm; struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); + lock_sock(sk); if (ask->refcnt) - return 0; + goto unlock_child; psk = ask->parent; pask = alg_sk(ask->parent); tfm = pask->private; err = -ENOKEY; - lock_sock(psk); + lock_sock_nested(psk, SINGLE_DEPTH_NESTING); if (!tfm->has_key) goto unlock; @@ -784,6 +785,8 @@ static int skcipher_check_key(struct socket *sock) unlock: release_sock(psk); +unlock_child: + release_sock(sk); return err; } -- cgit v1.2.3 From 4f0414e54e4d1893c6f08260693f8ef84c929293 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 18 Jan 2016 18:46:10 +0800 Subject: crypto: algif_skcipher - Load TX SG list after waiting We need to load the TX SG list in sendmsg(2) after waiting for incoming data, not before. Cc: stable@vger.kernel.org Reported-by: Dmitry Vyukov Signed-off-by: Herbert Xu Tested-by: Dmitry Vyukov --- crypto/algif_skcipher.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'crypto') diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index dfff8b0b56df..df86fb47c3ae 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -647,13 +647,6 @@ static int skcipher_recvmsg_sync(struct socket *sock, struct msghdr *msg, lock_sock(sk); while (msg_data_left(msg)) { - sgl = list_first_entry(&ctx->tsgl, - struct skcipher_sg_list, list); - sg = sgl->sg; - - while (!sg->length) - sg++; - if (!ctx->used) { err = skcipher_wait_for_data(sk, flags); if (err) @@ -674,6 +667,13 @@ static int skcipher_recvmsg_sync(struct socket *sock, struct msghdr *msg, if (!used) goto free; + sgl = list_first_entry(&ctx->tsgl, + struct skcipher_sg_list, list); + sg = sgl->sg; + + while (!sg->length) + sg++; + skcipher_request_set_crypt(&ctx->req, sg, ctx->rsgl.sg, used, ctx->iv); -- cgit v1.2.3 From fd7f6727102a1ccf6b4c1dfcc631f9b546526b26 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 18 Jan 2016 17:06:05 +0100 Subject: crypto: crc32c - Fix crc32c soft dependency I don't think it makes sense for a module to have a soft dependency on itself. This seems quite cyclic by nature and I can't see what purpose it could serve. OTOH libcrc32c calls crypto_alloc_shash("crc32c", 0, 0) so it pretty much assumes that some incarnation of the "crc32c" hash algorithm has been loaded. Therefore it makes sense to have the soft dependency there (as crc-t10dif does.) Cc: stable@vger.kernel.org Cc: Tim Chen Cc: "David S. Miller" Signed-off-by: Jean Delvare Signed-off-by: Herbert Xu --- crypto/crc32c_generic.c | 1 - lib/libcrc32c.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'crypto') diff --git a/crypto/crc32c_generic.c b/crypto/crc32c_generic.c index 06f1b60f02b2..4c0a0e271876 100644 --- a/crypto/crc32c_generic.c +++ b/crypto/crc32c_generic.c @@ -172,4 +172,3 @@ MODULE_DESCRIPTION("CRC32c (Castagnoli) calculations wrapper for lib/crc32c"); MODULE_LICENSE("GPL"); MODULE_ALIAS_CRYPTO("crc32c"); MODULE_ALIAS_CRYPTO("crc32c-generic"); -MODULE_SOFTDEP("pre: crc32c"); diff --git a/lib/libcrc32c.c b/lib/libcrc32c.c index 6a08ce7d6adc..acf9da449f81 100644 --- a/lib/libcrc32c.c +++ b/lib/libcrc32c.c @@ -74,3 +74,4 @@ module_exit(libcrc32c_mod_fini); MODULE_AUTHOR("Clay Haapala "); MODULE_DESCRIPTION("CRC32c (Castagnoli) calculations"); MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: crc32c"); -- cgit v1.2.3 From 202736d99b7f29279db9da61587f11a08a04a9c6 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 19 Jan 2016 21:23:57 +0800 Subject: crypto: algif_skcipher - sendmsg SG marking is off by one We mark the end of the SG list in sendmsg and sendpage and unmark it on the next send call. Unfortunately the unmarking in sendmsg is off-by-one, leading to an SG list that is too short. Fixes: 0f477b655a52 ("crypto: algif - Mark sgl end at the end of data") Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- crypto/algif_skcipher.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'crypto') diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index df86fb47c3ae..a81c10faf9c4 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -392,7 +392,8 @@ static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg, sgl = list_entry(ctx->tsgl.prev, struct skcipher_sg_list, list); sg = sgl->sg; - sg_unmark_end(sg + sgl->cur); + if (sgl->cur) + sg_unmark_end(sg + sgl->cur - 1); do { i = sgl->cur; plen = min_t(size_t, len, PAGE_SIZE); -- cgit v1.2.3