2019-11-19 13:07:11

by Herbert Xu

[permalink] [raw]
Subject: [PATCH] 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 instance.

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: 5068c7a883d1 ("crypto: pcrypt - Add pcrypt crypto parallelization wrapper")
Signed-off-by: Herbert Xu <[email protected]>

diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c
index 81bbea7f2ba6..3e026e7a7e75 100644
--- a/crypto/pcrypt.c
+++ b/crypto/pcrypt.c
@@ -24,6 +24,8 @@ static struct kset *pcrypt_kset;

struct pcrypt_instance_ctx {
struct crypto_aead_spawn spawn;
+ struct padata_shell *psenc;
+ struct padata_shell *psdec;
atomic_t tfm_count;
};

@@ -32,6 +34,12 @@ struct pcrypt_aead_ctx {
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,
const u8 *key, unsigned int keylen)
{
@@ -90,6 +98,9 @@ static int pcrypt_aead_encrypt(struct aead_request *req)
struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(aead);
u32 flags = aead_request_flags(req);
+ struct pcrypt_instance_ctx *ictx;
+
+ ictx = pcrypt_tfm_ictx(aead);

memset(padata, 0, sizeof(struct padata_priv));

@@ -103,7 +114,7 @@ static int pcrypt_aead_encrypt(struct aead_request *req)
req->cryptlen, req->iv);
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)
return -EINPROGRESS;

@@ -132,6 +143,9 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(aead);
u32 flags = aead_request_flags(req);
+ struct pcrypt_instance_ctx *ictx;
+
+ ictx = pcrypt_tfm_ictx(aead);

memset(padata, 0, sizeof(struct padata_priv));

@@ -145,7 +159,7 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
req->cryptlen, req->iv);
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)
return -EINPROGRESS;

@@ -192,6 +206,8 @@ static void pcrypt_free(struct aead_instance *inst)
struct pcrypt_instance_ctx *ctx = aead_instance_ctx(inst);

crypto_drop_aead(&ctx->spawn);
+ padata_free_shell(ctx->psdec);
+ padata_free_shell(ctx->psenc);
kfree(inst);
}

@@ -233,12 +249,22 @@ static int pcrypt_create_aead(struct crypto_template *tmpl, struct rtattr **tb,
if (!inst)
return -ENOMEM;

+ err = -ENOMEM;
+
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));

err = crypto_grab_aead(&ctx->spawn, name, 0, 0);
if (err)
- goto out_free_inst;
+ goto out_free_psdec;

alg = crypto_spawn_aead_alg(&ctx->spawn);
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,

out_drop_aead:
crypto_drop_aead(&ctx->spawn);
+out_free_psdec:
+ padata_free_shell(ctx->psdec);
+out_free_psenc:
+ padata_free_shell(ctx->psenc);
out_free_inst:
kfree(inst);
goto out;
diff --git a/include/linux/padata.h b/include/linux/padata.h
index 23717eeaad23..fd38897e1b91 100644
--- a/include/linux/padata.h
+++ b/include/linux/padata.h
@@ -9,6 +9,7 @@
#ifndef PADATA_H
#define PADATA_H

+#include <linux/compiler_types.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/list.h>
@@ -98,7 +99,7 @@ struct padata_cpumask {
* struct parallel_data - Internal control structure, covers everything
* that depends on the cpumask in use.
*
- * @pinst: padata instance.
+ * @sh: padata_shell object.
* @pqueue: percpu padata queues used for parallelization.
* @squeue: percpu padata queues used for serialuzation.
* @reorder_objects: Number of objects waiting in the reorder queues.
@@ -111,7 +112,7 @@ struct padata_cpumask {
* @lock: Reorder lock.
*/
struct parallel_data {
- struct padata_instance *pinst;
+ struct padata_shell *ps;
struct padata_parallel_queue __percpu *pqueue;
struct padata_serial_queue __percpu *squeue;
atomic_t reorder_objects;
@@ -125,12 +126,27 @@ struct parallel_data {
};

/**
+ * 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.
+ * @list: List entry in padata_instance list.
+ */
+struct padata_shell {
+ struct padata_instance *pinst;
+ struct parallel_data __rcu *pd;
+ struct list_head list;
+};
+
+/**
* struct padata_instance - The overall control structure.
*
* @cpu_notifier: cpu hotplug notifier.
* @parallel_wq: The workqueue used for parallel 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_change_notifier: Notifiers chain for user-defined notify
* callbacks that will be called when either @pcpu or @cbcpu
@@ -143,7 +159,7 @@ struct padata_instance {
struct hlist_node node;
struct workqueue_struct *parallel_wq;
struct workqueue_struct *serial_wq;
- struct parallel_data *pd;
+ struct list_head pslist;
struct padata_cpumask cpumask;
struct blocking_notifier_head cpumask_change_notifier;
struct kobject kobj;
@@ -156,7 +172,9 @@ struct padata_instance {

extern struct padata_instance *padata_alloc_possible(const char *name);
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);
extern void padata_do_serial(struct padata_priv *padata);
extern int padata_set_cpumask(struct padata_instance *pinst, int cpumask_type,
diff --git a/kernel/padata.c b/kernel/padata.c
index da56a235a255..0c33833c99dd 100644
--- a/kernel/padata.c
+++ b/kernel/padata.c
@@ -89,7 +89,7 @@ static void padata_parallel_worker(struct work_struct *parallel_work)
/**
* padata_do_parallel - padata parallelization function
*
- * @pinst: padata instance
+ * @ps: padatashell
* @padata: object to be parallelized
* @cb_cpu: pointer to the CPU that the serialization callback function should
* run on. If it's not in the serial cpumask of @pinst
@@ -100,16 +100,17 @@ static void padata_parallel_worker(struct work_struct *parallel_work)
* Note: Every object which is parallelized by padata_do_parallel
* must be seen by padata_do_serial.
*/
-int padata_do_parallel(struct padata_instance *pinst,
+int padata_do_parallel(struct padata_shell *ps,
struct padata_priv *padata, int *cb_cpu)
{
+ struct padata_instance *pinst = ps->pinst;
int i, cpu, cpu_index, target_cpu, err;
struct padata_parallel_queue *queue;
struct parallel_data *pd;

rcu_read_lock_bh();

- pd = rcu_dereference_bh(pinst->pd);
+ pd = rcu_dereference_bh(ps->pd);

err = -EINVAL;
if (!(pinst->flags & PADATA_INIT) || pinst->flags & PADATA_INVALID)
@@ -212,10 +213,10 @@ static struct padata_priv *padata_find_next(struct parallel_data *pd,

static void padata_reorder(struct parallel_data *pd)
{
+ struct padata_instance *pinst = pd->ps->pinst;
int cb_cpu;
struct padata_priv *padata;
struct padata_serial_queue *squeue;
- struct padata_instance *pinst = pd->pinst;
struct padata_parallel_queue *next_queue;

/*
@@ -370,7 +371,7 @@ static int padata_setup_cpumasks(struct parallel_data *pd,

/* Restrict parallel_wq workers to pd->cpumask.pcpu. */
cpumask_copy(attrs->cpumask, pd->cpumask.pcpu);
- err = apply_workqueue_attrs(pd->pinst->parallel_wq, attrs);
+ err = apply_workqueue_attrs(pd->ps->pinst->parallel_wq, attrs);
free_workqueue_attrs(attrs);
if (err < 0)
goto free_cbcpu_mask;
@@ -422,12 +423,16 @@ static void padata_init_pqueues(struct parallel_data *pd)
}

/* Allocate and initialize the internal cpumask dependend resources. */
-static struct parallel_data *padata_alloc_pd(struct padata_instance *pinst,
- const struct cpumask *pcpumask,
- const struct cpumask *cbcpumask)
+static struct parallel_data *padata_alloc_pd(struct padata_shell *ps)
{
+ struct padata_instance *pinst = ps->pinst;
+ const struct cpumask *cbcpumask;
+ const struct cpumask *pcpumask;
struct parallel_data *pd;

+ cbcpumask = pinst->cpumask.cbcpu;
+ pcpumask = pinst->cpumask.pcpu;
+
pd = kzalloc(sizeof(struct parallel_data), GFP_KERNEL);
if (!pd)
goto err;
@@ -440,7 +445,7 @@ static struct parallel_data *padata_alloc_pd(struct padata_instance *pinst,
if (!pd->squeue)
goto err_free_pqueue;

- pd->pinst = pinst;
+ pd->ps = ps;
if (padata_setup_cpumasks(pd, pcpumask, cbcpumask) < 0)
goto err_free_squeue;

@@ -490,17 +495,17 @@ static void __padata_stop(struct padata_instance *pinst)
}

/* Replace the internal control structure with a new one. */
-static void padata_replace(struct padata_instance *pinst,
- struct parallel_data *pd_new)
+static int padata_replace_one(struct padata_shell *ps)
{
- struct parallel_data *pd_old = pinst->pd;
+ struct parallel_data *pd_old = rcu_dereference_protected(ps->pd, 1);
+ struct parallel_data *pd_new;
int notification_mask = 0;

- pinst->flags |= PADATA_RESET;
-
- rcu_assign_pointer(pinst->pd, pd_new);
+ pd_new = padata_alloc_pd(ps);
+ if (!pd_new)
+ return -ENOMEM;

- synchronize_rcu();
+ rcu_assign_pointer(ps->pd, pd_new);

if (!cpumask_equal(pd_old->cpumask.pcpu, pd_new->cpumask.pcpu))
notification_mask |= PADATA_CPU_PARALLEL;
@@ -510,10 +515,25 @@ static void padata_replace(struct padata_instance *pinst,
if (atomic_dec_and_test(&pd_old->refcnt))
padata_free_pd(pd_old);

+ return notification_mask;
+}
+
+static void padata_replace(struct padata_instance *pinst)
+{
+ int notification_mask = 0;
+ struct padata_shell *ps;
+
+ pinst->flags |= PADATA_RESET;
+
+ list_for_each_entry(ps, &pinst->pslist, list)
+ notification_mask |= padata_replace_one(ps);
+
+ synchronize_rcu();
+
if (notification_mask)
blocking_notifier_call_chain(&pinst->cpumask_change_notifier,
notification_mask,
- &pd_new->cpumask);
+ &pinst->cpumask);

pinst->flags &= ~PADATA_RESET;
}
@@ -568,7 +588,6 @@ static int __padata_set_cpumasks(struct padata_instance *pinst,
cpumask_var_t cbcpumask)
{
int valid;
- struct parallel_data *pd;

valid = padata_validate_cpumask(pinst, pcpumask);
if (!valid) {
@@ -581,14 +600,10 @@ static int __padata_set_cpumasks(struct padata_instance *pinst,
__padata_stop(pinst);

out_replace:
- pd = padata_alloc_pd(pinst, pcpumask, cbcpumask);
- if (!pd)
- return -ENOMEM;
-
cpumask_copy(pinst->cpumask.pcpu, pcpumask);
cpumask_copy(pinst->cpumask.cbcpu, cbcpumask);

- padata_replace(pinst, pd);
+ padata_replace(pinst);

if (valid)
__padata_start(pinst);
@@ -676,15 +691,8 @@ EXPORT_SYMBOL(padata_stop);

static int __padata_add_cpu(struct padata_instance *pinst, int cpu)
{
- struct parallel_data *pd;
-
if (cpumask_test_cpu(cpu, cpu_online_mask)) {
- pd = padata_alloc_pd(pinst, pinst->cpumask.pcpu,
- pinst->cpumask.cbcpu);
- if (!pd)
- return -ENOMEM;
-
- padata_replace(pinst, pd);
+ padata_replace(pinst);

if (padata_validate_cpumask(pinst, pinst->cpumask.pcpu) &&
padata_validate_cpumask(pinst, pinst->cpumask.cbcpu))
@@ -696,23 +704,15 @@ static int __padata_add_cpu(struct padata_instance *pinst, int cpu)

static int __padata_remove_cpu(struct padata_instance *pinst, int cpu)
{
- struct parallel_data *pd = NULL;
-
if (cpumask_test_cpu(cpu, cpu_online_mask)) {
+ cpumask_clear_cpu(cpu, pinst->cpumask.pcpu);
+ cpumask_clear_cpu(cpu, pinst->cpumask.cbcpu);

if (!padata_validate_cpumask(pinst, pinst->cpumask.pcpu) ||
!padata_validate_cpumask(pinst, pinst->cpumask.cbcpu))
__padata_stop(pinst);

- pd = padata_alloc_pd(pinst, pinst->cpumask.pcpu,
- pinst->cpumask.cbcpu);
- if (!pd)
- return -ENOMEM;
-
- padata_replace(pinst, pd);
-
- cpumask_clear_cpu(cpu, pd->cpumask.cbcpu);
- cpumask_clear_cpu(cpu, pd->cpumask.pcpu);
+ padata_replace(pinst);
}

return 0;
@@ -798,8 +798,9 @@ static void __padata_free(struct padata_instance *pinst)
cpuhp_state_remove_instance_nocalls(hp_online, &pinst->node);
#endif

+ WARN_ON(!list_empty(&pinst->pslist));
+
padata_stop(pinst);
- padata_free_pd(pinst->pd);
free_cpumask_var(pinst->cpumask.pcpu);
free_cpumask_var(pinst->cpumask.cbcpu);
destroy_workqueue(pinst->serial_wq);
@@ -946,7 +947,6 @@ static struct padata_instance *padata_alloc(const char *name,
const struct cpumask *cbcpumask)
{
struct padata_instance *pinst;
- struct parallel_data *pd = NULL;

pinst = kzalloc(sizeof(struct padata_instance), GFP_KERNEL);
if (!pinst)
@@ -974,11 +974,8 @@ static struct padata_instance *padata_alloc(const char *name,
!padata_validate_cpumask(pinst, cbcpumask))
goto err_free_masks;

- pd = padata_alloc_pd(pinst, pcpumask, cbcpumask);
- if (!pd)
- goto err_free_masks;

- rcu_assign_pointer(pinst->pd, pd);
+ INIT_LIST_HEAD(&pinst->pslist);

cpumask_copy(pinst->cpumask.pcpu, pcpumask);
cpumask_copy(pinst->cpumask.cbcpu, cbcpumask);
@@ -1035,6 +1032,60 @@ void padata_free(struct padata_instance *pinst)
}
EXPORT_SYMBOL(padata_free);

+/**
+ * padata_alloc_shell - Allocate and initialize padata shell.
+ *
+ * @pinst: Parent padata_instance object.
+ */
+struct padata_shell *padata_alloc_shell(struct padata_instance *pinst)
+{
+ struct parallel_data *pd;
+ struct padata_shell *ps;
+
+ ps = kzalloc(sizeof(*ps), GFP_KERNEL);
+ if (!ps)
+ goto out;
+
+ ps->pinst = pinst;
+
+ mutex_lock(&pinst->lock);
+ pd = padata_alloc_pd(ps);
+ if (!pd)
+ goto out_unlock;
+
+ RCU_INIT_POINTER(ps->pd, pd);
+ list_add(&ps->list, &pinst->pslist);
+
+ mutex_unlock(&pinst->lock);
+ return ps;
+
+out_unlock:
+ mutex_unlock(&pinst->lock);
+ kfree(ps);
+
+out:
+ return NULL;
+}
+EXPORT_SYMBOL(padata_alloc_shell);
+
+/**
+ * padata_free_shell - free a padata shell
+ *
+ * @ps: padata shell to free
+ */
+void padata_free_shell(struct padata_shell *ps)
+{
+ struct padata_instance *pinst = ps->pinst;
+
+ mutex_lock(&pinst->lock);
+ list_del(&ps->list);
+ padata_free_pd(rcu_dereference_protected(ps->pd, 1));
+ mutex_unlock(&pinst->lock);
+
+ kfree(ps);
+}
+EXPORT_SYMBOL(padata_free_shell);
+
#ifdef CONFIG_HOTPLUG_CPU

static __init int padata_driver_init(void)
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt


2019-11-19 17:40:54

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH] crypto: pcrypt - Avoid deadlock by using per-instance padata queues

Hi Herbert,

On Tue, Nov 19, 2019 at 09:05:57PM +0800, Herbert Xu wrote:
> 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 instance.
>
> 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: 5068c7a883d1 ("crypto: pcrypt - Add pcrypt crypto parallelization wrapper")
> Signed-off-by: Herbert Xu <[email protected]>
>

FYI, with your 3 pcrypt patches applied, I tried enabling CONFIG_CRYPTO_PCRYPT=y
again and running syzkaller targetting AF_ALG, and I quickly got the following
warning:

------------[ cut here ]------------
WARNING: CPU: 7 PID: 64550 at kernel/cpu.c:329 lockdep_assert_cpus_held+0xc3/0xf0 kernel/cpu.c:329
Kernel panic - not syncing: panic_on_warn set ...
CPU: 7 PID: 64550 Comm: cryptomgr_probe Not tainted 5.4.0-rc1-00267-g94b2b3991aca #10
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS ?-20191013_105130-anatol 04/01/2014
Call Trace:
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0xd4/0x156 lib/dump_stack.c:113
panic+0x2a3/0x6b3 kernel/panic.c:220
__warn.cold+0x2f/0x3e kernel/panic.c:581
report_bug+0x291/0x300 lib/bug.c:195
fixup_bug arch/x86/kernel/traps.c:179 [inline]
fixup_bug arch/x86/kernel/traps.c:174 [inline]
do_error_trap+0x11b/0x1c0 arch/x86/kernel/traps.c:272
do_invalid_op+0x50/0x70 arch/x86/kernel/traps.c:291
invalid_op+0x23/0x30 arch/x86/entry/entry_64.S:1028
RIP: 0010:lockdep_assert_cpus_held+0xc3/0xf0 kernel/cpu.c:329
Code: e8 02 39 24 00 be ff ff ff ff 48 c7 c7 30 55 2b 83 e8 c1 5b 11 00 31 ff 89 c3 89 c6 e8 66 3a 24 00 85 db 75 d3 e8 dd 38 24 00 <0f> 0b e8 d6 38 24 00 5b 5d c3 48 c7 c7 44 71 5a 83 e8 17 7c 46 00
RSP: 0018:ffff888068007d98 EFLAGS: 00010293
RAX: ffff88804b10c040 RBX: 0000000000000000 RCX: ffffffff811f417a
RDX: 0000000000000000 RSI: ffffffff811f4183 RDI: 0000000000000005
RBP: ffff888068007da0 R08: ffff88804b10c040 R09: ffffed100dafe405
R10: ffffed100dafe404 R11: ffff88806d7f2023 R12: ffff88806baa0cc0
R13: ffff88806ae432c0 R14: 000000000000ffff R15: ffff88806b4e9008
apply_workqueue_attrs+0x18/0x50 kernel/workqueue.c:4042
padata_setup_cpumasks kernel/padata.c:374 [inline]
padata_alloc_pd+0x2f5/0xbb0 kernel/padata.c:449
padata_alloc_shell+0x97/0x290 kernel/padata.c:1052
pcrypt_create_aead crypto/pcrypt.c:255 [inline]
pcrypt_create+0x19e/0x890 crypto/pcrypt.c:319
cryptomgr_probe+0x6f/0x290 crypto/algboss.c:70
kthread+0x361/0x430 kernel/kthread.c:255
ret_from_fork+0x24/0x30 arch/x86/entry/entry_64.S:352
Dumping ftrace buffer:
(ftrace buffer empty)
Kernel Offset: disabled
Rebooting in 1 seconds..

2019-11-19 18:59:26

by Herbert Xu

[permalink] [raw]
Subject: [v2 PATCH] crypto: pcrypt - Avoid deadlock by using per-instance padata queues

On Tue, Nov 19, 2019 at 09:37:32AM -0800, Eric Biggers wrote:
>
> FYI, with your 3 pcrypt patches applied, I tried enabling CONFIG_CRYPTO_PCRYPT=y
> again and running syzkaller targetting AF_ALG, and I quickly got the following
> warning:

Thanks, I forgot to take the CPU lock in padata_alloc_shell.

---8<---
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 instance.

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: 5068c7a883d1 ("crypto: pcrypt - Add pcrypt crypto parallelization wrapper")
Signed-off-by: Herbert Xu <[email protected]>

diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c
index 81bbea7f2ba6..3e026e7a7e75 100644
--- a/crypto/pcrypt.c
+++ b/crypto/pcrypt.c
@@ -24,6 +24,8 @@ static struct kset *pcrypt_kset;

struct pcrypt_instance_ctx {
struct crypto_aead_spawn spawn;
+ struct padata_shell *psenc;
+ struct padata_shell *psdec;
atomic_t tfm_count;
};

@@ -32,6 +34,12 @@ struct pcrypt_aead_ctx {
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,
const u8 *key, unsigned int keylen)
{
@@ -90,6 +98,9 @@ static int pcrypt_aead_encrypt(struct aead_request *req)
struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(aead);
u32 flags = aead_request_flags(req);
+ struct pcrypt_instance_ctx *ictx;
+
+ ictx = pcrypt_tfm_ictx(aead);

memset(padata, 0, sizeof(struct padata_priv));

@@ -103,7 +114,7 @@ static int pcrypt_aead_encrypt(struct aead_request *req)
req->cryptlen, req->iv);
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)
return -EINPROGRESS;

@@ -132,6 +143,9 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(aead);
u32 flags = aead_request_flags(req);
+ struct pcrypt_instance_ctx *ictx;
+
+ ictx = pcrypt_tfm_ictx(aead);

memset(padata, 0, sizeof(struct padata_priv));

@@ -145,7 +159,7 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
req->cryptlen, req->iv);
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)
return -EINPROGRESS;

@@ -192,6 +206,8 @@ static void pcrypt_free(struct aead_instance *inst)
struct pcrypt_instance_ctx *ctx = aead_instance_ctx(inst);

crypto_drop_aead(&ctx->spawn);
+ padata_free_shell(ctx->psdec);
+ padata_free_shell(ctx->psenc);
kfree(inst);
}

@@ -233,12 +249,22 @@ static int pcrypt_create_aead(struct crypto_template *tmpl, struct rtattr **tb,
if (!inst)
return -ENOMEM;

+ err = -ENOMEM;
+
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));

err = crypto_grab_aead(&ctx->spawn, name, 0, 0);
if (err)
- goto out_free_inst;
+ goto out_free_psdec;

alg = crypto_spawn_aead_alg(&ctx->spawn);
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,

out_drop_aead:
crypto_drop_aead(&ctx->spawn);
+out_free_psdec:
+ padata_free_shell(ctx->psdec);
+out_free_psenc:
+ padata_free_shell(ctx->psenc);
out_free_inst:
kfree(inst);
goto out;
diff --git a/include/linux/padata.h b/include/linux/padata.h
index 23717eeaad23..fd38897e1b91 100644
--- a/include/linux/padata.h
+++ b/include/linux/padata.h
@@ -9,6 +9,7 @@
#ifndef PADATA_H
#define PADATA_H

+#include <linux/compiler_types.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/list.h>
@@ -98,7 +99,7 @@ struct padata_cpumask {
* struct parallel_data - Internal control structure, covers everything
* that depends on the cpumask in use.
*
- * @pinst: padata instance.
+ * @sh: padata_shell object.
* @pqueue: percpu padata queues used for parallelization.
* @squeue: percpu padata queues used for serialuzation.
* @reorder_objects: Number of objects waiting in the reorder queues.
@@ -111,7 +112,7 @@ struct padata_cpumask {
* @lock: Reorder lock.
*/
struct parallel_data {
- struct padata_instance *pinst;
+ struct padata_shell *ps;
struct padata_parallel_queue __percpu *pqueue;
struct padata_serial_queue __percpu *squeue;
atomic_t reorder_objects;
@@ -125,12 +126,27 @@ struct parallel_data {
};

/**
+ * 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.
+ * @list: List entry in padata_instance list.
+ */
+struct padata_shell {
+ struct padata_instance *pinst;
+ struct parallel_data __rcu *pd;
+ struct list_head list;
+};
+
+/**
* struct padata_instance - The overall control structure.
*
* @cpu_notifier: cpu hotplug notifier.
* @parallel_wq: The workqueue used for parallel 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_change_notifier: Notifiers chain for user-defined notify
* callbacks that will be called when either @pcpu or @cbcpu
@@ -143,7 +159,7 @@ struct padata_instance {
struct hlist_node node;
struct workqueue_struct *parallel_wq;
struct workqueue_struct *serial_wq;
- struct parallel_data *pd;
+ struct list_head pslist;
struct padata_cpumask cpumask;
struct blocking_notifier_head cpumask_change_notifier;
struct kobject kobj;
@@ -156,7 +172,9 @@ struct padata_instance {

extern struct padata_instance *padata_alloc_possible(const char *name);
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);
extern void padata_do_serial(struct padata_priv *padata);
extern int padata_set_cpumask(struct padata_instance *pinst, int cpumask_type,
diff --git a/kernel/padata.c b/kernel/padata.c
index da56a235a255..b2f21074a276 100644
--- a/kernel/padata.c
+++ b/kernel/padata.c
@@ -89,7 +89,7 @@ static void padata_parallel_worker(struct work_struct *parallel_work)
/**
* padata_do_parallel - padata parallelization function
*
- * @pinst: padata instance
+ * @ps: padatashell
* @padata: object to be parallelized
* @cb_cpu: pointer to the CPU that the serialization callback function should
* run on. If it's not in the serial cpumask of @pinst
@@ -100,16 +100,17 @@ static void padata_parallel_worker(struct work_struct *parallel_work)
* Note: Every object which is parallelized by padata_do_parallel
* must be seen by padata_do_serial.
*/
-int padata_do_parallel(struct padata_instance *pinst,
+int padata_do_parallel(struct padata_shell *ps,
struct padata_priv *padata, int *cb_cpu)
{
+ struct padata_instance *pinst = ps->pinst;
int i, cpu, cpu_index, target_cpu, err;
struct padata_parallel_queue *queue;
struct parallel_data *pd;

rcu_read_lock_bh();

- pd = rcu_dereference_bh(pinst->pd);
+ pd = rcu_dereference_bh(ps->pd);

err = -EINVAL;
if (!(pinst->flags & PADATA_INIT) || pinst->flags & PADATA_INVALID)
@@ -212,10 +213,10 @@ static struct padata_priv *padata_find_next(struct parallel_data *pd,

static void padata_reorder(struct parallel_data *pd)
{
+ struct padata_instance *pinst = pd->ps->pinst;
int cb_cpu;
struct padata_priv *padata;
struct padata_serial_queue *squeue;
- struct padata_instance *pinst = pd->pinst;
struct padata_parallel_queue *next_queue;

/*
@@ -370,7 +371,7 @@ static int padata_setup_cpumasks(struct parallel_data *pd,

/* Restrict parallel_wq workers to pd->cpumask.pcpu. */
cpumask_copy(attrs->cpumask, pd->cpumask.pcpu);
- err = apply_workqueue_attrs(pd->pinst->parallel_wq, attrs);
+ err = apply_workqueue_attrs(pd->ps->pinst->parallel_wq, attrs);
free_workqueue_attrs(attrs);
if (err < 0)
goto free_cbcpu_mask;
@@ -422,12 +423,16 @@ static void padata_init_pqueues(struct parallel_data *pd)
}

/* Allocate and initialize the internal cpumask dependend resources. */
-static struct parallel_data *padata_alloc_pd(struct padata_instance *pinst,
- const struct cpumask *pcpumask,
- const struct cpumask *cbcpumask)
+static struct parallel_data *padata_alloc_pd(struct padata_shell *ps)
{
+ struct padata_instance *pinst = ps->pinst;
+ const struct cpumask *cbcpumask;
+ const struct cpumask *pcpumask;
struct parallel_data *pd;

+ cbcpumask = pinst->cpumask.cbcpu;
+ pcpumask = pinst->cpumask.pcpu;
+
pd = kzalloc(sizeof(struct parallel_data), GFP_KERNEL);
if (!pd)
goto err;
@@ -440,7 +445,7 @@ static struct parallel_data *padata_alloc_pd(struct padata_instance *pinst,
if (!pd->squeue)
goto err_free_pqueue;

- pd->pinst = pinst;
+ pd->ps = ps;
if (padata_setup_cpumasks(pd, pcpumask, cbcpumask) < 0)
goto err_free_squeue;

@@ -490,17 +495,17 @@ static void __padata_stop(struct padata_instance *pinst)
}

/* Replace the internal control structure with a new one. */
-static void padata_replace(struct padata_instance *pinst,
- struct parallel_data *pd_new)
+static int padata_replace_one(struct padata_shell *ps)
{
- struct parallel_data *pd_old = pinst->pd;
+ struct parallel_data *pd_old = rcu_dereference_protected(ps->pd, 1);
+ struct parallel_data *pd_new;
int notification_mask = 0;

- pinst->flags |= PADATA_RESET;
-
- rcu_assign_pointer(pinst->pd, pd_new);
+ pd_new = padata_alloc_pd(ps);
+ if (!pd_new)
+ return -ENOMEM;

- synchronize_rcu();
+ rcu_assign_pointer(ps->pd, pd_new);

if (!cpumask_equal(pd_old->cpumask.pcpu, pd_new->cpumask.pcpu))
notification_mask |= PADATA_CPU_PARALLEL;
@@ -510,10 +515,25 @@ static void padata_replace(struct padata_instance *pinst,
if (atomic_dec_and_test(&pd_old->refcnt))
padata_free_pd(pd_old);

+ return notification_mask;
+}
+
+static void padata_replace(struct padata_instance *pinst)
+{
+ int notification_mask = 0;
+ struct padata_shell *ps;
+
+ pinst->flags |= PADATA_RESET;
+
+ list_for_each_entry(ps, &pinst->pslist, list)
+ notification_mask |= padata_replace_one(ps);
+
+ synchronize_rcu();
+
if (notification_mask)
blocking_notifier_call_chain(&pinst->cpumask_change_notifier,
notification_mask,
- &pd_new->cpumask);
+ &pinst->cpumask);

pinst->flags &= ~PADATA_RESET;
}
@@ -568,7 +588,6 @@ static int __padata_set_cpumasks(struct padata_instance *pinst,
cpumask_var_t cbcpumask)
{
int valid;
- struct parallel_data *pd;

valid = padata_validate_cpumask(pinst, pcpumask);
if (!valid) {
@@ -581,14 +600,10 @@ static int __padata_set_cpumasks(struct padata_instance *pinst,
__padata_stop(pinst);

out_replace:
- pd = padata_alloc_pd(pinst, pcpumask, cbcpumask);
- if (!pd)
- return -ENOMEM;
-
cpumask_copy(pinst->cpumask.pcpu, pcpumask);
cpumask_copy(pinst->cpumask.cbcpu, cbcpumask);

- padata_replace(pinst, pd);
+ padata_replace(pinst);

if (valid)
__padata_start(pinst);
@@ -676,15 +691,8 @@ EXPORT_SYMBOL(padata_stop);

static int __padata_add_cpu(struct padata_instance *pinst, int cpu)
{
- struct parallel_data *pd;
-
if (cpumask_test_cpu(cpu, cpu_online_mask)) {
- pd = padata_alloc_pd(pinst, pinst->cpumask.pcpu,
- pinst->cpumask.cbcpu);
- if (!pd)
- return -ENOMEM;
-
- padata_replace(pinst, pd);
+ padata_replace(pinst);

if (padata_validate_cpumask(pinst, pinst->cpumask.pcpu) &&
padata_validate_cpumask(pinst, pinst->cpumask.cbcpu))
@@ -696,23 +704,15 @@ static int __padata_add_cpu(struct padata_instance *pinst, int cpu)

static int __padata_remove_cpu(struct padata_instance *pinst, int cpu)
{
- struct parallel_data *pd = NULL;
-
if (cpumask_test_cpu(cpu, cpu_online_mask)) {
+ cpumask_clear_cpu(cpu, pinst->cpumask.pcpu);
+ cpumask_clear_cpu(cpu, pinst->cpumask.cbcpu);

if (!padata_validate_cpumask(pinst, pinst->cpumask.pcpu) ||
!padata_validate_cpumask(pinst, pinst->cpumask.cbcpu))
__padata_stop(pinst);

- pd = padata_alloc_pd(pinst, pinst->cpumask.pcpu,
- pinst->cpumask.cbcpu);
- if (!pd)
- return -ENOMEM;
-
- padata_replace(pinst, pd);
-
- cpumask_clear_cpu(cpu, pd->cpumask.cbcpu);
- cpumask_clear_cpu(cpu, pd->cpumask.pcpu);
+ padata_replace(pinst);
}

return 0;
@@ -798,8 +798,9 @@ static void __padata_free(struct padata_instance *pinst)
cpuhp_state_remove_instance_nocalls(hp_online, &pinst->node);
#endif

+ WARN_ON(!list_empty(&pinst->pslist));
+
padata_stop(pinst);
- padata_free_pd(pinst->pd);
free_cpumask_var(pinst->cpumask.pcpu);
free_cpumask_var(pinst->cpumask.cbcpu);
destroy_workqueue(pinst->serial_wq);
@@ -946,7 +947,6 @@ static struct padata_instance *padata_alloc(const char *name,
const struct cpumask *cbcpumask)
{
struct padata_instance *pinst;
- struct parallel_data *pd = NULL;

pinst = kzalloc(sizeof(struct padata_instance), GFP_KERNEL);
if (!pinst)
@@ -974,11 +974,8 @@ static struct padata_instance *padata_alloc(const char *name,
!padata_validate_cpumask(pinst, cbcpumask))
goto err_free_masks;

- pd = padata_alloc_pd(pinst, pcpumask, cbcpumask);
- if (!pd)
- goto err_free_masks;

- rcu_assign_pointer(pinst->pd, pd);
+ INIT_LIST_HEAD(&pinst->pslist);

cpumask_copy(pinst->cpumask.pcpu, pcpumask);
cpumask_copy(pinst->cpumask.cbcpu, cbcpumask);
@@ -1035,6 +1032,61 @@ void padata_free(struct padata_instance *pinst)
}
EXPORT_SYMBOL(padata_free);

+/**
+ * padata_alloc_shell - Allocate and initialize padata shell.
+ *
+ * @pinst: Parent padata_instance object.
+ */
+struct padata_shell *padata_alloc_shell(struct padata_instance *pinst)
+{
+ struct parallel_data *pd;
+ struct padata_shell *ps;
+
+ ps = kzalloc(sizeof(*ps), GFP_KERNEL);
+ if (!ps)
+ goto out;
+
+ ps->pinst = pinst;
+
+ get_online_cpus();
+ pd = padata_alloc_pd(ps);
+ put_online_cpus();
+
+ if (!pd)
+ goto out_free_ps;
+
+ mutex_lock(&pinst->lock);
+ RCU_INIT_POINTER(ps->pd, pd);
+ list_add(&ps->list, &pinst->pslist);
+ mutex_unlock(&pinst->lock);
+
+ return ps;
+
+out_free_ps:
+ kfree(ps);
+out:
+ return NULL;
+}
+EXPORT_SYMBOL(padata_alloc_shell);
+
+/**
+ * padata_free_shell - free a padata shell
+ *
+ * @ps: padata shell to free
+ */
+void padata_free_shell(struct padata_shell *ps)
+{
+ struct padata_instance *pinst = ps->pinst;
+
+ mutex_lock(&pinst->lock);
+ list_del(&ps->list);
+ padata_free_pd(rcu_dereference_protected(ps->pd, 1));
+ mutex_unlock(&pinst->lock);
+
+ kfree(ps);
+}
+EXPORT_SYMBOL(padata_free_shell);
+
#ifdef CONFIG_HOTPLUG_CPU

static __init int padata_driver_init(void)
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2019-11-22 16:29:36

by Daniel Jordan

[permalink] [raw]
Subject: Re: [v2 PATCH] crypto: pcrypt - Avoid deadlock by using per-instance padata queues

On Wed, Nov 20, 2019 at 02:58:27AM +0800, Herbert Xu wrote:
> static int __padata_remove_cpu(struct padata_instance *pinst, int cpu)
> {
> - struct parallel_data *pd = NULL;
> -
> if (cpumask_test_cpu(cpu, cpu_online_mask)) {
> + cpumask_clear_cpu(cpu, pinst->cpumask.pcpu);
> + cpumask_clear_cpu(cpu, pinst->cpumask.cbcpu);
>
> if (!padata_validate_cpumask(pinst, pinst->cpumask.pcpu) ||
> !padata_validate_cpumask(pinst, pinst->cpumask.cbcpu))
> __padata_stop(pinst);
>
> - pd = padata_alloc_pd(pinst, pinst->cpumask.pcpu,
> - pinst->cpumask.cbcpu);
> - if (!pd)
> - return -ENOMEM;
> -
> - padata_replace(pinst, pd);
> -
> - cpumask_clear_cpu(cpu, pd->cpumask.cbcpu);
> - cpumask_clear_cpu(cpu, pd->cpumask.pcpu);
> + padata_replace(pinst);
> }

Clearing the offlined CPU from pinst's cpumasks means it won't be used if it
comes back online.

The CPU could be cleared from all pd's attached to the instance, but that
doesn't address another bug in this function that's fixed by this patch:

https://lore.kernel.org/linux-crypto/[email protected]/

Should I add it into the next version of the padata series I just posted?:

https://lore.kernel.org/linux-crypto/[email protected]/

It would obviate the need for your patch to mess with cpumask_clear_cpu(). Or
is there something else you'd prefer?

Thanks,
Daniel

2019-11-26 05:37:19

by Daniel Jordan

[permalink] [raw]
Subject: Re: [v2 PATCH] crypto: pcrypt - Avoid deadlock by using per-instance padata queues

On Wed, Nov 20, 2019 at 02:58:27AM +0800, Herbert Xu wrote:
> /* Replace the internal control structure with a new one. */
> -static void padata_replace(struct padata_instance *pinst,
> - struct parallel_data *pd_new)
> +static int padata_replace_one(struct padata_shell *ps)
> {
> - struct parallel_data *pd_old = pinst->pd;
> + struct parallel_data *pd_old = rcu_dereference_protected(ps->pd, 1);
> + struct parallel_data *pd_new;
> int notification_mask = 0;
>
> - pinst->flags |= PADATA_RESET;
> -
> - rcu_assign_pointer(pinst->pd, pd_new);
> + pd_new = padata_alloc_pd(ps);
> + if (!pd_new)
> + return -ENOMEM;
>
> - synchronize_rcu();
> + rcu_assign_pointer(ps->pd, pd_new);
>
> if (!cpumask_equal(pd_old->cpumask.pcpu, pd_new->cpumask.pcpu))
> notification_mask |= PADATA_CPU_PARALLEL;
> @@ -510,10 +515,25 @@ static void padata_replace(struct padata_instance *pinst,
> if (atomic_dec_and_test(&pd_old->refcnt))
> padata_free_pd(pd_old);
>
> + return notification_mask;
> +}
> +
> +static void padata_replace(struct padata_instance *pinst)
> +{
> + int notification_mask = 0;
> + struct padata_shell *ps;
> +
> + pinst->flags |= PADATA_RESET;
> +
> + list_for_each_entry(ps, &pinst->pslist, list)
> + notification_mask |= padata_replace_one(ps);
> +
> + synchronize_rcu();
> +
> if (notification_mask)
> blocking_notifier_call_chain(&pinst->cpumask_change_notifier,
> notification_mask,
> - &pd_new->cpumask);
> + &pinst->cpumask);
>
> pinst->flags &= ~PADATA_RESET;
> }

I think it's possible for a task in padata_do_parallel() racing with another in
padata_replace() to use a pd after free. The synchronize_rcu() comes after the
pd_old->refcnt's are dec'd.

padata_do_parallel()
rcu_dereference_bh(ps->pd)
// doesn't see PADATA_RESET set
padata_replace()
pinst->flags |= PADATA_RESET
padata_replace_one()
rcu_assign_pointer(ps->pd, pd_new)
atomic_dec_and_test(&pd_old->refcnt)
padata_free_pd()
atomic_inc(&pd->refcnt) // too late

If I'm not missing something, one way out is adding a list_head to
parallel_data for remembering the old pd's on a local list in padata_replace()
so that this function can loop over it and drop the refs after
synchronize_rcu(). A padata_do_parallel() call will have then had a chance to
take a ref on the pd it's using.


And, not this patch, but with the removal of flushing it seems there's no need
for PADATA_RESET, so it and its EBUSY error can go away.

2019-11-26 08:01:16

by Herbert Xu

[permalink] [raw]
Subject: [v3 PATCH] crypto: pcrypt - Avoid deadlock by using per-instance padata queues

On Tue, Nov 26, 2019 at 12:32:38AM -0500, Daniel Jordan wrote:
>
> I think it's possible for a task in padata_do_parallel() racing with another in
> padata_replace() to use a pd after free. The synchronize_rcu() comes after the
> pd_old->refcnt's are dec'd.

Thanks. I've fixed this as well as the CPU mask issue you identified
earlier.

---8<---
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: 5068c7a883d1 ("crypto: pcrypt - Add pcrypt crypto parallelization wrapper")
Signed-off-by: Herbert Xu <[email protected]>

diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c
index 81bbea7f2ba6..3e026e7a7e75 100644
--- a/crypto/pcrypt.c
+++ b/crypto/pcrypt.c
@@ -24,6 +24,8 @@ static struct kset *pcrypt_kset;

struct pcrypt_instance_ctx {
struct crypto_aead_spawn spawn;
+ struct padata_shell *psenc;
+ struct padata_shell *psdec;
atomic_t tfm_count;
};

@@ -32,6 +34,12 @@ struct pcrypt_aead_ctx {
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,
const u8 *key, unsigned int keylen)
{
@@ -90,6 +98,9 @@ static int pcrypt_aead_encrypt(struct aead_request *req)
struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(aead);
u32 flags = aead_request_flags(req);
+ struct pcrypt_instance_ctx *ictx;
+
+ ictx = pcrypt_tfm_ictx(aead);

memset(padata, 0, sizeof(struct padata_priv));

@@ -103,7 +114,7 @@ static int pcrypt_aead_encrypt(struct aead_request *req)
req->cryptlen, req->iv);
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)
return -EINPROGRESS;

@@ -132,6 +143,9 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(aead);
u32 flags = aead_request_flags(req);
+ struct pcrypt_instance_ctx *ictx;
+
+ ictx = pcrypt_tfm_ictx(aead);

memset(padata, 0, sizeof(struct padata_priv));

@@ -145,7 +159,7 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
req->cryptlen, req->iv);
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)
return -EINPROGRESS;

@@ -192,6 +206,8 @@ static void pcrypt_free(struct aead_instance *inst)
struct pcrypt_instance_ctx *ctx = aead_instance_ctx(inst);

crypto_drop_aead(&ctx->spawn);
+ padata_free_shell(ctx->psdec);
+ padata_free_shell(ctx->psenc);
kfree(inst);
}

@@ -233,12 +249,22 @@ static int pcrypt_create_aead(struct crypto_template *tmpl, struct rtattr **tb,
if (!inst)
return -ENOMEM;

+ err = -ENOMEM;
+
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));

err = crypto_grab_aead(&ctx->spawn, name, 0, 0);
if (err)
- goto out_free_inst;
+ goto out_free_psdec;

alg = crypto_spawn_aead_alg(&ctx->spawn);
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,

out_drop_aead:
crypto_drop_aead(&ctx->spawn);
+out_free_psdec:
+ padata_free_shell(ctx->psdec);
+out_free_psenc:
+ padata_free_shell(ctx->psenc);
out_free_inst:
kfree(inst);
goto out;
diff --git a/include/linux/padata.h b/include/linux/padata.h
index 23717eeaad23..cccab7a59787 100644
--- a/include/linux/padata.h
+++ b/include/linux/padata.h
@@ -9,6 +9,7 @@
#ifndef PADATA_H
#define PADATA_H

+#include <linux/compiler_types.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/list.h>
@@ -98,7 +99,7 @@ struct padata_cpumask {
* struct parallel_data - Internal control structure, covers everything
* that depends on the cpumask in use.
*
- * @pinst: padata instance.
+ * @sh: padata_shell object.
* @pqueue: percpu padata queues used for parallelization.
* @squeue: percpu padata queues used for serialuzation.
* @reorder_objects: Number of objects waiting in the reorder queues.
@@ -111,7 +112,7 @@ struct padata_cpumask {
* @lock: Reorder lock.
*/
struct parallel_data {
- struct padata_instance *pinst;
+ struct padata_shell *ps;
struct padata_parallel_queue __percpu *pqueue;
struct padata_serial_queue __percpu *squeue;
atomic_t reorder_objects;
@@ -125,13 +126,32 @@ struct parallel_data {
};

/**
+ * 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.
*
* @cpu_notifier: cpu hotplug notifier.
* @parallel_wq: The workqueue used for parallel 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.
+ * @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
* callbacks that will be called when either @pcpu or @cbcpu
* or both cpumasks change.
@@ -143,8 +163,10 @@ struct padata_instance {
struct hlist_node node;
struct workqueue_struct *parallel_wq;
struct workqueue_struct *serial_wq;
- struct parallel_data *pd;
+ struct list_head pslist;
struct padata_cpumask cpumask;
+ struct padata_cpumask rcpumask;
+ cpumask_var_t omask;
struct blocking_notifier_head cpumask_change_notifier;
struct kobject kobj;
struct mutex lock;
@@ -156,7 +178,9 @@ struct padata_instance {

extern struct padata_instance *padata_alloc_possible(const char *name);
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);
extern void padata_do_serial(struct padata_priv *padata);
extern int padata_set_cpumask(struct padata_instance *pinst, int cpumask_type,
diff --git a/kernel/padata.c b/kernel/padata.c
index da56a235a255..0b64f36d0ea2 100644
--- a/kernel/padata.c
+++ b/kernel/padata.c
@@ -89,7 +89,7 @@ static void padata_parallel_worker(struct work_struct *parallel_work)
/**
* padata_do_parallel - padata parallelization function
*
- * @pinst: padata instance
+ * @ps: padatashell
* @padata: object to be parallelized
* @cb_cpu: pointer to the CPU that the serialization callback function should
* run on. If it's not in the serial cpumask of @pinst
@@ -100,16 +100,17 @@ static void padata_parallel_worker(struct work_struct *parallel_work)
* Note: Every object which is parallelized by padata_do_parallel
* must be seen by padata_do_serial.
*/
-int padata_do_parallel(struct padata_instance *pinst,
+int padata_do_parallel(struct padata_shell *ps,
struct padata_priv *padata, int *cb_cpu)
{
+ struct padata_instance *pinst = ps->pinst;
int i, cpu, cpu_index, target_cpu, err;
struct padata_parallel_queue *queue;
struct parallel_data *pd;

rcu_read_lock_bh();

- pd = rcu_dereference_bh(pinst->pd);
+ pd = rcu_dereference_bh(ps->pd);

err = -EINVAL;
if (!(pinst->flags & PADATA_INIT) || pinst->flags & PADATA_INVALID)
@@ -212,10 +213,10 @@ static struct padata_priv *padata_find_next(struct parallel_data *pd,

static void padata_reorder(struct parallel_data *pd)
{
+ struct padata_instance *pinst = pd->ps->pinst;
int cb_cpu;
struct padata_priv *padata;
struct padata_serial_queue *squeue;
- struct padata_instance *pinst = pd->pinst;
struct padata_parallel_queue *next_queue;

/*
@@ -349,36 +350,39 @@ void padata_do_serial(struct padata_priv *padata)
}
EXPORT_SYMBOL(padata_do_serial);

-static int padata_setup_cpumasks(struct parallel_data *pd,
- const struct cpumask *pcpumask,
- const struct cpumask *cbcpumask)
+static int padata_setup_cpumasks(struct padata_instance *pinst)
{
struct workqueue_attrs *attrs;
+ int err;
+
+ attrs = alloc_workqueue_attrs();
+ if (!attrs)
+ return -ENOMEM;
+
+ /* Restrict parallel_wq workers to pd->cpumask.pcpu. */
+ cpumask_copy(attrs->cpumask, pinst->cpumask.pcpu);
+ err = apply_workqueue_attrs(pinst->parallel_wq, attrs);
+ free_workqueue_attrs(attrs);
+
+ return err;
+}
+
+static int pd_setup_cpumasks(struct parallel_data *pd,
+ const struct cpumask *pcpumask,
+ const struct cpumask *cbcpumask)
+{
int err = -ENOMEM;

if (!alloc_cpumask_var(&pd->cpumask.pcpu, GFP_KERNEL))
goto out;
- cpumask_and(pd->cpumask.pcpu, pcpumask, cpu_online_mask);
-
if (!alloc_cpumask_var(&pd->cpumask.cbcpu, GFP_KERNEL))
goto free_pcpu_mask;
- cpumask_and(pd->cpumask.cbcpu, cbcpumask, cpu_online_mask);
-
- attrs = alloc_workqueue_attrs();
- if (!attrs)
- goto free_cbcpu_mask;

- /* Restrict parallel_wq workers to pd->cpumask.pcpu. */
- cpumask_copy(attrs->cpumask, pd->cpumask.pcpu);
- err = apply_workqueue_attrs(pd->pinst->parallel_wq, attrs);
- free_workqueue_attrs(attrs);
- if (err < 0)
- goto free_cbcpu_mask;
+ cpumask_copy(pd->cpumask.pcpu, pcpumask);
+ cpumask_copy(pd->cpumask.cbcpu, cbcpumask);

return 0;

-free_cbcpu_mask:
- free_cpumask_var(pd->cpumask.cbcpu);
free_pcpu_mask:
free_cpumask_var(pd->cpumask.pcpu);
out:
@@ -422,12 +426,16 @@ static void padata_init_pqueues(struct parallel_data *pd)
}

/* Allocate and initialize the internal cpumask dependend resources. */
-static struct parallel_data *padata_alloc_pd(struct padata_instance *pinst,
- const struct cpumask *pcpumask,
- const struct cpumask *cbcpumask)
+static struct parallel_data *padata_alloc_pd(struct padata_shell *ps)
{
+ struct padata_instance *pinst = ps->pinst;
+ const struct cpumask *cbcpumask;
+ const struct cpumask *pcpumask;
struct parallel_data *pd;

+ cbcpumask = pinst->rcpumask.cbcpu;
+ pcpumask = pinst->rcpumask.pcpu;
+
pd = kzalloc(sizeof(struct parallel_data), GFP_KERNEL);
if (!pd)
goto err;
@@ -440,8 +448,8 @@ static struct parallel_data *padata_alloc_pd(struct padata_instance *pinst,
if (!pd->squeue)
goto err_free_pqueue;

- pd->pinst = pinst;
- if (padata_setup_cpumasks(pd, pcpumask, cbcpumask) < 0)
+ pd->ps = ps;
+ if (pd_setup_cpumasks(pd, pcpumask, cbcpumask))
goto err_free_squeue;

padata_init_pqueues(pd);
@@ -490,32 +498,64 @@ static void __padata_stop(struct padata_instance *pinst)
}

/* Replace the internal control structure with a new one. */
-static void padata_replace(struct padata_instance *pinst,
- struct parallel_data *pd_new)
+static int padata_replace_one(struct padata_shell *ps)
{
- struct parallel_data *pd_old = pinst->pd;
- int notification_mask = 0;
+ struct parallel_data *pd_new;

- pinst->flags |= PADATA_RESET;
+ pd_new = padata_alloc_pd(ps);
+ if (!pd_new)
+ return -ENOMEM;

- rcu_assign_pointer(pinst->pd, pd_new);
+ ps->opd = rcu_dereference_protected(ps->pd, 1);
+ rcu_assign_pointer(ps->pd, pd_new);

- synchronize_rcu();
+ return 0;
+}
+
+static int padata_replace(struct padata_instance *pinst, int cpu)
+{
+ int notification_mask = 0;
+ struct padata_shell *ps;
+ int err;
+
+ pinst->flags |= PADATA_RESET;

- if (!cpumask_equal(pd_old->cpumask.pcpu, pd_new->cpumask.pcpu))
+ cpumask_copy(pinst->omask, pinst->rcpumask.pcpu);
+ cpumask_and(pinst->rcpumask.pcpu, pinst->cpumask.pcpu,
+ cpu_online_mask);
+ if (cpu >= 0)
+ cpumask_clear_cpu(cpu, pinst->rcpumask.pcpu);
+ if (!cpumask_equal(pinst->omask, pinst->rcpumask.pcpu))
notification_mask |= PADATA_CPU_PARALLEL;
- if (!cpumask_equal(pd_old->cpumask.cbcpu, pd_new->cpumask.cbcpu))
+
+ cpumask_copy(pinst->omask, pinst->rcpumask.cbcpu);
+ cpumask_and(pinst->rcpumask.cbcpu, pinst->cpumask.cbcpu,
+ cpu_online_mask);
+ if (cpu >= 0)
+ cpumask_clear_cpu(cpu, pinst->rcpumask.cbcpu);
+ if (!cpumask_equal(pinst->omask, pinst->rcpumask.cbcpu))
notification_mask |= PADATA_CPU_SERIAL;

- if (atomic_dec_and_test(&pd_old->refcnt))
- padata_free_pd(pd_old);
+ list_for_each_entry(ps, &pinst->pslist, list) {
+ err = padata_replace_one(ps);
+ if (err)
+ break;
+ }
+
+ synchronize_rcu();
+
+ list_for_each_entry_continue_reverse(ps, &pinst->pslist, list)
+ if (atomic_dec_and_test(&ps->opd->refcnt))
+ padata_free_pd(ps->opd);

if (notification_mask)
blocking_notifier_call_chain(&pinst->cpumask_change_notifier,
notification_mask,
- &pd_new->cpumask);
+ &pinst->cpumask);

pinst->flags &= ~PADATA_RESET;
+
+ return err;
}

/**
@@ -568,7 +608,7 @@ static int __padata_set_cpumasks(struct padata_instance *pinst,
cpumask_var_t cbcpumask)
{
int valid;
- struct parallel_data *pd;
+ int err;

valid = padata_validate_cpumask(pinst, pcpumask);
if (!valid) {
@@ -581,19 +621,15 @@ static int __padata_set_cpumasks(struct padata_instance *pinst,
__padata_stop(pinst);

out_replace:
- pd = padata_alloc_pd(pinst, pcpumask, cbcpumask);
- if (!pd)
- return -ENOMEM;
-
cpumask_copy(pinst->cpumask.pcpu, pcpumask);
cpumask_copy(pinst->cpumask.cbcpu, cbcpumask);

- padata_replace(pinst, pd);
+ err = padata_setup_cpumasks(pinst) ?: padata_replace(pinst, -1);

if (valid)
__padata_start(pinst);

- return 0;
+ return err;
}

/**
@@ -676,46 +712,32 @@ EXPORT_SYMBOL(padata_stop);

static int __padata_add_cpu(struct padata_instance *pinst, int cpu)
{
- struct parallel_data *pd;
+ int err = 0;

if (cpumask_test_cpu(cpu, cpu_online_mask)) {
- pd = padata_alloc_pd(pinst, pinst->cpumask.pcpu,
- pinst->cpumask.cbcpu);
- if (!pd)
- return -ENOMEM;
-
- padata_replace(pinst, pd);
+ err = padata_replace(pinst, -1);

if (padata_validate_cpumask(pinst, pinst->cpumask.pcpu) &&
padata_validate_cpumask(pinst, pinst->cpumask.cbcpu))
__padata_start(pinst);
}

- return 0;
+ return err;
}

static int __padata_remove_cpu(struct padata_instance *pinst, int cpu)
{
- struct parallel_data *pd = NULL;
+ int err = 0;

if (cpumask_test_cpu(cpu, cpu_online_mask)) {
-
if (!padata_validate_cpumask(pinst, pinst->cpumask.pcpu) ||
!padata_validate_cpumask(pinst, pinst->cpumask.cbcpu))
__padata_stop(pinst);

- pd = padata_alloc_pd(pinst, pinst->cpumask.pcpu,
- pinst->cpumask.cbcpu);
- if (!pd)
- return -ENOMEM;
-
- padata_replace(pinst, pd);
-
- cpumask_clear_cpu(cpu, pd->cpumask.cbcpu);
- cpumask_clear_cpu(cpu, pd->cpumask.pcpu);
+ err = padata_replace(pinst, cpu);
}

- return 0;
+ return err;
}

/**
@@ -798,8 +820,12 @@ static void __padata_free(struct padata_instance *pinst)
cpuhp_state_remove_instance_nocalls(hp_online, &pinst->node);
#endif

+ WARN_ON(!list_empty(&pinst->pslist));
+
padata_stop(pinst);
- padata_free_pd(pinst->pd);
+ free_cpumask_var(pinst->omask);
+ free_cpumask_var(pinst->rcpumask.cbcpu);
+ free_cpumask_var(pinst->rcpumask.pcpu);
free_cpumask_var(pinst->cpumask.pcpu);
free_cpumask_var(pinst->cpumask.cbcpu);
destroy_workqueue(pinst->serial_wq);
@@ -946,7 +972,6 @@ static struct padata_instance *padata_alloc(const char *name,
const struct cpumask *cbcpumask)
{
struct padata_instance *pinst;
- struct parallel_data *pd = NULL;

pinst = kzalloc(sizeof(struct padata_instance), GFP_KERNEL);
if (!pinst)
@@ -974,14 +999,22 @@ static struct padata_instance *padata_alloc(const char *name,
!padata_validate_cpumask(pinst, cbcpumask))
goto err_free_masks;

- pd = padata_alloc_pd(pinst, pcpumask, cbcpumask);
- if (!pd)
+ if (!alloc_cpumask_var(&pinst->rcpumask.pcpu, GFP_KERNEL))
goto err_free_masks;
+ if (!alloc_cpumask_var(&pinst->rcpumask.cbcpu, GFP_KERNEL))
+ goto err_free_rcpumask_pcpu;
+ if (!alloc_cpumask_var(&pinst->omask, GFP_KERNEL))
+ goto err_free_rcpumask_cbcpu;

- rcu_assign_pointer(pinst->pd, pd);
+ INIT_LIST_HEAD(&pinst->pslist);

cpumask_copy(pinst->cpumask.pcpu, pcpumask);
cpumask_copy(pinst->cpumask.cbcpu, cbcpumask);
+ cpumask_and(pinst->rcpumask.pcpu, pcpumask, cpu_online_mask);
+ cpumask_and(pinst->rcpumask.cbcpu, cbcpumask, cpu_online_mask);
+
+ if (padata_setup_cpumasks(pinst))
+ goto err_free_omask;

pinst->flags = 0;

@@ -997,6 +1030,12 @@ static struct padata_instance *padata_alloc(const char *name,

return pinst;

+err_free_omask:
+ free_cpumask_var(pinst->omask);
+err_free_rcpumask_cbcpu:
+ free_cpumask_var(pinst->rcpumask.cbcpu);
+err_free_rcpumask_pcpu:
+ free_cpumask_var(pinst->rcpumask.pcpu);
err_free_masks:
free_cpumask_var(pinst->cpumask.pcpu);
free_cpumask_var(pinst->cpumask.cbcpu);
@@ -1035,6 +1074,61 @@ void padata_free(struct padata_instance *pinst)
}
EXPORT_SYMBOL(padata_free);

+/**
+ * padata_alloc_shell - Allocate and initialize padata shell.
+ *
+ * @pinst: Parent padata_instance object.
+ */
+struct padata_shell *padata_alloc_shell(struct padata_instance *pinst)
+{
+ struct parallel_data *pd;
+ struct padata_shell *ps;
+
+ ps = kzalloc(sizeof(*ps), GFP_KERNEL);
+ if (!ps)
+ goto out;
+
+ ps->pinst = pinst;
+
+ get_online_cpus();
+ pd = padata_alloc_pd(ps);
+ put_online_cpus();
+
+ if (!pd)
+ goto out_free_ps;
+
+ mutex_lock(&pinst->lock);
+ RCU_INIT_POINTER(ps->pd, pd);
+ list_add(&ps->list, &pinst->pslist);
+ mutex_unlock(&pinst->lock);
+
+ return ps;
+
+out_free_ps:
+ kfree(ps);
+out:
+ return NULL;
+}
+EXPORT_SYMBOL(padata_alloc_shell);
+
+/**
+ * padata_free_shell - free a padata shell
+ *
+ * @ps: padata shell to free
+ */
+void padata_free_shell(struct padata_shell *ps)
+{
+ struct padata_instance *pinst = ps->pinst;
+
+ mutex_lock(&pinst->lock);
+ list_del(&ps->list);
+ padata_free_pd(rcu_dereference_protected(ps->pd, 1));
+ mutex_unlock(&pinst->lock);
+
+ kfree(ps);
+}
+EXPORT_SYMBOL(padata_free_shell);
+
#ifdef CONFIG_HOTPLUG_CPU

static __init int padata_driver_init(void)
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2019-11-27 19:15:33

by Eric Biggers

[permalink] [raw]
Subject: Re: [v3 PATCH] crypto: pcrypt - Avoid deadlock by using per-instance padata queues

Hi Herbert,

On Tue, Nov 26, 2019 at 03:58:45PM +0800, Herbert Xu wrote:
> On Tue, Nov 26, 2019 at 12:32:38AM -0500, Daniel Jordan wrote:
> >
> > I think it's possible for a task in padata_do_parallel() racing with another in
> > padata_replace() to use a pd after free. The synchronize_rcu() comes after the
> > pd_old->refcnt's are dec'd.
>
> Thanks. I've fixed this as well as the CPU mask issue you identified
> earlier.
>
> ---8<---
> 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: 5068c7a883d1 ("crypto: pcrypt - Add pcrypt crypto parallelization wrapper")
> Signed-off-by: Herbert Xu <[email protected]>

I tried applying the following patches and running syzkaller again:

padata: Remove unused padata_remove_cpu
padata: Remove broken queue flushing
crypto: pcrypt - Fix user-after-free on module unload
[v3] crypto: pcrypt - Avoid deadlock by using per-instance padata queues

This time I got a crypto self-test failure when
"pcrypt(pcrypt(rfc4106-gcm-aesni))" was instantiated:

[ 2220.165113] alg: aead: pcrypt(pcrypt(rfc4106-gcm-aesni)) encryption corrupted request struct on test vector 0, cfg="uneven misaligned splits, may sleep"
[ 2220.170295] alg: aead: changed 'req->base.flags'
[ 2220.171799] Kernel panic - not syncing: alg: self-tests for pcrypt(pcrypt(rfc4106-gcm-aesni)) (rfc4106(gcm(aes))) failed in panic_on_fail mode!

So the algorithm is not preserving aead_request::base.flags.

- Eric

2019-11-27 23:39:55

by Daniel Jordan

[permalink] [raw]
Subject: Re: [v3 PATCH] crypto: pcrypt - Avoid deadlock by using per-instance padata queues

On Tue, Nov 26, 2019 at 03:58:45PM +0800, Herbert Xu wrote:
> On Tue, Nov 26, 2019 at 12:32:38AM -0500, Daniel Jordan wrote:
> >
> > I think it's possible for a task in padata_do_parallel() racing with another in
> > padata_replace() to use a pd after free. The synchronize_rcu() comes after the
> > pd_old->refcnt's are dec'd.
>
> Thanks. I've fixed this as well as the CPU mask issue you identified
> earlier.

I spent some time reviewing and testing this today, and the changes to
padata.h/c look fine to me.

Daniel

2019-11-29 08:41:11

by Herbert Xu

[permalink] [raw]
Subject: [PATCH] crypto: pcrypt - Do not clear MAY_SLEEP flag in original request

On Wed, Nov 27, 2019 at 11:14:52AM -0800, Eric Biggers wrote:
>
> I tried applying the following patches and running syzkaller again:
>
> padata: Remove unused padata_remove_cpu
> padata: Remove broken queue flushing
> crypto: pcrypt - Fix user-after-free on module unload
> [v3] crypto: pcrypt - Avoid deadlock by using per-instance padata queues
>
> This time I got a crypto self-test failure when
> "pcrypt(pcrypt(rfc4106-gcm-aesni))" was instantiated:
>
> [ 2220.165113] alg: aead: pcrypt(pcrypt(rfc4106-gcm-aesni)) encryption corrupted request struct on test vector 0, cfg="uneven misaligned splits, may sleep"
> [ 2220.170295] alg: aead: changed 'req->base.flags'
> [ 2220.171799] Kernel panic - not syncing: alg: self-tests for pcrypt(pcrypt(rfc4106-gcm-aesni)) (rfc4106(gcm(aes))) failed in panic_on_fail mode!
>
> So the algorithm is not preserving aead_request::base.flags.

Thanks for the report. This is a preexisting bug in pcrypt. Here
is a patch for it.

---8<---
We should not be modifying the original request's MAY_SLEEP flag
upon completion. It makes no sense to do so anyway.

Reported-by: Eric Biggers <[email protected]>
Fixes: 5068c7a883d1 ("crypto: pcrypt - Add pcrypt crypto...")
Signed-off-by: Herbert Xu <[email protected]>

diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c
index 543792e0ebf0..2f6f81183e45 100644
--- a/crypto/pcrypt.c
+++ b/crypto/pcrypt.c
@@ -63,7 +63,6 @@ static void pcrypt_aead_done(struct crypto_async_request *areq, int err)
struct padata_priv *padata = pcrypt_request_padata(preq);

padata->info = err;
- req->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;

padata_do_serial(padata);
}
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2019-11-29 19:29:54

by Eric Biggers

[permalink] [raw]
Subject: Re: [v3 PATCH] crypto: pcrypt - Avoid deadlock by using per-instance padata queues

On Tue, Nov 26, 2019 at 03:58:45PM +0800, Herbert Xu wrote:
> On Tue, Nov 26, 2019 at 12:32:38AM -0500, Daniel Jordan wrote:
> >
> > I think it's possible for a task in padata_do_parallel() racing with another in
> > padata_replace() to use a pd after free. The synchronize_rcu() comes after the
> > pd_old->refcnt's are dec'd.
>
> Thanks. I've fixed this as well as the CPU mask issue you identified
> earlier.
>
> ---8<---
> 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: 5068c7a883d1 ("crypto: pcrypt - Add pcrypt crypto parallelization wrapper")
> Signed-off-by: Herbert Xu <[email protected]>
>

Tested-by: Eric Biggers <[email protected]>

2019-11-29 19:30:26

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH] crypto: pcrypt - Do not clear MAY_SLEEP flag in original request

On Fri, Nov 29, 2019 at 04:40:24PM +0800, Herbert Xu wrote:
> On Wed, Nov 27, 2019 at 11:14:52AM -0800, Eric Biggers wrote:
> >
> > I tried applying the following patches and running syzkaller again:
> >
> > padata: Remove unused padata_remove_cpu
> > padata: Remove broken queue flushing
> > crypto: pcrypt - Fix user-after-free on module unload
> > [v3] crypto: pcrypt - Avoid deadlock by using per-instance padata queues
> >
> > This time I got a crypto self-test failure when
> > "pcrypt(pcrypt(rfc4106-gcm-aesni))" was instantiated:
> >
> > [ 2220.165113] alg: aead: pcrypt(pcrypt(rfc4106-gcm-aesni)) encryption corrupted request struct on test vector 0, cfg="uneven misaligned splits, may sleep"
> > [ 2220.170295] alg: aead: changed 'req->base.flags'
> > [ 2220.171799] Kernel panic - not syncing: alg: self-tests for pcrypt(pcrypt(rfc4106-gcm-aesni)) (rfc4106(gcm(aes))) failed in panic_on_fail mode!
> >
> > So the algorithm is not preserving aead_request::base.flags.
>
> Thanks for the report. This is a preexisting bug in pcrypt. Here
> is a patch for it.
>
> ---8<---
> We should not be modifying the original request's MAY_SLEEP flag
> upon completion. It makes no sense to do so anyway.
>
> Reported-by: Eric Biggers <[email protected]>
> Fixes: 5068c7a883d1 ("crypto: pcrypt - Add pcrypt crypto...")
> Signed-off-by: Herbert Xu <[email protected]>
>
> diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c
> index 543792e0ebf0..2f6f81183e45 100644
> --- a/crypto/pcrypt.c
> +++ b/crypto/pcrypt.c
> @@ -63,7 +63,6 @@ static void pcrypt_aead_done(struct crypto_async_request *areq, int err)
> struct padata_priv *padata = pcrypt_request_padata(preq);
>
> padata->info = err;
> - req->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
>
> padata_do_serial(padata);
> }

Tested-by: Eric Biggers <[email protected]>