Message ID | 20200630231000.1576771-1-mfo@canonical.com |
---|---|
State | New |
Headers | show |
Series | [D] crypto: af_alg - fix use-after-free in af_alg_accept() due to bh_lock_sock() | expand |
On 01.07.20 01:10, Mauricio Faria de Oliveira wrote: > From: Herbert Xu <herbert@gondor.apana.org.au> > > BugLink: https://bugs.launchpad.net/bugs/1884766 > > The locking in af_alg_release_parent is broken as the BH socket > lock can only be taken if there is a code-path to handle the case > where the lock is owned by process-context. Instead of adding > such handling, we can fix this by changing the ref counts to > atomic_t. > > This patch also modifies the main refcnt to include both normal > and nokey sockets. This way we don't have to fudge the nokey > ref count when a socket changes from nokey to normal. > > Credits go to Mauricio Faria de Oliveira who diagnosed this bug > and sent a patch for it: > > https://lore.kernel.org/linux-crypto/20200605161657.535043-1-mfo@canonical.com/ > > Reported-by: Brian Moyles <bmoyles@netflix.com> > Reported-by: Mauricio Faria de Oliveira <mfo@canonical.com> > Fixes: 37f96694cf73 ("crypto: af_alg - Use bh_lock_sock in...") > Cc: <stable@vger.kernel.org> > Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> > (backported from commit 34c86f4c4a7be3b3e35aa48bd18299d4c756064d) > [mfo: backport: refresh context lines in af_alg.c hunk 4] > Signed-off-by: Mauricio Faria de Oliveira <mfo@canonical.com> Acked-by: Stefan Bader <stefan.bader@canonical.com> > --- > crypto/af_alg.c | 26 +++++++++++--------------- > crypto/algif_aead.c | 9 +++------ > crypto/algif_hash.c | 9 +++------ > crypto/algif_skcipher.c | 9 +++------ > include/crypto/if_alg.h | 4 ++-- > 5 files changed, 22 insertions(+), 35 deletions(-) > > diff --git a/crypto/af_alg.c b/crypto/af_alg.c > index 4fc8e6a7abb2..272879d7b0d1 100644 > --- a/crypto/af_alg.c > +++ b/crypto/af_alg.c > @@ -133,21 +133,15 @@ EXPORT_SYMBOL_GPL(af_alg_release); > void af_alg_release_parent(struct sock *sk) > { > struct alg_sock *ask = alg_sk(sk); > - unsigned int nokey = ask->nokey_refcnt; > - bool last = nokey && !ask->refcnt; > + unsigned int nokey = atomic_read(&ask->nokey_refcnt); > > sk = ask->parent; > ask = alg_sk(sk); > > - local_bh_disable(); > - bh_lock_sock(sk); > - ask->nokey_refcnt -= nokey; > - if (!last) > - last = !--ask->refcnt; > - bh_unlock_sock(sk); > - local_bh_enable(); > + if (nokey) > + atomic_dec(&ask->nokey_refcnt); > > - if (last) > + if (atomic_dec_and_test(&ask->refcnt)) > sock_put(sk); > } > EXPORT_SYMBOL_GPL(af_alg_release_parent); > @@ -192,7 +186,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) > > err = -EBUSY; > lock_sock(sk); > - if (ask->refcnt | ask->nokey_refcnt) > + if (atomic_read(&ask->refcnt)) > goto unlock; > > swap(ask->type, type); > @@ -241,7 +235,7 @@ static int alg_setsockopt(struct socket *sock, int level, int optname, > int err = -EBUSY; > > lock_sock(sk); > - if (ask->refcnt) > + if (atomic_read(&ask->refcnt) != atomic_read(&ask->nokey_refcnt)) > goto unlock; > > type = ask->type; > @@ -308,12 +302,14 @@ int af_alg_accept(struct sock *sk, struct socket *newsock, bool kern) > > sk2->sk_family = PF_ALG; > > - if (nokey || !ask->refcnt++) > + if (atomic_inc_return_relaxed(&ask->refcnt) == 1) > sock_hold(sk); > - ask->nokey_refcnt += nokey; > + if (nokey) { > + atomic_inc(&ask->nokey_refcnt); > + atomic_set(&alg_sk(sk2)->nokey_refcnt, 1); > + } > 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/crypto/algif_aead.c b/crypto/algif_aead.c > index eb100a04ce9f..e643d268c7ba 100644 > --- a/crypto/algif_aead.c > +++ b/crypto/algif_aead.c > @@ -388,7 +388,7 @@ static int aead_check_key(struct socket *sock) > struct alg_sock *ask = alg_sk(sk); > > lock_sock(sk); > - if (ask->refcnt) > + if (!atomic_read(&ask->nokey_refcnt)) > goto unlock_child; > > psk = ask->parent; > @@ -400,11 +400,8 @@ static int aead_check_key(struct socket *sock) > if (crypto_aead_get_flags(tfm->aead) & CRYPTO_TFM_NEED_KEY) > goto unlock; > > - if (!pask->refcnt++) > - sock_hold(psk); > - > - ask->refcnt = 1; > - sock_put(psk); > + atomic_dec(&pask->nokey_refcnt); > + atomic_set(&ask->nokey_refcnt, 0); > > err = 0; > > diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c > index d0cde541beb6..73abe6a33f3a 100644 > --- a/crypto/algif_hash.c > +++ b/crypto/algif_hash.c > @@ -306,7 +306,7 @@ static int hash_check_key(struct socket *sock) > struct alg_sock *ask = alg_sk(sk); > > lock_sock(sk); > - if (ask->refcnt) > + if (!atomic_read(&ask->nokey_refcnt)) > goto unlock_child; > > psk = ask->parent; > @@ -318,11 +318,8 @@ static int hash_check_key(struct socket *sock) > if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) > goto unlock; > > - if (!pask->refcnt++) > - sock_hold(psk); > - > - ask->refcnt = 1; > - sock_put(psk); > + atomic_dec(&pask->nokey_refcnt); > + atomic_set(&ask->nokey_refcnt, 0); > > err = 0; > > diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c > index cfdaab2b7d76..75444b8beed9 100644 > --- a/crypto/algif_skcipher.c > +++ b/crypto/algif_skcipher.c > @@ -219,7 +219,7 @@ static int skcipher_check_key(struct socket *sock) > struct alg_sock *ask = alg_sk(sk); > > lock_sock(sk); > - if (ask->refcnt) > + if (!atomic_read(&ask->nokey_refcnt)) > goto unlock_child; > > psk = ask->parent; > @@ -231,11 +231,8 @@ static int skcipher_check_key(struct socket *sock) > if (crypto_skcipher_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) > goto unlock; > > - if (!pask->refcnt++) > - sock_hold(psk); > - > - ask->refcnt = 1; > - sock_put(psk); > + atomic_dec(&pask->nokey_refcnt); > + atomic_set(&ask->nokey_refcnt, 0); > > err = 0; > > diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h > index 482461d8931d..11f107df78dc 100644 > --- a/include/crypto/if_alg.h > +++ b/include/crypto/if_alg.h > @@ -34,8 +34,8 @@ struct alg_sock { > > struct sock *parent; > > - unsigned int refcnt; > - unsigned int nokey_refcnt; > + atomic_t refcnt; > + atomic_t nokey_refcnt; > > const struct af_alg_type *type; > void *private; >
On Tue, Jun 30, 2020 at 08:10:00PM -0300, Mauricio Faria de Oliveira wrote: > From: Herbert Xu <herbert@gondor.apana.org.au> > > BugLink: https://bugs.launchpad.net/bugs/1884766 > > The locking in af_alg_release_parent is broken as the BH socket > lock can only be taken if there is a code-path to handle the case > where the lock is owned by process-context. Instead of adding > such handling, we can fix this by changing the ref counts to > atomic_t. > > This patch also modifies the main refcnt to include both normal > and nokey sockets. This way we don't have to fudge the nokey > ref count when a socket changes from nokey to normal. > > Credits go to Mauricio Faria de Oliveira who diagnosed this bug > and sent a patch for it: > > https://lore.kernel.org/linux-crypto/20200605161657.535043-1-mfo@canonical.com/ > > Reported-by: Brian Moyles <bmoyles@netflix.com> > Reported-by: Mauricio Faria de Oliveira <mfo@canonical.com> > Fixes: 37f96694cf73 ("crypto: af_alg - Use bh_lock_sock in...") > Cc: <stable@vger.kernel.org> > Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> > (backported from commit 34c86f4c4a7be3b3e35aa48bd18299d4c756064d) > [mfo: backport: refresh context lines in af_alg.c hunk 4] > Signed-off-by: Mauricio Faria de Oliveira <mfo@canonical.com> > --- Acked-by: Andrea Righi <andrea.righi@canonical.com> > crypto/af_alg.c | 26 +++++++++++--------------- > crypto/algif_aead.c | 9 +++------ > crypto/algif_hash.c | 9 +++------ > crypto/algif_skcipher.c | 9 +++------ > include/crypto/if_alg.h | 4 ++-- > 5 files changed, 22 insertions(+), 35 deletions(-) > > diff --git a/crypto/af_alg.c b/crypto/af_alg.c > index 4fc8e6a7abb2..272879d7b0d1 100644 > --- a/crypto/af_alg.c > +++ b/crypto/af_alg.c > @@ -133,21 +133,15 @@ EXPORT_SYMBOL_GPL(af_alg_release); > void af_alg_release_parent(struct sock *sk) > { > struct alg_sock *ask = alg_sk(sk); > - unsigned int nokey = ask->nokey_refcnt; > - bool last = nokey && !ask->refcnt; > + unsigned int nokey = atomic_read(&ask->nokey_refcnt); > > sk = ask->parent; > ask = alg_sk(sk); > > - local_bh_disable(); > - bh_lock_sock(sk); > - ask->nokey_refcnt -= nokey; > - if (!last) > - last = !--ask->refcnt; > - bh_unlock_sock(sk); > - local_bh_enable(); > + if (nokey) > + atomic_dec(&ask->nokey_refcnt); > > - if (last) > + if (atomic_dec_and_test(&ask->refcnt)) > sock_put(sk); > } > EXPORT_SYMBOL_GPL(af_alg_release_parent); > @@ -192,7 +186,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) > > err = -EBUSY; > lock_sock(sk); > - if (ask->refcnt | ask->nokey_refcnt) > + if (atomic_read(&ask->refcnt)) > goto unlock; > > swap(ask->type, type); > @@ -241,7 +235,7 @@ static int alg_setsockopt(struct socket *sock, int level, int optname, > int err = -EBUSY; > > lock_sock(sk); > - if (ask->refcnt) > + if (atomic_read(&ask->refcnt) != atomic_read(&ask->nokey_refcnt)) > goto unlock; > > type = ask->type; > @@ -308,12 +302,14 @@ int af_alg_accept(struct sock *sk, struct socket *newsock, bool kern) > > sk2->sk_family = PF_ALG; > > - if (nokey || !ask->refcnt++) > + if (atomic_inc_return_relaxed(&ask->refcnt) == 1) > sock_hold(sk); > - ask->nokey_refcnt += nokey; > + if (nokey) { > + atomic_inc(&ask->nokey_refcnt); > + atomic_set(&alg_sk(sk2)->nokey_refcnt, 1); > + } > 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/crypto/algif_aead.c b/crypto/algif_aead.c > index eb100a04ce9f..e643d268c7ba 100644 > --- a/crypto/algif_aead.c > +++ b/crypto/algif_aead.c > @@ -388,7 +388,7 @@ static int aead_check_key(struct socket *sock) > struct alg_sock *ask = alg_sk(sk); > > lock_sock(sk); > - if (ask->refcnt) > + if (!atomic_read(&ask->nokey_refcnt)) > goto unlock_child; > > psk = ask->parent; > @@ -400,11 +400,8 @@ static int aead_check_key(struct socket *sock) > if (crypto_aead_get_flags(tfm->aead) & CRYPTO_TFM_NEED_KEY) > goto unlock; > > - if (!pask->refcnt++) > - sock_hold(psk); > - > - ask->refcnt = 1; > - sock_put(psk); > + atomic_dec(&pask->nokey_refcnt); > + atomic_set(&ask->nokey_refcnt, 0); > > err = 0; > > diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c > index d0cde541beb6..73abe6a33f3a 100644 > --- a/crypto/algif_hash.c > +++ b/crypto/algif_hash.c > @@ -306,7 +306,7 @@ static int hash_check_key(struct socket *sock) > struct alg_sock *ask = alg_sk(sk); > > lock_sock(sk); > - if (ask->refcnt) > + if (!atomic_read(&ask->nokey_refcnt)) > goto unlock_child; > > psk = ask->parent; > @@ -318,11 +318,8 @@ static int hash_check_key(struct socket *sock) > if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) > goto unlock; > > - if (!pask->refcnt++) > - sock_hold(psk); > - > - ask->refcnt = 1; > - sock_put(psk); > + atomic_dec(&pask->nokey_refcnt); > + atomic_set(&ask->nokey_refcnt, 0); > > err = 0; > > diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c > index cfdaab2b7d76..75444b8beed9 100644 > --- a/crypto/algif_skcipher.c > +++ b/crypto/algif_skcipher.c > @@ -219,7 +219,7 @@ static int skcipher_check_key(struct socket *sock) > struct alg_sock *ask = alg_sk(sk); > > lock_sock(sk); > - if (ask->refcnt) > + if (!atomic_read(&ask->nokey_refcnt)) > goto unlock_child; > > psk = ask->parent; > @@ -231,11 +231,8 @@ static int skcipher_check_key(struct socket *sock) > if (crypto_skcipher_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) > goto unlock; > > - if (!pask->refcnt++) > - sock_hold(psk); > - > - ask->refcnt = 1; > - sock_put(psk); > + atomic_dec(&pask->nokey_refcnt); > + atomic_set(&ask->nokey_refcnt, 0); > > err = 0; > > diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h > index 482461d8931d..11f107df78dc 100644 > --- a/include/crypto/if_alg.h > +++ b/include/crypto/if_alg.h > @@ -34,8 +34,8 @@ struct alg_sock { > > struct sock *parent; > > - unsigned int refcnt; > - unsigned int nokey_refcnt; > + atomic_t refcnt; > + atomic_t nokey_refcnt; > > const struct af_alg_type *type; > void *private; > -- > 2.25.1 > > > -- > kernel-team mailing list > kernel-team@lists.ubuntu.com > https://lists.ubuntu.com/mailman/listinfo/kernel-team
Applied to Disco/master-next. Thank you! -Kelsey On 2020-06-30 20:10:00 , Mauricio Faria de Oliveira wrote: > From: Herbert Xu <herbert@gondor.apana.org.au> > > BugLink: https://bugs.launchpad.net/bugs/1884766 > > The locking in af_alg_release_parent is broken as the BH socket > lock can only be taken if there is a code-path to handle the case > where the lock is owned by process-context. Instead of adding > such handling, we can fix this by changing the ref counts to > atomic_t. > > This patch also modifies the main refcnt to include both normal > and nokey sockets. This way we don't have to fudge the nokey > ref count when a socket changes from nokey to normal. > > Credits go to Mauricio Faria de Oliveira who diagnosed this bug > and sent a patch for it: > > https://lore.kernel.org/linux-crypto/20200605161657.535043-1-mfo@canonical.com/ > > Reported-by: Brian Moyles <bmoyles@netflix.com> > Reported-by: Mauricio Faria de Oliveira <mfo@canonical.com> > Fixes: 37f96694cf73 ("crypto: af_alg - Use bh_lock_sock in...") > Cc: <stable@vger.kernel.org> > Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> > (backported from commit 34c86f4c4a7be3b3e35aa48bd18299d4c756064d) > [mfo: backport: refresh context lines in af_alg.c hunk 4] > Signed-off-by: Mauricio Faria de Oliveira <mfo@canonical.com> > --- > crypto/af_alg.c | 26 +++++++++++--------------- > crypto/algif_aead.c | 9 +++------ > crypto/algif_hash.c | 9 +++------ > crypto/algif_skcipher.c | 9 +++------ > include/crypto/if_alg.h | 4 ++-- > 5 files changed, 22 insertions(+), 35 deletions(-) > > diff --git a/crypto/af_alg.c b/crypto/af_alg.c > index 4fc8e6a7abb2..272879d7b0d1 100644 > --- a/crypto/af_alg.c > +++ b/crypto/af_alg.c > @@ -133,21 +133,15 @@ EXPORT_SYMBOL_GPL(af_alg_release); > void af_alg_release_parent(struct sock *sk) > { > struct alg_sock *ask = alg_sk(sk); > - unsigned int nokey = ask->nokey_refcnt; > - bool last = nokey && !ask->refcnt; > + unsigned int nokey = atomic_read(&ask->nokey_refcnt); > > sk = ask->parent; > ask = alg_sk(sk); > > - local_bh_disable(); > - bh_lock_sock(sk); > - ask->nokey_refcnt -= nokey; > - if (!last) > - last = !--ask->refcnt; > - bh_unlock_sock(sk); > - local_bh_enable(); > + if (nokey) > + atomic_dec(&ask->nokey_refcnt); > > - if (last) > + if (atomic_dec_and_test(&ask->refcnt)) > sock_put(sk); > } > EXPORT_SYMBOL_GPL(af_alg_release_parent); > @@ -192,7 +186,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) > > err = -EBUSY; > lock_sock(sk); > - if (ask->refcnt | ask->nokey_refcnt) > + if (atomic_read(&ask->refcnt)) > goto unlock; > > swap(ask->type, type); > @@ -241,7 +235,7 @@ static int alg_setsockopt(struct socket *sock, int level, int optname, > int err = -EBUSY; > > lock_sock(sk); > - if (ask->refcnt) > + if (atomic_read(&ask->refcnt) != atomic_read(&ask->nokey_refcnt)) > goto unlock; > > type = ask->type; > @@ -308,12 +302,14 @@ int af_alg_accept(struct sock *sk, struct socket *newsock, bool kern) > > sk2->sk_family = PF_ALG; > > - if (nokey || !ask->refcnt++) > + if (atomic_inc_return_relaxed(&ask->refcnt) == 1) > sock_hold(sk); > - ask->nokey_refcnt += nokey; > + if (nokey) { > + atomic_inc(&ask->nokey_refcnt); > + atomic_set(&alg_sk(sk2)->nokey_refcnt, 1); > + } > 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/crypto/algif_aead.c b/crypto/algif_aead.c > index eb100a04ce9f..e643d268c7ba 100644 > --- a/crypto/algif_aead.c > +++ b/crypto/algif_aead.c > @@ -388,7 +388,7 @@ static int aead_check_key(struct socket *sock) > struct alg_sock *ask = alg_sk(sk); > > lock_sock(sk); > - if (ask->refcnt) > + if (!atomic_read(&ask->nokey_refcnt)) > goto unlock_child; > > psk = ask->parent; > @@ -400,11 +400,8 @@ static int aead_check_key(struct socket *sock) > if (crypto_aead_get_flags(tfm->aead) & CRYPTO_TFM_NEED_KEY) > goto unlock; > > - if (!pask->refcnt++) > - sock_hold(psk); > - > - ask->refcnt = 1; > - sock_put(psk); > + atomic_dec(&pask->nokey_refcnt); > + atomic_set(&ask->nokey_refcnt, 0); > > err = 0; > > diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c > index d0cde541beb6..73abe6a33f3a 100644 > --- a/crypto/algif_hash.c > +++ b/crypto/algif_hash.c > @@ -306,7 +306,7 @@ static int hash_check_key(struct socket *sock) > struct alg_sock *ask = alg_sk(sk); > > lock_sock(sk); > - if (ask->refcnt) > + if (!atomic_read(&ask->nokey_refcnt)) > goto unlock_child; > > psk = ask->parent; > @@ -318,11 +318,8 @@ static int hash_check_key(struct socket *sock) > if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) > goto unlock; > > - if (!pask->refcnt++) > - sock_hold(psk); > - > - ask->refcnt = 1; > - sock_put(psk); > + atomic_dec(&pask->nokey_refcnt); > + atomic_set(&ask->nokey_refcnt, 0); > > err = 0; > > diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c > index cfdaab2b7d76..75444b8beed9 100644 > --- a/crypto/algif_skcipher.c > +++ b/crypto/algif_skcipher.c > @@ -219,7 +219,7 @@ static int skcipher_check_key(struct socket *sock) > struct alg_sock *ask = alg_sk(sk); > > lock_sock(sk); > - if (ask->refcnt) > + if (!atomic_read(&ask->nokey_refcnt)) > goto unlock_child; > > psk = ask->parent; > @@ -231,11 +231,8 @@ static int skcipher_check_key(struct socket *sock) > if (crypto_skcipher_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) > goto unlock; > > - if (!pask->refcnt++) > - sock_hold(psk); > - > - ask->refcnt = 1; > - sock_put(psk); > + atomic_dec(&pask->nokey_refcnt); > + atomic_set(&ask->nokey_refcnt, 0); > > err = 0; > > diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h > index 482461d8931d..11f107df78dc 100644 > --- a/include/crypto/if_alg.h > +++ b/include/crypto/if_alg.h > @@ -34,8 +34,8 @@ struct alg_sock { > > struct sock *parent; > > - unsigned int refcnt; > - unsigned int nokey_refcnt; > + atomic_t refcnt; > + atomic_t nokey_refcnt; > > const struct af_alg_type *type; > void *private; > -- > 2.25.1 > > > -- > kernel-team mailing list > kernel-team@lists.ubuntu.com > https://lists.ubuntu.com/mailman/listinfo/kernel-team
diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 4fc8e6a7abb2..272879d7b0d1 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -133,21 +133,15 @@ EXPORT_SYMBOL_GPL(af_alg_release); void af_alg_release_parent(struct sock *sk) { struct alg_sock *ask = alg_sk(sk); - unsigned int nokey = ask->nokey_refcnt; - bool last = nokey && !ask->refcnt; + unsigned int nokey = atomic_read(&ask->nokey_refcnt); sk = ask->parent; ask = alg_sk(sk); - local_bh_disable(); - bh_lock_sock(sk); - ask->nokey_refcnt -= nokey; - if (!last) - last = !--ask->refcnt; - bh_unlock_sock(sk); - local_bh_enable(); + if (nokey) + atomic_dec(&ask->nokey_refcnt); - if (last) + if (atomic_dec_and_test(&ask->refcnt)) sock_put(sk); } EXPORT_SYMBOL_GPL(af_alg_release_parent); @@ -192,7 +186,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) err = -EBUSY; lock_sock(sk); - if (ask->refcnt | ask->nokey_refcnt) + if (atomic_read(&ask->refcnt)) goto unlock; swap(ask->type, type); @@ -241,7 +235,7 @@ static int alg_setsockopt(struct socket *sock, int level, int optname, int err = -EBUSY; lock_sock(sk); - if (ask->refcnt) + if (atomic_read(&ask->refcnt) != atomic_read(&ask->nokey_refcnt)) goto unlock; type = ask->type; @@ -308,12 +302,14 @@ int af_alg_accept(struct sock *sk, struct socket *newsock, bool kern) sk2->sk_family = PF_ALG; - if (nokey || !ask->refcnt++) + if (atomic_inc_return_relaxed(&ask->refcnt) == 1) sock_hold(sk); - ask->nokey_refcnt += nokey; + if (nokey) { + atomic_inc(&ask->nokey_refcnt); + atomic_set(&alg_sk(sk2)->nokey_refcnt, 1); + } 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/crypto/algif_aead.c b/crypto/algif_aead.c index eb100a04ce9f..e643d268c7ba 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -388,7 +388,7 @@ static int aead_check_key(struct socket *sock) struct alg_sock *ask = alg_sk(sk); lock_sock(sk); - if (ask->refcnt) + if (!atomic_read(&ask->nokey_refcnt)) goto unlock_child; psk = ask->parent; @@ -400,11 +400,8 @@ static int aead_check_key(struct socket *sock) if (crypto_aead_get_flags(tfm->aead) & CRYPTO_TFM_NEED_KEY) goto unlock; - if (!pask->refcnt++) - sock_hold(psk); - - ask->refcnt = 1; - sock_put(psk); + atomic_dec(&pask->nokey_refcnt); + atomic_set(&ask->nokey_refcnt, 0); err = 0; diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c index d0cde541beb6..73abe6a33f3a 100644 --- a/crypto/algif_hash.c +++ b/crypto/algif_hash.c @@ -306,7 +306,7 @@ static int hash_check_key(struct socket *sock) struct alg_sock *ask = alg_sk(sk); lock_sock(sk); - if (ask->refcnt) + if (!atomic_read(&ask->nokey_refcnt)) goto unlock_child; psk = ask->parent; @@ -318,11 +318,8 @@ static int hash_check_key(struct socket *sock) if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) goto unlock; - if (!pask->refcnt++) - sock_hold(psk); - - ask->refcnt = 1; - sock_put(psk); + atomic_dec(&pask->nokey_refcnt); + atomic_set(&ask->nokey_refcnt, 0); err = 0; diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index cfdaab2b7d76..75444b8beed9 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -219,7 +219,7 @@ static int skcipher_check_key(struct socket *sock) struct alg_sock *ask = alg_sk(sk); lock_sock(sk); - if (ask->refcnt) + if (!atomic_read(&ask->nokey_refcnt)) goto unlock_child; psk = ask->parent; @@ -231,11 +231,8 @@ static int skcipher_check_key(struct socket *sock) if (crypto_skcipher_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) goto unlock; - if (!pask->refcnt++) - sock_hold(psk); - - ask->refcnt = 1; - sock_put(psk); + atomic_dec(&pask->nokey_refcnt); + atomic_set(&ask->nokey_refcnt, 0); err = 0; diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h index 482461d8931d..11f107df78dc 100644 --- a/include/crypto/if_alg.h +++ b/include/crypto/if_alg.h @@ -34,8 +34,8 @@ struct alg_sock { struct sock *parent; - unsigned int refcnt; - unsigned int nokey_refcnt; + atomic_t refcnt; + atomic_t nokey_refcnt; const struct af_alg_type *type; void *private;