qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] file-posix: Skip effectiveless OFD lock operations
@ 2018-07-18  8:43 Fam Zheng
  2018-08-10 12:14 ` Kevin Wolf
  0 siblings, 1 reply; 4+ messages in thread
From: Fam Zheng @ 2018-07-18  8:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Max Reitz, qemu-block

If we know we've already locked the bytes, don't do it again; similarly
don't unlock a byte if we haven't locked it. This doesn't change the
behavior, but fixes a corner case explained below.

Libvirt had an error handling bug that an image can get its (ownership,
file mode, SELinux) permissions changed (RHBZ 1584982) by mistake behind
QEMU. Specifically, an image in use by Libvirt VM has:

    $ ls -lhZ b.img
    -rw-r--r--. qemu qemu system_u:object_r:svirt_image_t:s0:c600,c690 b.img

Trying to attach it a second time won't work because of image locking.
And after the error, it becomes:

    $ ls -lhZ b.img
    -rw-r--r--. root root system_u:object_r:virt_image_t:s0 b.img

Then, we won't be able to do OFD lock operations with the existing fd.
In other words, the code such as in blk_detach_dev:

    blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort);

can abort() QEMU, out of environmental changes.

This patch is an easy fix to this and the change is regardlessly
reasonable, so do it.

Signed-off-by: Fam Zheng <famz@redhat.com>
---
 block/file-posix.c | 27 +++++++++++++++++----------
 1 file changed, 17 insertions(+), 10 deletions(-)

diff --git a/block/file-posix.c b/block/file-posix.c
index 60af4b3d51..45d44c9947 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -680,23 +680,28 @@ typedef enum {
  * file; if @unlock == true, also unlock the unneeded bytes.
  * @shared_perm_lock_bits is the mask of all permissions that are NOT shared.
  */
-static int raw_apply_lock_bytes(int fd,
+static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
                                 uint64_t perm_lock_bits,
                                 uint64_t shared_perm_lock_bits,
                                 bool unlock, Error **errp)
 {
     int ret;
     int i;
+    uint64_t locked_perm, locked_shared_perm;
+
+    locked_perm = s ? s->perm : 0;
+    locked_shared_perm = s ? ~s->shared_perm & BLK_PERM_ALL : 0;
 
     PERM_FOREACH(i) {
         int off = RAW_LOCK_PERM_BASE + i;
-        if (perm_lock_bits & (1ULL << i)) {
+        uint64_t bit = (1ULL << i);
+        if ((perm_lock_bits & bit) && !(locked_perm & bit)) {
             ret = qemu_lock_fd(fd, off, 1, false);
             if (ret) {
                 error_setg(errp, "Failed to lock byte %d", off);
                 return ret;
             }
-        } else if (unlock) {
+        } else if (unlock && (locked_perm & bit) && !(perm_lock_bits & bit)) {
             ret = qemu_unlock_fd(fd, off, 1);
             if (ret) {
                 error_setg(errp, "Failed to unlock byte %d", off);
@@ -706,13 +711,15 @@ static int raw_apply_lock_bytes(int fd,
     }
     PERM_FOREACH(i) {
         int off = RAW_LOCK_SHARED_BASE + i;
-        if (shared_perm_lock_bits & (1ULL << i)) {
+        uint64_t bit = (1ULL << i);
+        if ((shared_perm_lock_bits & bit) && !(locked_shared_perm & bit)) {
             ret = qemu_lock_fd(fd, off, 1, false);
             if (ret) {
                 error_setg(errp, "Failed to lock byte %d", off);
                 return ret;
             }
-        } else if (unlock) {
+        } else if (unlock && (locked_shared_perm & bit) &&
+                   !(shared_perm_lock_bits & bit)) {
             ret = qemu_unlock_fd(fd, off, 1);
             if (ret) {
                 error_setg(errp, "Failed to unlock byte %d", off);
@@ -788,7 +795,7 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
 
     switch (op) {
     case RAW_PL_PREPARE:
-        ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm,
+        ret = raw_apply_lock_bytes(s, s->lock_fd, s->perm | new_perm,
                                    ~s->shared_perm | ~new_shared,
                                    false, errp);
         if (!ret) {
@@ -800,7 +807,7 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
         op = RAW_PL_ABORT;
         /* fall through to unlock bytes. */
     case RAW_PL_ABORT:
-        raw_apply_lock_bytes(s->lock_fd, s->perm, ~s->shared_perm,
+        raw_apply_lock_bytes(s, s->lock_fd, s->perm, ~s->shared_perm,
                              true, &local_err);
         if (local_err) {
             /* Theoretically the above call only unlocks bytes and it cannot
@@ -810,7 +817,7 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
         }
         break;
     case RAW_PL_COMMIT:
-        raw_apply_lock_bytes(s->lock_fd, new_perm, ~new_shared,
+        raw_apply_lock_bytes(s, s->lock_fd, new_perm, ~new_shared,
                              true, &local_err);
         if (local_err) {
             /* Theoretically the above call only unlocks bytes and it cannot
@@ -2174,7 +2181,7 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
     shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
 
     /* Step one: Take locks */
-    result = raw_apply_lock_bytes(fd, perm, ~shared, false, errp);
+    result = raw_apply_lock_bytes(NULL, fd, perm, ~shared, false, errp);
     if (result < 0) {
         goto out_close;
     }
@@ -2215,7 +2222,7 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
     }
 
 out_unlock:
-    raw_apply_lock_bytes(fd, 0, 0, true, &local_err);
+    raw_apply_lock_bytes(NULL, fd, 0, 0, true, &local_err);
     if (local_err) {
         /* The above call should not fail, and if it does, that does
          * not mean the whole creation operation has failed.  So
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH] file-posix: Skip effectiveless OFD lock operations
  2018-07-18  8:43 [Qemu-devel] [PATCH] file-posix: Skip effectiveless OFD lock operations Fam Zheng
@ 2018-08-10 12:14 ` Kevin Wolf
  2018-08-13  1:45   ` Fam Zheng
  0 siblings, 1 reply; 4+ messages in thread
From: Kevin Wolf @ 2018-08-10 12:14 UTC (permalink / raw)
  To: Fam Zheng; +Cc: qemu-devel, Max Reitz, qemu-block

Am 18.07.2018 um 10:43 hat Fam Zheng geschrieben:
> If we know we've already locked the bytes, don't do it again; similarly
> don't unlock a byte if we haven't locked it. This doesn't change the
> behavior, but fixes a corner case explained below.
> 
> Libvirt had an error handling bug that an image can get its (ownership,
> file mode, SELinux) permissions changed (RHBZ 1584982) by mistake behind
> QEMU. Specifically, an image in use by Libvirt VM has:
> 
>     $ ls -lhZ b.img
>     -rw-r--r--. qemu qemu system_u:object_r:svirt_image_t:s0:c600,c690 b.img
> 
> Trying to attach it a second time won't work because of image locking.
> And after the error, it becomes:
> 
>     $ ls -lhZ b.img
>     -rw-r--r--. root root system_u:object_r:virt_image_t:s0 b.img
> 
> Then, we won't be able to do OFD lock operations with the existing fd.
> In other words, the code such as in blk_detach_dev:
> 
>     blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort);
> 
> can abort() QEMU, out of environmental changes.
> 
> This patch is an easy fix to this and the change is regardlessly
> reasonable, so do it.
> 
> Signed-off-by: Fam Zheng <famz@redhat.com>
> ---
>  block/file-posix.c | 27 +++++++++++++++++----------
>  1 file changed, 17 insertions(+), 10 deletions(-)
> 
> diff --git a/block/file-posix.c b/block/file-posix.c
> index 60af4b3d51..45d44c9947 100644
> --- a/block/file-posix.c
> +++ b/block/file-posix.c
> @@ -680,23 +680,28 @@ typedef enum {
>   * file; if @unlock == true, also unlock the unneeded bytes.
>   * @shared_perm_lock_bits is the mask of all permissions that are NOT shared.
>   */
> -static int raw_apply_lock_bytes(int fd,
> +static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
>                                  uint64_t perm_lock_bits,
>                                  uint64_t shared_perm_lock_bits,
>                                  bool unlock, Error **errp)
>  {
>      int ret;
>      int i;
> +    uint64_t locked_perm, locked_shared_perm;
> +
> +    locked_perm = s ? s->perm : 0;
> +    locked_shared_perm = s ? ~s->shared_perm & BLK_PERM_ALL : 0;

For the s == NULL case, using 0 is okay for locking because we will
always consider the bit as previously unlocked, so we will lock it.

For unlocking, however, we'll also see it as previously unlocked, so we
will never actually unlock anything any more.

Am I missing something?

Kevin

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH] file-posix: Skip effectiveless OFD lock operations
  2018-08-10 12:14 ` Kevin Wolf
@ 2018-08-13  1:45   ` Fam Zheng
  2018-08-13  8:52     ` Kevin Wolf
  0 siblings, 1 reply; 4+ messages in thread
From: Fam Zheng @ 2018-08-13  1:45 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-devel, Max Reitz, qemu-block

On Fri, 08/10 14:14, Kevin Wolf wrote:
> Am 18.07.2018 um 10:43 hat Fam Zheng geschrieben:
> > If we know we've already locked the bytes, don't do it again; similarly
> > don't unlock a byte if we haven't locked it. This doesn't change the
> > behavior, but fixes a corner case explained below.
> > 
> > Libvirt had an error handling bug that an image can get its (ownership,
> > file mode, SELinux) permissions changed (RHBZ 1584982) by mistake behind
> > QEMU. Specifically, an image in use by Libvirt VM has:
> > 
> >     $ ls -lhZ b.img
> >     -rw-r--r--. qemu qemu system_u:object_r:svirt_image_t:s0:c600,c690 b.img
> > 
> > Trying to attach it a second time won't work because of image locking.
> > And after the error, it becomes:
> > 
> >     $ ls -lhZ b.img
> >     -rw-r--r--. root root system_u:object_r:virt_image_t:s0 b.img
> > 
> > Then, we won't be able to do OFD lock operations with the existing fd.
> > In other words, the code such as in blk_detach_dev:
> > 
> >     blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort);
> > 
> > can abort() QEMU, out of environmental changes.
> > 
> > This patch is an easy fix to this and the change is regardlessly
> > reasonable, so do it.
> > 
> > Signed-off-by: Fam Zheng <famz@redhat.com>
> > ---
> >  block/file-posix.c | 27 +++++++++++++++++----------
> >  1 file changed, 17 insertions(+), 10 deletions(-)
> > 
> > diff --git a/block/file-posix.c b/block/file-posix.c
> > index 60af4b3d51..45d44c9947 100644
> > --- a/block/file-posix.c
> > +++ b/block/file-posix.c
> > @@ -680,23 +680,28 @@ typedef enum {
> >   * file; if @unlock == true, also unlock the unneeded bytes.
> >   * @shared_perm_lock_bits is the mask of all permissions that are NOT shared.
> >   */
> > -static int raw_apply_lock_bytes(int fd,
> > +static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
> >                                  uint64_t perm_lock_bits,
> >                                  uint64_t shared_perm_lock_bits,
> >                                  bool unlock, Error **errp)
> >  {
> >      int ret;
> >      int i;
> > +    uint64_t locked_perm, locked_shared_perm;
> > +
> > +    locked_perm = s ? s->perm : 0;
> > +    locked_shared_perm = s ? ~s->shared_perm & BLK_PERM_ALL : 0;
> 
> For the s == NULL case, using 0 is okay for locking because we will
> always consider the bit as previously unlocked, so we will lock it.
> 
> For unlocking, however, we'll also see it as previously unlocked, so we
> will never actually unlock anything any more.
> 
> Am I missing something?

You are right. Though s == NULL only happens in raw_co_create and the fd will be
closed before the function returns, I agree for the correctness of this function
it's better to do a blanket unlock when unlocking. Will respin.

Fam

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH] file-posix: Skip effectiveless OFD lock operations
  2018-08-13  1:45   ` Fam Zheng
@ 2018-08-13  8:52     ` Kevin Wolf
  0 siblings, 0 replies; 4+ messages in thread
From: Kevin Wolf @ 2018-08-13  8:52 UTC (permalink / raw)
  To: Fam Zheng; +Cc: qemu-devel, Max Reitz, qemu-block

Am 13.08.2018 um 03:45 hat Fam Zheng geschrieben:
> On Fri, 08/10 14:14, Kevin Wolf wrote:
> > Am 18.07.2018 um 10:43 hat Fam Zheng geschrieben:
> > > If we know we've already locked the bytes, don't do it again; similarly
> > > don't unlock a byte if we haven't locked it. This doesn't change the
> > > behavior, but fixes a corner case explained below.
> > > 
> > > Libvirt had an error handling bug that an image can get its (ownership,
> > > file mode, SELinux) permissions changed (RHBZ 1584982) by mistake behind
> > > QEMU. Specifically, an image in use by Libvirt VM has:
> > > 
> > >     $ ls -lhZ b.img
> > >     -rw-r--r--. qemu qemu system_u:object_r:svirt_image_t:s0:c600,c690 b.img
> > > 
> > > Trying to attach it a second time won't work because of image locking.
> > > And after the error, it becomes:
> > > 
> > >     $ ls -lhZ b.img
> > >     -rw-r--r--. root root system_u:object_r:virt_image_t:s0 b.img
> > > 
> > > Then, we won't be able to do OFD lock operations with the existing fd.
> > > In other words, the code such as in blk_detach_dev:
> > > 
> > >     blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort);
> > > 
> > > can abort() QEMU, out of environmental changes.
> > > 
> > > This patch is an easy fix to this and the change is regardlessly
> > > reasonable, so do it.
> > > 
> > > Signed-off-by: Fam Zheng <famz@redhat.com>
> > > ---
> > >  block/file-posix.c | 27 +++++++++++++++++----------
> > >  1 file changed, 17 insertions(+), 10 deletions(-)
> > > 
> > > diff --git a/block/file-posix.c b/block/file-posix.c
> > > index 60af4b3d51..45d44c9947 100644
> > > --- a/block/file-posix.c
> > > +++ b/block/file-posix.c
> > > @@ -680,23 +680,28 @@ typedef enum {
> > >   * file; if @unlock == true, also unlock the unneeded bytes.
> > >   * @shared_perm_lock_bits is the mask of all permissions that are NOT shared.
> > >   */
> > > -static int raw_apply_lock_bytes(int fd,
> > > +static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
> > >                                  uint64_t perm_lock_bits,
> > >                                  uint64_t shared_perm_lock_bits,
> > >                                  bool unlock, Error **errp)
> > >  {
> > >      int ret;
> > >      int i;
> > > +    uint64_t locked_perm, locked_shared_perm;
> > > +
> > > +    locked_perm = s ? s->perm : 0;
> > > +    locked_shared_perm = s ? ~s->shared_perm & BLK_PERM_ALL : 0;
> > 
> > For the s == NULL case, using 0 is okay for locking because we will
> > always consider the bit as previously unlocked, so we will lock it.
> > 
> > For unlocking, however, we'll also see it as previously unlocked, so we
> > will never actually unlock anything any more.
> > 
> > Am I missing something?
> 
> You are right. Though s == NULL only happens in raw_co_create and the fd will be
> closed before the function returns, I agree for the correctness of this function
> it's better to do a blanket unlock when unlocking. Will respin.

At least one reason why you can't rely on file descriptors being closed
and releasing the locks is fdsets, where there are still more file
descriptors for the same OFD.

Kevin

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2018-08-13  8:52 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-07-18  8:43 [Qemu-devel] [PATCH] file-posix: Skip effectiveless OFD lock operations Fam Zheng
2018-08-10 12:14 ` Kevin Wolf
2018-08-13  1:45   ` Fam Zheng
2018-08-13  8:52     ` Kevin Wolf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).