2021-02-09 23:13:03

by Anthony Krowiak

[permalink] [raw]
Subject: [PATCH 1/1] s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks

This patch fixes a circular locking dependency in the CI introduced by
commit f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM
pointer invalidated"). The lockdep only occurs when starting a Secure
Execution guest. Crypto virtualization (vfio_ap) is not yet supported for
SE guests; however, in order to avoid CI errors, this fix is being
provided.

The circular lockdep was introduced when the masks in the guest's APCB
were taken under the matrix_dev->lock. While the lock is definitely
needed to protect the setting/unsetting of the KVM pointer, it is not
necessarily critical for setting the masks, so this will not be done under
protection of the matrix_dev->lock.

Fixes: f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM pointer invalidated")
Cc: [email protected]
Signed-off-by: Tony Krowiak <[email protected]>
---
drivers/s390/crypto/vfio_ap_ops.c | 75 ++++++++++++++++++-------------
1 file changed, 45 insertions(+), 30 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 41fc2e4135fe..f4e19aa2acb9 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -322,6 +322,20 @@ static void vfio_ap_matrix_init(struct ap_config_info *info,
matrix->adm_max = info->apxa ? info->Nd : 15;
}

+static bool vfio_ap_mdev_has_crycb(struct ap_matrix_mdev *matrix_mdev)
+{
+ return (matrix_mdev->kvm && matrix_mdev->kvm->arch.crypto.crycbd);
+}
+
+static void vfio_ap_mdev_commit_apcb(struct ap_matrix_mdev *matrix_mdev)
+{
+ if (vfio_ap_mdev_has_crycb(matrix_mdev))
+ kvm_arch_crypto_set_masks(matrix_mdev->kvm,
+ matrix_mdev->matrix.apm,
+ matrix_mdev->matrix.aqm,
+ matrix_mdev->matrix.adm);
+}
+
static int vfio_ap_mdev_create(struct kobject *kobj, struct mdev_device *mdev)
{
struct ap_matrix_mdev *matrix_mdev;
@@ -1028,7 +1042,9 @@ static const struct attribute_group *vfio_ap_mdev_attr_groups[] = {
* @kvm: reference to KVM instance
*
* Verifies no other mediated matrix device has @kvm and sets a reference to
- * it in @matrix_mdev->kvm.
+ * it in @matrix_mdev->kvm. The matrix_dev->lock must not be taken prior to
+ * calling this function; doing so may result in a circular lock dependency
+ * when the kvm->lock is taken to set masks in the guest's APCB.
*
* Return 0 if no other mediated matrix device has a reference to @kvm;
* otherwise, returns an -EPERM.
@@ -1038,6 +1054,8 @@ static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,
{
struct ap_matrix_mdev *m;

+ mutex_lock(&matrix_dev->lock);
+
list_for_each_entry(m, &matrix_dev->mdev_list, node) {
if ((m != matrix_mdev) && (m->kvm == kvm))
return -EPERM;
@@ -1046,6 +1064,8 @@ static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,
matrix_mdev->kvm = kvm;
kvm_get_kvm(kvm);
kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
+ mutex_unlock(&matrix_dev->lock);
+ vfio_ap_mdev_commit_apcb(matrix_mdev);

return 0;
}
@@ -1079,13 +1099,27 @@ static int vfio_ap_mdev_iommu_notifier(struct notifier_block *nb,
return NOTIFY_DONE;
}

+/**
+ * vfio_ap_mdev_unset_kvm
+ *
+ * @matrix_mdev: a matrix mediated device
+ *
+ * Clears the masks in the guest's APCB as well as the reference to KVM from
+ * @matrix_mdev. The matrix_dev->lock must not be taken prior to calling this
+ * function; doing so may result in a circular lock dependency when the
+ * kvm->lock is taken to clear the masks in the guest's APCB.
+ */
static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
{
- kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
- matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
- vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
- kvm_put_kvm(matrix_mdev->kvm);
- matrix_mdev->kvm = NULL;
+ if (matrix_mdev->kvm) {
+ kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
+ mutex_lock(&matrix_dev->lock);
+ matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
+ vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
+ kvm_put_kvm(matrix_mdev->kvm);
+ matrix_mdev->kvm = NULL;
+ mutex_unlock(&matrix_dev->lock);
+ }
}

static int vfio_ap_mdev_group_notifier(struct notifier_block *nb,
@@ -1098,32 +1132,15 @@ static int vfio_ap_mdev_group_notifier(struct notifier_block *nb,
return NOTIFY_OK;

matrix_mdev = container_of(nb, struct ap_matrix_mdev, group_notifier);
- mutex_lock(&matrix_dev->lock);
-
- if (!data) {
- if (matrix_mdev->kvm)
- vfio_ap_mdev_unset_kvm(matrix_mdev);
- goto notify_done;
- }

- ret = vfio_ap_mdev_set_kvm(matrix_mdev, data);
- if (ret) {
- notify_rc = NOTIFY_DONE;
- goto notify_done;
- }
+ if (!data)
+ vfio_ap_mdev_unset_kvm(matrix_mdev);
+ else
+ ret = vfio_ap_mdev_set_kvm(matrix_mdev, data);

- /* If there is no CRYCB pointer, then we can't copy the masks */
- if (!matrix_mdev->kvm->arch.crypto.crycbd) {
+ if (ret)
notify_rc = NOTIFY_DONE;
- goto notify_done;
- }
-
- kvm_arch_crypto_set_masks(matrix_mdev->kvm, matrix_mdev->matrix.apm,
- matrix_mdev->matrix.aqm,
- matrix_mdev->matrix.adm);

-notify_done:
- mutex_unlock(&matrix_dev->lock);
return notify_rc;
}

@@ -1257,10 +1274,8 @@ static void vfio_ap_mdev_release(struct mdev_device *mdev)
{
struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);

- mutex_lock(&matrix_dev->lock);
if (matrix_mdev->kvm)
vfio_ap_mdev_unset_kvm(matrix_mdev);
- mutex_unlock(&matrix_dev->lock);

vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
&matrix_mdev->iommu_notifier);
--
2.21.1


2021-02-10 11:00:49

by Cornelia Huck

[permalink] [raw]
Subject: Re: [PATCH 1/1] s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks

On Tue, 9 Feb 2021 14:48:30 -0500
Tony Krowiak <[email protected]> wrote:

> This patch fixes a circular locking dependency in the CI introduced by
> commit f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM
> pointer invalidated"). The lockdep only occurs when starting a Secure
> Execution guest. Crypto virtualization (vfio_ap) is not yet supported for
> SE guests; however, in order to avoid CI errors, this fix is being
> provided.
>
> The circular lockdep was introduced when the masks in the guest's APCB
> were taken under the matrix_dev->lock. While the lock is definitely
> needed to protect the setting/unsetting of the KVM pointer, it is not
> necessarily critical for setting the masks, so this will not be done under
> protection of the matrix_dev->lock.
>
> Fixes: f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM pointer invalidated")
> Cc: [email protected]
> Signed-off-by: Tony Krowiak <[email protected]>
> ---
> drivers/s390/crypto/vfio_ap_ops.c | 75 ++++++++++++++++++-------------
> 1 file changed, 45 insertions(+), 30 deletions(-)
>

> static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
> {
> - kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
> - matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
> - vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
> - kvm_put_kvm(matrix_mdev->kvm);
> - matrix_mdev->kvm = NULL;
> + if (matrix_mdev->kvm) {

If you're doing setting/unsetting under matrix_dev->lock, is it
possible that matrix_mdev->kvm gets unset between here and the next
line, as you don't hold the lock?

Maybe you could
- grab a reference to kvm while holding the lock
- call the mask handling functions with that kvm reference
- lock again, drop the reference, and do the rest of the processing?

> + kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
> + mutex_lock(&matrix_dev->lock);
> + matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
> + vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
> + kvm_put_kvm(matrix_mdev->kvm);
> + matrix_mdev->kvm = NULL;
> + mutex_unlock(&matrix_dev->lock);
> + }
> }

2021-02-10 15:27:13

by Halil Pasic

[permalink] [raw]
Subject: Re: [PATCH 1/1] s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks

On Wed, 10 Feb 2021 11:53:34 +0100
Cornelia Huck <[email protected]> wrote:

> On Tue, 9 Feb 2021 14:48:30 -0500
> Tony Krowiak <[email protected]> wrote:
>
> > This patch fixes a circular locking dependency in the CI introduced by
> > commit f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM
> > pointer invalidated"). The lockdep only occurs when starting a Secure
> > Execution guest. Crypto virtualization (vfio_ap) is not yet supported for
> > SE guests; however, in order to avoid CI errors, this fix is being
> > provided.
> >
> > The circular lockdep was introduced when the masks in the guest's APCB
> > were taken under the matrix_dev->lock. While the lock is definitely
> > needed to protect the setting/unsetting of the KVM pointer, it is not
> > necessarily critical for setting the masks, so this will not be done under
> > protection of the matrix_dev->lock.
> >
> > Fixes: f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM pointer invalidated")
> > Cc: [email protected]
> > Signed-off-by: Tony Krowiak <[email protected]>
> > ---
> > drivers/s390/crypto/vfio_ap_ops.c | 75 ++++++++++++++++++-------------
> > 1 file changed, 45 insertions(+), 30 deletions(-)
> >
>
> > static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
> > {
> > - kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
> > - matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
> > - vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
> > - kvm_put_kvm(matrix_mdev->kvm);
> > - matrix_mdev->kvm = NULL;
> > + if (matrix_mdev->kvm) {
>
> If you're doing setting/unsetting under matrix_dev->lock, is it
> possible that matrix_mdev->kvm gets unset between here and the next
> line, as you don't hold the lock?
>
> Maybe you could
> - grab a reference to kvm while holding the lock
> - call the mask handling functions with that kvm reference
> - lock again, drop the reference, and do the rest of the processing?

I agree, matrix_mdev->kvm can go NULL any time and we are risking
a null pointer dereference here.

Another idea would be to do


static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
{
struct kvm *kvm;

mutex_lock(&matrix_dev->lock);
if (matrix_mdev->kvm) {
kvm = matrix_mdev->kvm;
matrix_mdev->kvm = NULL;
mutex_unlock(&matrix_dev->lock);
kvm_arch_crypto_clear_masks(kvm);
mutex_lock(&matrix_dev->lock);
matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
kvm_put_kvm(kvm);
}
mutex_unlock(&matrix_dev->lock);
}

That way only one unset would actually do the unset and cleanup
and every other invocation would bail out with only checking
matrix_mdev->kvm.


> > + kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
> > + mutex_lock(&matrix_dev->lock);
> > + matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
> > + vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
> > + kvm_put_kvm(matrix_mdev->kvm);
> > + matrix_mdev->kvm = NULL;
> > + mutex_unlock(&matrix_dev->lock);
> > + }
> > }
>

2021-02-10 15:36:08

by Halil Pasic

[permalink] [raw]
Subject: Re: [PATCH 1/1] s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks

On Wed, 10 Feb 2021 16:24:29 +0100
Halil Pasic <[email protected]> wrote:

> > Maybe you could
> > - grab a reference to kvm while holding the lock
> > - call the mask handling functions with that kvm reference
> > - lock again, drop the reference, and do the rest of the processing?
>
> I agree, matrix_mdev->kvm can go NULL any time and we are risking
> a null pointer dereference here.
>
> Another idea would be to do
>
>
> static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
> {
> struct kvm *kvm;
>
> mutex_lock(&matrix_dev->lock);
> if (matrix_mdev->kvm) {
> kvm = matrix_mdev->kvm;
> matrix_mdev->kvm = NULL;
> mutex_unlock(&matrix_dev->lock);
> kvm_arch_crypto_clear_masks(kvm);
> mutex_lock(&matrix_dev->lock);
> matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
s/matrix_mdev->kvm/kvm
> vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
> kvm_put_kvm(kvm);
> }
> mutex_unlock(&matrix_dev->lock);
> }
>
> That way only one unset would actually do the unset and cleanup
> and every other invocation would bail out with only checking
> matrix_mdev->kvm.

But the problem with that is that we enable the the assign/unassign
prematurely, which could interfere wit reset_queues(). Forget about
it.

2021-02-10 20:36:40

by Anthony Krowiak

[permalink] [raw]
Subject: Re: [PATCH 1/1] s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks



On 2/10/21 5:53 AM, Cornelia Huck wrote:
> On Tue, 9 Feb 2021 14:48:30 -0500
> Tony Krowiak <[email protected]> wrote:
>
>> This patch fixes a circular locking dependency in the CI introduced by
>> commit f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM
>> pointer invalidated"). The lockdep only occurs when starting a Secure
>> Execution guest. Crypto virtualization (vfio_ap) is not yet supported for
>> SE guests; however, in order to avoid CI errors, this fix is being
>> provided.
>>
>> The circular lockdep was introduced when the masks in the guest's APCB
>> were taken under the matrix_dev->lock. While the lock is definitely
>> needed to protect the setting/unsetting of the KVM pointer, it is not
>> necessarily critical for setting the masks, so this will not be done under
>> protection of the matrix_dev->lock.
>>
>> Fixes: f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM pointer invalidated")
>> Cc: [email protected]
>> Signed-off-by: Tony Krowiak <[email protected]>
>> ---
>> drivers/s390/crypto/vfio_ap_ops.c | 75 ++++++++++++++++++-------------
>> 1 file changed, 45 insertions(+), 30 deletions(-)
>>
>> static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
>> {
>> - kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
>> - matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
>> - vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
>> - kvm_put_kvm(matrix_mdev->kvm);
>> - matrix_mdev->kvm = NULL;
>> + if (matrix_mdev->kvm) {
> If you're doing setting/unsetting under matrix_dev->lock, is it
> possible that matrix_mdev->kvm gets unset between here and the next
> line, as you don't hold the lock?

That is highly unlikely because the only place the matrix_mdev->kvm
pointer is cleared is in this function which is called from only two
places: the notifier that handles the VFIO_GROUP_NOTIFY_SET_KVM
notification when the KVM pointer is cleared; the vfio_ap_mdev_release()
function which is called when the mdev fd is closed (i.e., when the guest
is shut down). The fact is, with the only end-to-end implementation
currently available, the notifier callback is never invoked to clear
the KVM pointer because the vfio_ap_mdev_release callback is
invoked first and it unregisters the notifier callback.

Having said that, I suppose there is no guarantee that there will not
be different userspace clients in the future that do things in a
different order. At the very least, it wouldn't hurt to protect against
that as you suggest below.

>
> Maybe you could
> - grab a reference to kvm while holding the lock
> - call the mask handling functions with that kvm reference
> - lock again, drop the reference, and do the rest of the processing?
>
>> + kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
>> + mutex_lock(&matrix_dev->lock);
>> + matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
>> + vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
>> + kvm_put_kvm(matrix_mdev->kvm);
>> + matrix_mdev->kvm = NULL;
>> + mutex_unlock(&matrix_dev->lock);
>> + }
>> }

2021-02-10 22:08:23

by Anthony Krowiak

[permalink] [raw]
Subject: Re: [PATCH 1/1] s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks



On 2/10/21 10:32 AM, Halil Pasic wrote:
> On Wed, 10 Feb 2021 16:24:29 +0100
> Halil Pasic <[email protected]> wrote:
>
>>> Maybe you could
>>> - grab a reference to kvm while holding the lock
>>> - call the mask handling functions with that kvm reference
>>> - lock again, drop the reference, and do the rest of the processing?
>> I agree, matrix_mdev->kvm can go NULL any time and we are risking
>> a null pointer dereference here.
>>
>> Another idea would be to do
>>
>>
>> static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
>> {
>> struct kvm *kvm;
>>
>> mutex_lock(&matrix_dev->lock);
>> if (matrix_mdev->kvm) {
>> kvm = matrix_mdev->kvm;
>> matrix_mdev->kvm = NULL;
>> mutex_unlock(&matrix_dev->lock);
>> kvm_arch_crypto_clear_masks(kvm);
>> mutex_lock(&matrix_dev->lock);
>> matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
> s/matrix_mdev->kvm/kvm
>> vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
>> kvm_put_kvm(kvm);
>> }
>> mutex_unlock(&matrix_dev->lock);
>> }
>>
>> That way only one unset would actually do the unset and cleanup
>> and every other invocation would bail out with only checking
>> matrix_mdev->kvm.
> But the problem with that is that we enable the the assign/unassign
> prematurely, which could interfere wit reset_queues(). Forget about
> it.

Not sure what you mean by this.


2021-02-10 22:09:49

by Anthony Krowiak

[permalink] [raw]
Subject: Re: [PATCH 1/1] s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks



On 2/10/21 10:24 AM, Halil Pasic wrote:
> On Wed, 10 Feb 2021 11:53:34 +0100
> Cornelia Huck <[email protected]> wrote:
>
>> On Tue, 9 Feb 2021 14:48:30 -0500
>> Tony Krowiak <[email protected]> wrote:
>>
>>> This patch fixes a circular locking dependency in the CI introduced by
>>> commit f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM
>>> pointer invalidated"). The lockdep only occurs when starting a Secure
>>> Execution guest. Crypto virtualization (vfio_ap) is not yet supported for
>>> SE guests; however, in order to avoid CI errors, this fix is being
>>> provided.
>>>
>>> The circular lockdep was introduced when the masks in the guest's APCB
>>> were taken under the matrix_dev->lock. While the lock is definitely
>>> needed to protect the setting/unsetting of the KVM pointer, it is not
>>> necessarily critical for setting the masks, so this will not be done under
>>> protection of the matrix_dev->lock.
>>>
>>> Fixes: f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM pointer invalidated")
>>> Cc: [email protected]
>>> Signed-off-by: Tony Krowiak <[email protected]>
>>> ---
>>> drivers/s390/crypto/vfio_ap_ops.c | 75 ++++++++++++++++++-------------
>>> 1 file changed, 45 insertions(+), 30 deletions(-)
>>>
>>> static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
>>> {
>>> - kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
>>> - matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
>>> - vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
>>> - kvm_put_kvm(matrix_mdev->kvm);
>>> - matrix_mdev->kvm = NULL;
>>> + if (matrix_mdev->kvm) {
>> If you're doing setting/unsetting under matrix_dev->lock, is it
>> possible that matrix_mdev->kvm gets unset between here and the next
>> line, as you don't hold the lock?
>>
>> Maybe you could
>> - grab a reference to kvm while holding the lock
>> - call the mask handling functions with that kvm reference
>> - lock again, drop the reference, and do the rest of the processing?
> I agree, matrix_mdev->kvm can go NULL any time and we are risking
> a null pointer dereference here.
>
> Another idea would be to do
>
>
> static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
> {
> struct kvm *kvm;
>
> mutex_lock(&matrix_dev->lock);
> if (matrix_mdev->kvm) {
> kvm = matrix_mdev->kvm;
> matrix_mdev->kvm = NULL;
> mutex_unlock(&matrix_dev->lock);
> kvm_arch_crypto_clear_masks(kvm);
> mutex_lock(&matrix_dev->lock);
> matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
> vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
> kvm_put_kvm(kvm);
> }
> mutex_unlock(&matrix_dev->lock);
> }
>
> That way only one unset would actually do the unset and cleanup
> and every other invocation would bail out with only checking
> matrix_mdev->kvm.

How ironic, that is exactly what I did after agreeing with Connie.

>
>
>>> + kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
>>> + mutex_lock(&matrix_dev->lock);
>>> + matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
>>> + vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
>>> + kvm_put_kvm(matrix_mdev->kvm);
>>> + matrix_mdev->kvm = NULL;
>>> + mutex_unlock(&matrix_dev->lock);
>>> + }
>>> }

2021-02-10 22:53:20

by Halil Pasic

[permalink] [raw]
Subject: Re: [PATCH 1/1] s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks

On Wed, 10 Feb 2021 17:05:48 -0500
Tony Krowiak <[email protected]> wrote:

> On 2/10/21 10:32 AM, Halil Pasic wrote:
> > On Wed, 10 Feb 2021 16:24:29 +0100
> > Halil Pasic <[email protected]> wrote:
> >
> >>> Maybe you could
> >>> - grab a reference to kvm while holding the lock
> >>> - call the mask handling functions with that kvm reference
> >>> - lock again, drop the reference, and do the rest of the processing?
> >> I agree, matrix_mdev->kvm can go NULL any time and we are risking
> >> a null pointer dereference here.
> >>
> >> Another idea would be to do
> >>
> >>
> >> static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
> >> {
> >> struct kvm *kvm;
> >>
> >> mutex_lock(&matrix_dev->lock);
> >> if (matrix_mdev->kvm) {
> >> kvm = matrix_mdev->kvm;
> >> matrix_mdev->kvm = NULL;
> >> mutex_unlock(&matrix_dev->lock);
> >> kvm_arch_crypto_clear_masks(kvm);
> >> mutex_lock(&matrix_dev->lock);
> >> matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
> > s/matrix_mdev->kvm/kvm
> >> vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
> >> kvm_put_kvm(kvm);
> >> }
> >> mutex_unlock(&matrix_dev->lock);
> >> }
> >>
> >> That way only one unset would actually do the unset and cleanup
> >> and every other invocation would bail out with only checking
> >> matrix_mdev->kvm.
> > But the problem with that is that we enable the the assign/unassign
> > prematurely, which could interfere wit reset_queues(). Forget about
> > it.
>
> Not sure what you mean by this.
>
>

I mean because above I first do
(1) matrix_mdev->kvm = NULL;
and then do
(2) vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
another thread could do
static ssize_t unassign_adapter_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
unsigned long apid;
struct mdev_device *mdev = mdev_from_dev(dev);
struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);

/* If the guest is running, disallow un-assignment of adapter */
if (matrix_mdev->kvm)
return -EBUSY;
...
}
between (1) and (2), and we would not bail out with -EBUSY because !!kvm
because of (1). That means we would change matrix_mdev->matrix and we
would not reset the queues that correspond to the apid that was just
removed, because by the time we do the reset_queues, the queues are
not in the matrix_mdev->matrix any more.

Does that make sense?

2021-02-11 12:44:17

by Cornelia Huck

[permalink] [raw]
Subject: Re: [PATCH 1/1] s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks

On Wed, 10 Feb 2021 15:34:24 -0500
Tony Krowiak <[email protected]> wrote:

> On 2/10/21 5:53 AM, Cornelia Huck wrote:
> > On Tue, 9 Feb 2021 14:48:30 -0500
> > Tony Krowiak <[email protected]> wrote:
> >
> >> This patch fixes a circular locking dependency in the CI introduced by
> >> commit f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM
> >> pointer invalidated"). The lockdep only occurs when starting a Secure
> >> Execution guest. Crypto virtualization (vfio_ap) is not yet supported for
> >> SE guests; however, in order to avoid CI errors, this fix is being
> >> provided.
> >>
> >> The circular lockdep was introduced when the masks in the guest's APCB
> >> were taken under the matrix_dev->lock. While the lock is definitely
> >> needed to protect the setting/unsetting of the KVM pointer, it is not
> >> necessarily critical for setting the masks, so this will not be done under
> >> protection of the matrix_dev->lock.
> >>
> >> Fixes: f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM pointer invalidated")
> >> Cc: [email protected]
> >> Signed-off-by: Tony Krowiak <[email protected]>
> >> ---
> >> drivers/s390/crypto/vfio_ap_ops.c | 75 ++++++++++++++++++-------------
> >> 1 file changed, 45 insertions(+), 30 deletions(-)
> >>
> >> static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
> >> {
> >> - kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
> >> - matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
> >> - vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
> >> - kvm_put_kvm(matrix_mdev->kvm);
> >> - matrix_mdev->kvm = NULL;
> >> + if (matrix_mdev->kvm) {
> > If you're doing setting/unsetting under matrix_dev->lock, is it
> > possible that matrix_mdev->kvm gets unset between here and the next
> > line, as you don't hold the lock?
>
> That is highly unlikely because the only place the matrix_mdev->kvm
> pointer is cleared is in this function which is called from only two
> places: the notifier that handles the VFIO_GROUP_NOTIFY_SET_KVM
> notification when the KVM pointer is cleared; the vfio_ap_mdev_release()
> function which is called when the mdev fd is closed (i.e., when the guest
> is shut down). The fact is, with the only end-to-end implementation
> currently available, the notifier callback is never invoked to clear
> the KVM pointer because the vfio_ap_mdev_release callback is
> invoked first and it unregisters the notifier callback.
>
> Having said that, I suppose there is no guarantee that there will not
> be different userspace clients in the future that do things in a
> different order. At the very least, it wouldn't hurt to protect against
> that as you suggest below.

Yes, if userspace is able to use the interfaces in the certain way, we
should always make sure that nothing bad happens if it does so, even if
known userspace applications are well-behaved.

[Can we make an 'evil userspace' test program, maybe? The hardware
dependency makes this hard to run, though.]

>
> >
> > Maybe you could
> > - grab a reference to kvm while holding the lock
> > - call the mask handling functions with that kvm reference
> > - lock again, drop the reference, and do the rest of the processing?
> >
> >> + kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
> >> + mutex_lock(&matrix_dev->lock);
> >> + matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
> >> + vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
> >> + kvm_put_kvm(matrix_mdev->kvm);
> >> + matrix_mdev->kvm = NULL;
> >> + mutex_unlock(&matrix_dev->lock);
> >> + }
> >> }
>

2021-02-11 14:33:38

by Anthony Krowiak

[permalink] [raw]
Subject: Re: [PATCH 1/1] s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks



On 2/10/21 5:46 PM, Halil Pasic wrote:
> On Wed, 10 Feb 2021 17:05:48 -0500
> Tony Krowiak <[email protected]> wrote:
>
>> On 2/10/21 10:32 AM, Halil Pasic wrote:
>>> On Wed, 10 Feb 2021 16:24:29 +0100
>>> Halil Pasic <[email protected]> wrote:
>>>
>>>>> Maybe you could
>>>>> - grab a reference to kvm while holding the lock
>>>>> - call the mask handling functions with that kvm reference
>>>>> - lock again, drop the reference, and do the rest of the processing?
>>>> I agree, matrix_mdev->kvm can go NULL any time and we are risking
>>>> a null pointer dereference here.
>>>>
>>>> Another idea would be to do
>>>>
>>>>
>>>> static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
>>>> {
>>>> struct kvm *kvm;
>>>>
>>>> mutex_lock(&matrix_dev->lock);
>>>> if (matrix_mdev->kvm) {
>>>> kvm = matrix_mdev->kvm;
>>>> matrix_mdev->kvm = NULL;
>>>> mutex_unlock(&matrix_dev->lock);
>>>> kvm_arch_crypto_clear_masks(kvm);
>>>> mutex_lock(&matrix_dev->lock);
>>>> matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
>>> s/matrix_mdev->kvm/kvm
>>>> vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
>>>> kvm_put_kvm(kvm);
>>>> }
>>>> mutex_unlock(&matrix_dev->lock);
>>>> }
>>>>
>>>> That way only one unset would actually do the unset and cleanup
>>>> and every other invocation would bail out with only checking
>>>> matrix_mdev->kvm.
>>> But the problem with that is that we enable the the assign/unassign
>>> prematurely, which could interfere wit reset_queues(). Forget about
>>> it.
>> Not sure what you mean by this.
>>
>>
> I mean because above I first do
> (1) matrix_mdev->kvm = NULL;
> and then do
> (2) vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
> another thread could do
> static ssize_t unassign_adapter_store(struct device *dev,
> struct device_attribute *attr,
> const char *buf, size_t count)
> {
> int ret;
> unsigned long apid;
> struct mdev_device *mdev = mdev_from_dev(dev);
> struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
>
> /* If the guest is running, disallow un-assignment of adapter */
> if (matrix_mdev->kvm)
> return -EBUSY;
> ...
> }
> between (1) and (2), and we would not bail out with -EBUSY because !!kvm
> because of (1). That means we would change matrix_mdev->matrix and we
> would not reset the queues that correspond to the apid that was just
> removed, because by the time we do the reset_queues, the queues are
> not in the matrix_mdev->matrix any more.
>
> Does that make sense?

Yes, it makes sense. I guess I didn't look closely at your
suggestion when I said it was exactly what I implemented
after agreeing with Connie. I had a slight difference in
my implementation:

static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
{
    struct kvm *kvm;

    mutex_lock(&matrix_dev->lock);

    if (matrix_mdev->kvm) {
        kvm = matrix_mdev->kvm;
        mutex_unlock(&matrix_dev->lock);
        kvm_arch_crypto_clear_masks(kvm);
        mutex_lock(&matrix_dev->lock);
        kvm->arch.crypto.pqap_hook = NULL;
        vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
        matrix_mdev->kvm = NULL;
        kvm_put_kvm(kvm);
    }

    mutex_unlock(&matrix_dev->lock);
}

In your scenario, the unassignment would fail with -EBUSY because
the matrix_mdev->kvm pointer would not have yet been
cleared. The other problem with your implementation is that
IRQ resources would not get cleared after the reset because
the matrix_mdev->kvm pointer would be NULL at that time.

2021-02-11 14:52:09

by Anthony Krowiak

[permalink] [raw]
Subject: Re: [PATCH 1/1] s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks



On 2/11/21 7:23 AM, Cornelia Huck wrote:
> On Wed, 10 Feb 2021 15:34:24 -0500
> Tony Krowiak <[email protected]> wrote:
>
>> On 2/10/21 5:53 AM, Cornelia Huck wrote:
>>> On Tue, 9 Feb 2021 14:48:30 -0500
>>> Tony Krowiak <[email protected]> wrote:
>>>
>>>> This patch fixes a circular locking dependency in the CI introduced by
>>>> commit f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM
>>>> pointer invalidated"). The lockdep only occurs when starting a Secure
>>>> Execution guest. Crypto virtualization (vfio_ap) is not yet supported for
>>>> SE guests; however, in order to avoid CI errors, this fix is being
>>>> provided.
>>>>
>>>> The circular lockdep was introduced when the masks in the guest's APCB
>>>> were taken under the matrix_dev->lock. While the lock is definitely
>>>> needed to protect the setting/unsetting of the KVM pointer, it is not
>>>> necessarily critical for setting the masks, so this will not be done under
>>>> protection of the matrix_dev->lock.
>>>>
>>>> Fixes: f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM pointer invalidated")
>>>> Cc: [email protected]
>>>> Signed-off-by: Tony Krowiak <[email protected]>
>>>> ---
>>>> drivers/s390/crypto/vfio_ap_ops.c | 75 ++++++++++++++++++-------------
>>>> 1 file changed, 45 insertions(+), 30 deletions(-)
>>>>
>>>> static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
>>>> {
>>>> - kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
>>>> - matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
>>>> - vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
>>>> - kvm_put_kvm(matrix_mdev->kvm);
>>>> - matrix_mdev->kvm = NULL;
>>>> + if (matrix_mdev->kvm) {
>>> If you're doing setting/unsetting under matrix_dev->lock, is it
>>> possible that matrix_mdev->kvm gets unset between here and the next
>>> line, as you don't hold the lock?
>> That is highly unlikely because the only place the matrix_mdev->kvm
>> pointer is cleared is in this function which is called from only two
>> places: the notifier that handles the VFIO_GROUP_NOTIFY_SET_KVM
>> notification when the KVM pointer is cleared; the vfio_ap_mdev_release()
>> function which is called when the mdev fd is closed (i.e., when the guest
>> is shut down). The fact is, with the only end-to-end implementation
>> currently available, the notifier callback is never invoked to clear
>> the KVM pointer because the vfio_ap_mdev_release callback is
>> invoked first and it unregisters the notifier callback.
>>
>> Having said that, I suppose there is no guarantee that there will not
>> be different userspace clients in the future that do things in a
>> different order. At the very least, it wouldn't hurt to protect against
>> that as you suggest below.
> Yes, if userspace is able to use the interfaces in the certain way, we
> should always make sure that nothing bad happens if it does so, even if
> known userspace applications are well-behaved.
>
> [Can we make an 'evil userspace' test program, maybe? The hardware
> dependency makes this hard to run, though.]

Of course it is possible to create such a test program, but off the
top of my head, I can't come up with an algorithm that would
result in the scenario you have laid out. I haven't dabbled in the QEMU
space in quite some time; so, there would also be a bit of a re-learning
curve. I'm not sure it would be worth the effort to take this on given
how unlikely it is this scenario can happen, but I will take it into
consideration as it is a good idea.

>
>>> Maybe you could
>>> - grab a reference to kvm while holding the lock
>>> - call the mask handling functions with that kvm reference
>>> - lock again, drop the reference, and do the rest of the processing?
>>>
>>>> + kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
>>>> + mutex_lock(&matrix_dev->lock);
>>>> + matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
>>>> + vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
>>>> + kvm_put_kvm(matrix_mdev->kvm);
>>>> + matrix_mdev->kvm = NULL;
>>>> + mutex_unlock(&matrix_dev->lock);
>>>> + }
>>>> }

2021-02-11 17:46:43

by Halil Pasic

[permalink] [raw]
Subject: Re: [PATCH 1/1] s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks

On Thu, 11 Feb 2021 09:21:26 -0500
Tony Krowiak <[email protected]> wrote:

> Yes, it makes sense. I guess I didn't look closely at your
> suggestion when I said it was exactly what I implemented
> after agreeing with Connie. I had a slight difference in
> my implementation:
>
> static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
> {
>     struct kvm *kvm;
>
>     mutex_lock(&matrix_dev->lock);
>
>     if (matrix_mdev->kvm) {
>         kvm = matrix_mdev->kvm;
>         mutex_unlock(&matrix_dev->lock);

The problem with this one is that as soon as we drop
the lock here, another thread can in theory execute
the critical section below, which drops our reference
to kvm via kvm_put_kvm(kvm). Thus when we enter
kvm_arch_crypto_clear_mask(), even if we are guaranteed
to have a non-null pointer, the pointee is not guaranteed
to be around. So like Connie suggested, you better take
another reference to kvm in the first critical section.

Regards,
Halil

>         kvm_arch_crypto_clear_masks(kvm);
>         mutex_lock(&matrix_dev->lock);
>         kvm->arch.crypto.pqap_hook = NULL;
>         vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
>         matrix_mdev->kvm = NULL;
>         kvm_put_kvm(kvm);
>     }
>
>     mutex_unlock(&matrix_dev->lock);
> }

2021-02-11 19:21:06

by Anthony Krowiak

[permalink] [raw]
Subject: Re: [PATCH 1/1] s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks



On 2/11/21 11:47 AM, Halil Pasic wrote:
> On Thu, 11 Feb 2021 09:21:26 -0500
> Tony Krowiak <[email protected]> wrote:
>
>> Yes, it makes sense. I guess I didn't look closely at your
>> suggestion when I said it was exactly what I implemented
>> after agreeing with Connie. I had a slight difference in
>> my implementation:
>>
>> static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
>> {
>>     struct kvm *kvm;
>>
>>     mutex_lock(&matrix_dev->lock);
>>
>>     if (matrix_mdev->kvm) {
>>         kvm = matrix_mdev->kvm;
>>         mutex_unlock(&matrix_dev->lock);
> The problem with this one is that as soon as we drop
> the lock here, another thread can in theory execute
> the critical section below, which drops our reference
> to kvm via kvm_put_kvm(kvm). Thus when we enter
> kvm_arch_crypto_clear_mask(), even if we are guaranteed
> to have a non-null pointer, the pointee is not guaranteed
> to be around. So like Connie suggested, you better take
> another reference to kvm in the first critical section.

Sure.

>
> Regards,
> Halil
>
>>         kvm_arch_crypto_clear_masks(kvm);
>>         mutex_lock(&matrix_dev->lock);
>>         kvm->arch.crypto.pqap_hook = NULL;
>>         vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
>>         matrix_mdev->kvm = NULL;
>>         kvm_put_kvm(kvm);
>>     }
>>
>>     mutex_unlock(&matrix_dev->lock);
>> }