Commit bbefa1dd authored by Herbert Xu's avatar Herbert Xu

crypto: pcrypt - Avoid deadlock by using per-instance padata queues

If the pcrypt template is used multiple times in an algorithm, then a
deadlock occurs because all pcrypt instances share the same
padata_instance, which completes requests in the order submitted.  That
is, the inner pcrypt request waits for the outer pcrypt request while
the outer request is already waiting for the inner.

This patch fixes this by allocating a set of queues for each pcrypt
instance instead of using two global queues.  In order to maintain
the existing user-space interface, the pinst structure remains global
so any sysfs modifications will apply to every pcrypt instance.

Note that when an update occurs we have to allocate memory for
every pcrypt instance.  Should one of the allocations fail we
will abort the update without rolling back changes already made.

The new per-instance data structure is called padata_shell and is
essentially a wrapper around parallel_data.

Reproducer:

	#include <linux/if_alg.h>
	#include <sys/socket.h>
	#include <unistd.h>

	int main()
	{
		struct sockaddr_alg addr = {
			.salg_type = "aead",
			.salg_name = "pcrypt(pcrypt(rfc4106-gcm-aesni))"
		};
		int algfd, reqfd;
		char buf[32] = { 0 };

		algfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
		bind(algfd, (void *)&addr, sizeof(addr));
		setsockopt(algfd, SOL_ALG, ALG_SET_KEY, buf, 20);
		reqfd = accept(algfd, 0, 0);
		write(reqfd, buf, 32);
		read(reqfd, buf, 16);
	}

Reported-by: syzbot+56c7151cad94eec37c521f0e47d2eee53f9361c4@syzkaller.appspotmail.com
Fixes: 5068c7a8 ("crypto: pcrypt - Add pcrypt crypto parallelization wrapper")
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Tested-by: default avatarEric Biggers <ebiggers@kernel.org>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 45a536e3
...@@ -24,6 +24,8 @@ static struct kset *pcrypt_kset; ...@@ -24,6 +24,8 @@ static struct kset *pcrypt_kset;
struct pcrypt_instance_ctx { struct pcrypt_instance_ctx {
struct crypto_aead_spawn spawn; struct crypto_aead_spawn spawn;
struct padata_shell *psenc;
struct padata_shell *psdec;
atomic_t tfm_count; atomic_t tfm_count;
}; };
...@@ -32,6 +34,12 @@ struct pcrypt_aead_ctx { ...@@ -32,6 +34,12 @@ struct pcrypt_aead_ctx {
unsigned int cb_cpu; unsigned int cb_cpu;
}; };
static inline struct pcrypt_instance_ctx *pcrypt_tfm_ictx(
struct crypto_aead *tfm)
{
return aead_instance_ctx(aead_alg_instance(tfm));
}
static int pcrypt_aead_setkey(struct crypto_aead *parent, static int pcrypt_aead_setkey(struct crypto_aead *parent,
const u8 *key, unsigned int keylen) const u8 *key, unsigned int keylen)
{ {
...@@ -90,6 +98,9 @@ static int pcrypt_aead_encrypt(struct aead_request *req) ...@@ -90,6 +98,9 @@ static int pcrypt_aead_encrypt(struct aead_request *req)
struct crypto_aead *aead = crypto_aead_reqtfm(req); struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(aead); struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(aead);
u32 flags = aead_request_flags(req); u32 flags = aead_request_flags(req);
struct pcrypt_instance_ctx *ictx;
ictx = pcrypt_tfm_ictx(aead);
memset(padata, 0, sizeof(struct padata_priv)); memset(padata, 0, sizeof(struct padata_priv));
...@@ -103,7 +114,7 @@ static int pcrypt_aead_encrypt(struct aead_request *req) ...@@ -103,7 +114,7 @@ static int pcrypt_aead_encrypt(struct aead_request *req)
req->cryptlen, req->iv); req->cryptlen, req->iv);
aead_request_set_ad(creq, req->assoclen); aead_request_set_ad(creq, req->assoclen);
err = padata_do_parallel(pencrypt, padata, &ctx->cb_cpu); err = padata_do_parallel(ictx->psenc, padata, &ctx->cb_cpu);
if (!err) if (!err)
return -EINPROGRESS; return -EINPROGRESS;
...@@ -132,6 +143,9 @@ static int pcrypt_aead_decrypt(struct aead_request *req) ...@@ -132,6 +143,9 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
struct crypto_aead *aead = crypto_aead_reqtfm(req); struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(aead); struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(aead);
u32 flags = aead_request_flags(req); u32 flags = aead_request_flags(req);
struct pcrypt_instance_ctx *ictx;
ictx = pcrypt_tfm_ictx(aead);
memset(padata, 0, sizeof(struct padata_priv)); memset(padata, 0, sizeof(struct padata_priv));
...@@ -145,7 +159,7 @@ static int pcrypt_aead_decrypt(struct aead_request *req) ...@@ -145,7 +159,7 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
req->cryptlen, req->iv); req->cryptlen, req->iv);
aead_request_set_ad(creq, req->assoclen); aead_request_set_ad(creq, req->assoclen);
err = padata_do_parallel(pdecrypt, padata, &ctx->cb_cpu); err = padata_do_parallel(ictx->psdec, padata, &ctx->cb_cpu);
if (!err) if (!err)
return -EINPROGRESS; return -EINPROGRESS;
...@@ -192,6 +206,8 @@ static void pcrypt_free(struct aead_instance *inst) ...@@ -192,6 +206,8 @@ static void pcrypt_free(struct aead_instance *inst)
struct pcrypt_instance_ctx *ctx = aead_instance_ctx(inst); struct pcrypt_instance_ctx *ctx = aead_instance_ctx(inst);
crypto_drop_aead(&ctx->spawn); crypto_drop_aead(&ctx->spawn);
padata_free_shell(ctx->psdec);
padata_free_shell(ctx->psenc);
kfree(inst); kfree(inst);
} }
...@@ -233,12 +249,22 @@ static int pcrypt_create_aead(struct crypto_template *tmpl, struct rtattr **tb, ...@@ -233,12 +249,22 @@ static int pcrypt_create_aead(struct crypto_template *tmpl, struct rtattr **tb,
if (!inst) if (!inst)
return -ENOMEM; return -ENOMEM;
err = -ENOMEM;
ctx = aead_instance_ctx(inst); ctx = aead_instance_ctx(inst);
ctx->psenc = padata_alloc_shell(pencrypt);
if (!ctx->psenc)
goto out_free_inst;
ctx->psdec = padata_alloc_shell(pdecrypt);
if (!ctx->psdec)
goto out_free_psenc;
crypto_set_aead_spawn(&ctx->spawn, aead_crypto_instance(inst)); crypto_set_aead_spawn(&ctx->spawn, aead_crypto_instance(inst));
err = crypto_grab_aead(&ctx->spawn, name, 0, 0); err = crypto_grab_aead(&ctx->spawn, name, 0, 0);
if (err) if (err)
goto out_free_inst; goto out_free_psdec;
alg = crypto_spawn_aead_alg(&ctx->spawn); alg = crypto_spawn_aead_alg(&ctx->spawn);
err = pcrypt_init_instance(aead_crypto_instance(inst), &alg->base); err = pcrypt_init_instance(aead_crypto_instance(inst), &alg->base);
...@@ -271,6 +297,10 @@ static int pcrypt_create_aead(struct crypto_template *tmpl, struct rtattr **tb, ...@@ -271,6 +297,10 @@ static int pcrypt_create_aead(struct crypto_template *tmpl, struct rtattr **tb,
out_drop_aead: out_drop_aead:
crypto_drop_aead(&ctx->spawn); crypto_drop_aead(&ctx->spawn);
out_free_psdec:
padata_free_shell(ctx->psdec);
out_free_psenc:
padata_free_shell(ctx->psenc);
out_free_inst: out_free_inst:
kfree(inst); kfree(inst);
goto out; goto out;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#ifndef PADATA_H #ifndef PADATA_H
#define PADATA_H #define PADATA_H
#include <linux/compiler_types.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/list.h> #include <linux/list.h>
...@@ -98,7 +99,7 @@ struct padata_cpumask { ...@@ -98,7 +99,7 @@ struct padata_cpumask {
* struct parallel_data - Internal control structure, covers everything * struct parallel_data - Internal control structure, covers everything
* that depends on the cpumask in use. * that depends on the cpumask in use.
* *
* @pinst: padata instance. * @sh: padata_shell object.
* @pqueue: percpu padata queues used for parallelization. * @pqueue: percpu padata queues used for parallelization.
* @squeue: percpu padata queues used for serialuzation. * @squeue: percpu padata queues used for serialuzation.
* @reorder_objects: Number of objects waiting in the reorder queues. * @reorder_objects: Number of objects waiting in the reorder queues.
...@@ -111,7 +112,7 @@ struct padata_cpumask { ...@@ -111,7 +112,7 @@ struct padata_cpumask {
* @lock: Reorder lock. * @lock: Reorder lock.
*/ */
struct parallel_data { struct parallel_data {
struct padata_instance *pinst; struct padata_shell *ps;
struct padata_parallel_queue __percpu *pqueue; struct padata_parallel_queue __percpu *pqueue;
struct padata_serial_queue __percpu *squeue; struct padata_serial_queue __percpu *squeue;
atomic_t reorder_objects; atomic_t reorder_objects;
...@@ -124,14 +125,33 @@ struct parallel_data { ...@@ -124,14 +125,33 @@ struct parallel_data {
spinlock_t lock ____cacheline_aligned; spinlock_t lock ____cacheline_aligned;
}; };
/**
* struct padata_shell - Wrapper around struct parallel_data, its
* purpose is to allow the underlying control structure to be replaced
* on the fly using RCU.
*
* @pinst: padat instance.
* @pd: Actual parallel_data structure which may be substituted on the fly.
* @opd: Pointer to old pd to be freed by padata_replace.
* @list: List entry in padata_instance list.
*/
struct padata_shell {
struct padata_instance *pinst;
struct parallel_data __rcu *pd;
struct parallel_data *opd;
struct list_head list;
};
/** /**
* struct padata_instance - The overall control structure. * struct padata_instance - The overall control structure.
* *
* @cpu_notifier: cpu hotplug notifier. * @cpu_notifier: cpu hotplug notifier.
* @parallel_wq: The workqueue used for parallel work. * @parallel_wq: The workqueue used for parallel work.
* @serial_wq: The workqueue used for serial work. * @serial_wq: The workqueue used for serial work.
* @pd: The internal control structure. * @pslist: List of padata_shell objects attached to this instance.
* @cpumask: User supplied cpumasks for parallel and serial works. * @cpumask: User supplied cpumasks for parallel and serial works.
* @rcpumask: Actual cpumasks based on user cpumask and cpu_online_mask.
* @omask: Temporary storage used to compute the notification mask.
* @cpumask_change_notifier: Notifiers chain for user-defined notify * @cpumask_change_notifier: Notifiers chain for user-defined notify
* callbacks that will be called when either @pcpu or @cbcpu * callbacks that will be called when either @pcpu or @cbcpu
* or both cpumasks change. * or both cpumasks change.
...@@ -143,8 +163,10 @@ struct padata_instance { ...@@ -143,8 +163,10 @@ struct padata_instance {
struct hlist_node node; struct hlist_node node;
struct workqueue_struct *parallel_wq; struct workqueue_struct *parallel_wq;
struct workqueue_struct *serial_wq; struct workqueue_struct *serial_wq;
struct parallel_data *pd; struct list_head pslist;
struct padata_cpumask cpumask; struct padata_cpumask cpumask;
struct padata_cpumask rcpumask;
cpumask_var_t omask;
struct blocking_notifier_head cpumask_change_notifier; struct blocking_notifier_head cpumask_change_notifier;
struct kobject kobj; struct kobject kobj;
struct mutex lock; struct mutex lock;
...@@ -156,7 +178,9 @@ struct padata_instance { ...@@ -156,7 +178,9 @@ struct padata_instance {
extern struct padata_instance *padata_alloc_possible(const char *name); extern struct padata_instance *padata_alloc_possible(const char *name);
extern void padata_free(struct padata_instance *pinst); extern void padata_free(struct padata_instance *pinst);
extern int padata_do_parallel(struct padata_instance *pinst, extern struct padata_shell *padata_alloc_shell(struct padata_instance *pinst);
extern void padata_free_shell(struct padata_shell *ps);
extern int padata_do_parallel(struct padata_shell *ps,
struct padata_priv *padata, int *cb_cpu); struct padata_priv *padata, int *cb_cpu);
extern void padata_do_serial(struct padata_priv *padata); extern void padata_do_serial(struct padata_priv *padata);
extern int padata_set_cpumask(struct padata_instance *pinst, int cpumask_type, extern int padata_set_cpumask(struct padata_instance *pinst, int cpumask_type,
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment