qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/4] mirror: Do not dereference invalid pointers
@ 2019-10-14 15:39 Max Reitz
  2019-10-14 15:39 ` [PATCH v3 1/4] " Max Reitz
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Max Reitz @ 2019-10-14 15:39 UTC (permalink / raw)
  To: qemu-block
  Cc: Kevin Wolf, Vladimir Sementsov-Ogievskiy, John Snow, qemu-devel,
	Max Reitz

Hi,

v2’s cover letter should explain everything:

https://lists.nongnu.org/archive/html/qemu-block/2019-09/msg01079.html


v3:
- Patch 2: Use input visitor as proposed by Vladimir

git-backport-diff against v2:

Key:
[----] : patches are identical
[####] : number of functional differences between upstream/downstream patch
[down] : patch is downstream-only
The flags [FC] indicate (F)unctional and (C)ontextual differences, respectively

001/4:[----] [--] 'mirror: Do not dereference invalid pointers'
002/4:[0041] [FC] 'blkdebug: Allow taking/unsharing permissions'
003/4:[----] [--] 'iotests: Add @error to wait_until_completed'
004/4:[----] [--] 'iotests: Add test for failing mirror complete'


Max Reitz (4):
  mirror: Do not dereference invalid pointers
  blkdebug: Allow taking/unsharing permissions
  iotests: Add @error to wait_until_completed
  iotests: Add test for failing mirror complete

 qapi/block-core.json          | 14 +++++-
 block/blkdebug.c              | 91 ++++++++++++++++++++++++++++++++++-
 block/mirror.c                | 13 +++--
 tests/qemu-iotests/041        | 44 +++++++++++++++++
 tests/qemu-iotests/041.out    |  4 +-
 tests/qemu-iotests/iotests.py | 18 ++++---
 6 files changed, 170 insertions(+), 14 deletions(-)

-- 
2.21.0



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

* [PATCH v3 1/4] mirror: Do not dereference invalid pointers
  2019-10-14 15:39 [PATCH v3 0/4] mirror: Do not dereference invalid pointers Max Reitz
@ 2019-10-14 15:39 ` Max Reitz
  2019-10-14 15:39 ` [PATCH v3 2/4] blkdebug: Allow taking/unsharing permissions Max Reitz
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Max Reitz @ 2019-10-14 15:39 UTC (permalink / raw)
  To: qemu-block
  Cc: Kevin Wolf, Vladimir Sementsov-Ogievskiy, John Snow, qemu-devel,
	Max Reitz

mirror_exit_common() may be called twice (if it is called from
mirror_prepare() and fails, it will be called from mirror_abort()
again).

In such a case, many of the pointers in the MirrorBlockJob object will
already be freed.  This can be seen most reliably for s->target, which
is set to NULL (and then dereferenced by blk_bs()).

Cc: qemu-stable@nongnu.org
Fixes: 737efc1eda23b904fbe0e66b37715fb0e5c3e58b
Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 block/mirror.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/block/mirror.c b/block/mirror.c
index fe984efb90..706d80fced 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -620,11 +620,11 @@ static int mirror_exit_common(Job *job)
 {
     MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
     BlockJob *bjob = &s->common;
-    MirrorBDSOpaque *bs_opaque = s->mirror_top_bs->opaque;
+    MirrorBDSOpaque *bs_opaque;
     AioContext *replace_aio_context = NULL;
-    BlockDriverState *src = s->mirror_top_bs->backing->bs;
-    BlockDriverState *target_bs = blk_bs(s->target);
-    BlockDriverState *mirror_top_bs = s->mirror_top_bs;
+    BlockDriverState *src;
+    BlockDriverState *target_bs;
+    BlockDriverState *mirror_top_bs;
     Error *local_err = NULL;
     bool abort = job->ret < 0;
     int ret = 0;
@@ -634,6 +634,11 @@ static int mirror_exit_common(Job *job)
     }
     s->prepared = true;
 
+    mirror_top_bs = s->mirror_top_bs;
+    bs_opaque = mirror_top_bs->opaque;
+    src = mirror_top_bs->backing->bs;
+    target_bs = blk_bs(s->target);
+
     if (bdrv_chain_contains(src, target_bs)) {
         bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
     }
-- 
2.21.0



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

* [PATCH v3 2/4] blkdebug: Allow taking/unsharing permissions
  2019-10-14 15:39 [PATCH v3 0/4] mirror: Do not dereference invalid pointers Max Reitz
  2019-10-14 15:39 ` [PATCH v3 1/4] " Max Reitz
@ 2019-10-14 15:39 ` Max Reitz
  2019-10-16 11:13   ` Vladimir Sementsov-Ogievskiy
  2019-10-14 15:39 ` [PATCH v3 3/4] iotests: Add @error to wait_until_completed Max Reitz
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Max Reitz @ 2019-10-14 15:39 UTC (permalink / raw)
  To: qemu-block
  Cc: Kevin Wolf, Vladimir Sementsov-Ogievskiy, John Snow, qemu-devel,
	Max Reitz

Sometimes it is useful to be able to add a node to the block graph that
takes or unshare a certain set of permissions for debugging purposes.
This patch adds this capability to blkdebug.

(Note that you cannot make blkdebug release or share permissions that it
needs to take or cannot share, because this might result in assertion
failures in the block layer.  But if the blkdebug node has no parents,
it will not take any permissions and share everything by default, so you
can then freely choose what permissions to take and share.)

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 qapi/block-core.json | 14 ++++++-
 block/blkdebug.c     | 91 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 103 insertions(+), 2 deletions(-)

diff --git a/qapi/block-core.json b/qapi/block-core.json
index f66553aac7..054ce651de 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3453,6 +3453,16 @@
 #
 # @set-state:       array of state-change descriptions
 #
+# @take-child-perms: Permissions to take on @image in addition to what
+#                    is necessary anyway (which depends on how the
+#                    blkdebug node is used).  Defaults to none.
+#                    (since 4.2)
+#
+# @unshare-child-perms: Permissions not to share on @image in addition
+#                       to what cannot be shared anyway (which depends
+#                       on how the blkdebug node is used).  Defaults
+#                       to none.  (since 4.2)
+#
 # Since: 2.9
 ##
 { 'struct': 'BlockdevOptionsBlkdebug',
@@ -3462,7 +3472,9 @@
             '*opt-write-zero': 'int32', '*max-write-zero': 'int32',
             '*opt-discard': 'int32', '*max-discard': 'int32',
             '*inject-error': ['BlkdebugInjectErrorOptions'],
-            '*set-state': ['BlkdebugSetStateOptions'] } }
+            '*set-state': ['BlkdebugSetStateOptions'],
+            '*take-child-perms': ['BlockPermission'],
+            '*unshare-child-perms': ['BlockPermission'] } }
 
 ##
 # @BlockdevOptionsBlklogwrites:
diff --git a/block/blkdebug.c b/block/blkdebug.c
index 5ae96c52b0..6807c03065 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -28,10 +28,14 @@
 #include "qemu/cutils.h"
 #include "qemu/config-file.h"
 #include "block/block_int.h"
+#include "block/qdict.h"
 #include "qemu/module.h"
 #include "qemu/option.h"
+#include "qapi/qapi-visit-block-core.h"
 #include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qlist.h"
 #include "qapi/qmp/qstring.h"
+#include "qapi/qobject-input-visitor.h"
 #include "sysemu/qtest.h"
 
 typedef struct BDRVBlkdebugState {
@@ -44,6 +48,9 @@ typedef struct BDRVBlkdebugState {
     uint64_t opt_discard;
     uint64_t max_discard;
 
+    uint64_t take_child_perms;
+    uint64_t unshare_child_perms;
+
     /* For blkdebug_refresh_filename() */
     char *config_file;
 
@@ -344,6 +351,67 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
     qdict_put_str(options, "x-image", filename);
 }
 
+static int blkdebug_parse_perm_list(uint64_t *dest, QDict *options,
+                                    const char *prefix, Error **errp)
+{
+    int ret = 0;
+    QDict *subqdict = NULL;
+    QObject *crumpled_subqdict = NULL;
+    Visitor *v = NULL;
+    BlockPermissionList *perm_list = NULL, *element;
+    Error *local_err = NULL;
+
+    qdict_extract_subqdict(options, &subqdict, prefix);
+    if (!qdict_size(subqdict)) {
+        goto out;
+    }
+
+    crumpled_subqdict = qdict_crumple(subqdict, errp);
+    if (!crumpled_subqdict) {
+        ret = -EINVAL;
+        goto out;
+    }
+
+    v = qobject_input_visitor_new(crumpled_subqdict);
+    visit_type_BlockPermissionList(v, NULL, &perm_list, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        ret = -EINVAL;
+        goto out;
+    }
+
+    for (element = perm_list; element; element = element->next) {
+        *dest |= UINT64_C(1) << element->value;
+    }
+
+out:
+    qapi_free_BlockPermissionList(perm_list);
+    visit_free(v);
+    qobject_unref(subqdict);
+    qobject_unref(crumpled_subqdict);
+    return ret;
+}
+
+static int blkdebug_parse_perms(BDRVBlkdebugState *s, QDict *options,
+                                Error **errp)
+{
+    int ret;
+
+    ret = blkdebug_parse_perm_list(&s->take_child_perms, options,
+                                   "take-child-perms.", errp);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = blkdebug_parse_perm_list(&s->unshare_child_perms, options,
+                                   "unshare-child-perms.", errp);
+    if (ret < 0) {
+        return ret;
+    }
+
+    return 0;
+}
+
 static QemuOptsList runtime_opts = {
     .name = "blkdebug",
     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
@@ -419,6 +487,12 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
     /* Set initial state */
     s->state = 1;
 
+    /* Parse permissions modifiers before opening the image file */
+    ret = blkdebug_parse_perms(s, options, errp);
+    if (ret < 0) {
+        goto out;
+    }
+
     /* Open the image file */
     bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
                                bs, &child_file, false, &local_err);
@@ -916,6 +990,21 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
     return 0;
 }
 
+static void blkdebug_child_perm(BlockDriverState *bs, BdrvChild *c,
+                                const BdrvChildRole *role,
+                                BlockReopenQueue *reopen_queue,
+                                uint64_t perm, uint64_t shared,
+                                uint64_t *nperm, uint64_t *nshared)
+{
+    BDRVBlkdebugState *s = bs->opaque;
+
+    bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
+                              nperm, nshared);
+
+    *nperm |= s->take_child_perms;
+    *nshared &= ~s->unshare_child_perms;
+}
+
 static const char *const blkdebug_strong_runtime_opts[] = {
     "config",
     "inject-error.",
@@ -940,7 +1029,7 @@ static BlockDriver bdrv_blkdebug = {
     .bdrv_file_open         = blkdebug_open,
     .bdrv_close             = blkdebug_close,
     .bdrv_reopen_prepare    = blkdebug_reopen_prepare,
-    .bdrv_child_perm        = bdrv_filter_default_perms,
+    .bdrv_child_perm        = blkdebug_child_perm,
 
     .bdrv_getlength         = blkdebug_getlength,
     .bdrv_refresh_filename  = blkdebug_refresh_filename,
-- 
2.21.0



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

* [PATCH v3 3/4] iotests: Add @error to wait_until_completed
  2019-10-14 15:39 [PATCH v3 0/4] mirror: Do not dereference invalid pointers Max Reitz
  2019-10-14 15:39 ` [PATCH v3 1/4] " Max Reitz
  2019-10-14 15:39 ` [PATCH v3 2/4] blkdebug: Allow taking/unsharing permissions Max Reitz
@ 2019-10-14 15:39 ` Max Reitz
  2019-10-14 15:39 ` [PATCH v3 4/4] iotests: Add test for failing mirror complete Max Reitz
  2019-10-28 10:50 ` [PATCH v3 0/4] mirror: Do not dereference invalid pointers Max Reitz
  4 siblings, 0 replies; 9+ messages in thread
From: Max Reitz @ 2019-10-14 15:39 UTC (permalink / raw)
  To: qemu-block
  Cc: Kevin Wolf, Vladimir Sementsov-Ogievskiy, John Snow, qemu-devel,
	Max Reitz

Callers can use this new parameter to expect failure during the
completion process.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 tests/qemu-iotests/iotests.py | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 43759e4e27..7af77ef496 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -770,15 +770,20 @@ class QMPTestCase(unittest.TestCase):
         self.assert_no_active_block_jobs()
         return result
 
-    def wait_until_completed(self, drive='drive0', check_offset=True, wait=60.0):
+    def wait_until_completed(self, drive='drive0', check_offset=True, wait=60.0,
+                             error=None):
         '''Wait for a block job to finish, returning the event'''
         while True:
             for event in self.vm.get_qmp_events(wait=wait):
                 if event['event'] == 'BLOCK_JOB_COMPLETED':
                     self.assert_qmp(event, 'data/device', drive)
-                    self.assert_qmp_absent(event, 'data/error')
-                    if check_offset:
-                        self.assert_qmp(event, 'data/offset', event['data']['len'])
+                    if error is None:
+                        self.assert_qmp_absent(event, 'data/error')
+                        if check_offset:
+                            self.assert_qmp(event, 'data/offset',
+                                            event['data']['len'])
+                    else:
+                        self.assert_qmp(event, 'data/error', error)
                     self.assert_no_active_block_jobs()
                     return event
                 elif event['event'] == 'JOB_STATUS_CHANGE':
@@ -796,7 +801,8 @@ class QMPTestCase(unittest.TestCase):
         self.assert_qmp(event, 'data/type', 'mirror')
         self.assert_qmp(event, 'data/offset', event['data']['len'])
 
-    def complete_and_wait(self, drive='drive0', wait_ready=True):
+    def complete_and_wait(self, drive='drive0', wait_ready=True,
+                          completion_error=None):
         '''Complete a block job and wait for it to finish'''
         if wait_ready:
             self.wait_ready(drive=drive)
@@ -804,7 +810,7 @@ class QMPTestCase(unittest.TestCase):
         result = self.vm.qmp('block-job-complete', device=drive)
         self.assert_qmp(result, 'return', {})
 
-        event = self.wait_until_completed(drive=drive)
+        event = self.wait_until_completed(drive=drive, error=completion_error)
         self.assert_qmp(event, 'data/type', 'mirror')
 
     def pause_wait(self, job_id='job0'):
-- 
2.21.0



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

* [PATCH v3 4/4] iotests: Add test for failing mirror complete
  2019-10-14 15:39 [PATCH v3 0/4] mirror: Do not dereference invalid pointers Max Reitz
                   ` (2 preceding siblings ...)
  2019-10-14 15:39 ` [PATCH v3 3/4] iotests: Add @error to wait_until_completed Max Reitz
@ 2019-10-14 15:39 ` Max Reitz
  2019-10-28 10:50 ` [PATCH v3 0/4] mirror: Do not dereference invalid pointers Max Reitz
  4 siblings, 0 replies; 9+ messages in thread
From: Max Reitz @ 2019-10-14 15:39 UTC (permalink / raw)
  To: qemu-block
  Cc: Kevin Wolf, Vladimir Sementsov-Ogievskiy, John Snow, qemu-devel,
	Max Reitz

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Tested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: John Snow <jsnow@redhat.com>
---
 tests/qemu-iotests/041     | 44 ++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/041.out |  4 ++--
 2 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
index 8568426311..d7be30b62b 100755
--- a/tests/qemu-iotests/041
+++ b/tests/qemu-iotests/041
@@ -1121,6 +1121,50 @@ class TestOrphanedSource(iotests.QMPTestCase):
                              target='dest-ro')
         self.assert_qmp(result, 'error/class', 'GenericError')
 
+    def test_failing_permission_in_complete(self):
+        self.assert_no_active_block_jobs()
+
+        # Unshare consistent-read on the target
+        # (The mirror job does not care)
+        result = self.vm.qmp('blockdev-add',
+                             driver='blkdebug',
+                             node_name='dest-perm',
+                             image='dest',
+                             unshare_child_perms=['consistent-read'])
+        self.assert_qmp(result, 'return', {})
+
+        result = self.vm.qmp('blockdev-mirror', job_id='job', device='src',
+                             sync='full', target='dest',
+                             filter_node_name='mirror-filter')
+        self.assert_qmp(result, 'return', {})
+
+        # Require consistent-read on the source
+        # (We can only add this node once the job has started, or it
+        # will complain that it does not want to run on non-root nodes)
+        result = self.vm.qmp('blockdev-add',
+                             driver='blkdebug',
+                             node_name='src-perm',
+                             image='src',
+                             take_child_perms=['consistent-read'])
+        self.assert_qmp(result, 'return', {})
+
+        # While completing, mirror will attempt to replace src by
+        # dest, which must fail because src-perm requires
+        # consistent-read but dest-perm does not share it; thus
+        # aborting the job when it is supposed to complete
+        self.complete_and_wait('job',
+                               completion_error='Operation not permitted')
+
+        # Assert that all of our nodes are still there (except for the
+        # mirror filter, which should be gone despite the failure)
+        nodes = self.vm.qmp('query-named-block-nodes')['return']
+        nodes = [node['node-name'] for node in nodes]
+
+        for expect in ('src', 'src-perm', 'dest', 'dest-perm'):
+            self.assertTrue(expect in nodes, '%s disappeared' % expect)
+        self.assertFalse('mirror-filter' in nodes,
+                         'Mirror filter node did not disappear')
+
 if __name__ == '__main__':
     iotests.main(supported_fmts=['qcow2', 'qed'],
                  supported_protocols=['file'])
diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out
index 2c448b4239..f496be9197 100644
--- a/tests/qemu-iotests/041.out
+++ b/tests/qemu-iotests/041.out
@@ -1,5 +1,5 @@
-..........................................................................................
+...........................................................................................
 ----------------------------------------------------------------------
-Ran 90 tests
+Ran 91 tests
 
 OK
-- 
2.21.0



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

* Re: [PATCH v3 2/4] blkdebug: Allow taking/unsharing permissions
  2019-10-14 15:39 ` [PATCH v3 2/4] blkdebug: Allow taking/unsharing permissions Max Reitz
@ 2019-10-16 11:13   ` Vladimir Sementsov-Ogievskiy
  2019-10-28 10:46     ` Max Reitz
  0 siblings, 1 reply; 9+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-10-16 11:13 UTC (permalink / raw)
  To: Max Reitz, qemu-block; +Cc: Kevin Wolf, John Snow, qemu-devel

14.10.2019 18:39, Max Reitz wrote:
> Sometimes it is useful to be able to add a node to the block graph that
> takes or unshare a certain set of permissions for debugging purposes.
> This patch adds this capability to blkdebug.
> 
> (Note that you cannot make blkdebug release or share permissions that it
> needs to take or cannot share, because this might result in assertion
> failures in the block layer.  But if the blkdebug node has no parents,
> it will not take any permissions and share everything by default, so you
> can then freely choose what permissions to take and share.)
> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>   qapi/block-core.json | 14 ++++++-
>   block/blkdebug.c     | 91 +++++++++++++++++++++++++++++++++++++++++++-
>   2 files changed, 103 insertions(+), 2 deletions(-)
> 
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index f66553aac7..054ce651de 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -3453,6 +3453,16 @@
>   #
>   # @set-state:       array of state-change descriptions
>   #
> +# @take-child-perms: Permissions to take on @image in addition to what
> +#                    is necessary anyway (which depends on how the
> +#                    blkdebug node is used).  Defaults to none.
> +#                    (since 4.2)
> +#
> +# @unshare-child-perms: Permissions not to share on @image in addition
> +#                       to what cannot be shared anyway (which depends
> +#                       on how the blkdebug node is used).  Defaults
> +#                       to none.  (since 4.2)
> +#
>   # Since: 2.9
>   ##
>   { 'struct': 'BlockdevOptionsBlkdebug',
> @@ -3462,7 +3472,9 @@
>               '*opt-write-zero': 'int32', '*max-write-zero': 'int32',
>               '*opt-discard': 'int32', '*max-discard': 'int32',
>               '*inject-error': ['BlkdebugInjectErrorOptions'],
> -            '*set-state': ['BlkdebugSetStateOptions'] } }
> +            '*set-state': ['BlkdebugSetStateOptions'],
> +            '*take-child-perms': ['BlockPermission'],
> +            '*unshare-child-perms': ['BlockPermission'] } }
>   
>   ##
>   # @BlockdevOptionsBlklogwrites:
> diff --git a/block/blkdebug.c b/block/blkdebug.c
> index 5ae96c52b0..6807c03065 100644
> --- a/block/blkdebug.c
> +++ b/block/blkdebug.c
> @@ -28,10 +28,14 @@
>   #include "qemu/cutils.h"
>   #include "qemu/config-file.h"
>   #include "block/block_int.h"
> +#include "block/qdict.h"
>   #include "qemu/module.h"
>   #include "qemu/option.h"
> +#include "qapi/qapi-visit-block-core.h"
>   #include "qapi/qmp/qdict.h"
> +#include "qapi/qmp/qlist.h"
>   #include "qapi/qmp/qstring.h"
> +#include "qapi/qobject-input-visitor.h"
>   #include "sysemu/qtest.h"
>   
>   typedef struct BDRVBlkdebugState {
> @@ -44,6 +48,9 @@ typedef struct BDRVBlkdebugState {
>       uint64_t opt_discard;
>       uint64_t max_discard;
>   
> +    uint64_t take_child_perms;
> +    uint64_t unshare_child_perms;
> +
>       /* For blkdebug_refresh_filename() */
>       char *config_file;
>   
> @@ -344,6 +351,67 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
>       qdict_put_str(options, "x-image", filename);
>   }
>   
> +static int blkdebug_parse_perm_list(uint64_t *dest, QDict *options,
> +                                    const char *prefix, Error **errp)
> +{
> +    int ret = 0;
> +    QDict *subqdict = NULL;
> +    QObject *crumpled_subqdict = NULL;
> +    Visitor *v = NULL;
> +    BlockPermissionList *perm_list = NULL, *element;
> +    Error *local_err = NULL;
> +
> +    qdict_extract_subqdict(options, &subqdict, prefix);
> +    if (!qdict_size(subqdict)) {


Hmm, you consider it as a success, so you mean default. Then, it's safer to
set *dest = 0 here.

> +        goto out;
> +    }
> +
> +    crumpled_subqdict = qdict_crumple(subqdict, errp);
> +    if (!crumpled_subqdict) {
> +        ret = -EINVAL;
> +        goto out;
> +    }
> +
> +    v = qobject_input_visitor_new(crumpled_subqdict);
> +    visit_type_BlockPermissionList(v, NULL, &perm_list, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        ret = -EINVAL;
> +        goto out;
> +    }
> +

I'd prefer explicitly set *dest = 0 here too.

> +    for (element = perm_list; element; element = element->next) {
> +        *dest |= UINT64_C(1) << element->value;

Hmm, so, you rely on correct correspondence between generated BlockPermission enum
and unnamed enum with BLK_PERM_* constants...

I'm afraid it's unsafe, so, in xdbg_graph_add_edge() special mapping variable is
used + QEMU_BUILD_BUG_ON on BLK_PERM_ALL.

I think something like this should be done here.

> +    }
> +
> +out:
> +    qapi_free_BlockPermissionList(perm_list);
> +    visit_free(v);
> +    qobject_unref(subqdict);
> +    qobject_unref(crumpled_subqdict);
> +    return ret;
> +}
> +
> +static int blkdebug_parse_perms(BDRVBlkdebugState *s, QDict *options,
> +                                Error **errp)
> +{
> +    int ret;
> +
> +    ret = blkdebug_parse_perm_list(&s->take_child_perms, options,
> +                                   "take-child-perms.", errp);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +
> +    ret = blkdebug_parse_perm_list(&s->unshare_child_perms, options,
> +                                   "unshare-child-perms.", errp);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +
> +    return 0;
> +}
> +
>   static QemuOptsList runtime_opts = {
>       .name = "blkdebug",
>       .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
> @@ -419,6 +487,12 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
>       /* Set initial state */
>       s->state = 1;
>   
> +    /* Parse permissions modifiers before opening the image file */
> +    ret = blkdebug_parse_perms(s, options, errp);
> +    if (ret < 0) {
> +        goto out;
> +    }
> +
>       /* Open the image file */
>       bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
>                                  bs, &child_file, false, &local_err);
> @@ -916,6 +990,21 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
>       return 0;
>   }
>   
> +static void blkdebug_child_perm(BlockDriverState *bs, BdrvChild *c,
> +                                const BdrvChildRole *role,
> +                                BlockReopenQueue *reopen_queue,
> +                                uint64_t perm, uint64_t shared,
> +                                uint64_t *nperm, uint64_t *nshared)
> +{
> +    BDRVBlkdebugState *s = bs->opaque;
> +
> +    bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
> +                              nperm, nshared);
> +
> +    *nperm |= s->take_child_perms;
> +    *nshared &= ~s->unshare_child_perms;
> +}
> +
>   static const char *const blkdebug_strong_runtime_opts[] = {
>       "config",
>       "inject-error.",
> @@ -940,7 +1029,7 @@ static BlockDriver bdrv_blkdebug = {
>       .bdrv_file_open         = blkdebug_open,
>       .bdrv_close             = blkdebug_close,
>       .bdrv_reopen_prepare    = blkdebug_reopen_prepare,
> -    .bdrv_child_perm        = bdrv_filter_default_perms,
> +    .bdrv_child_perm        = blkdebug_child_perm,
>   
>       .bdrv_getlength         = blkdebug_getlength,
>       .bdrv_refresh_filename  = blkdebug_refresh_filename,
> 


-- 
Best regards,
Vladimir

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

* Re: [PATCH v3 2/4] blkdebug: Allow taking/unsharing permissions
  2019-10-16 11:13   ` Vladimir Sementsov-Ogievskiy
@ 2019-10-28 10:46     ` Max Reitz
  2019-10-28 11:30       ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 9+ messages in thread
From: Max Reitz @ 2019-10-28 10:46 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: Kevin Wolf, John Snow, qemu-devel


[-- Attachment #1.1: Type: text/plain, Size: 5811 bytes --]

On 16.10.19 13:13, Vladimir Sementsov-Ogievskiy wrote:
> 14.10.2019 18:39, Max Reitz wrote:
>> Sometimes it is useful to be able to add a node to the block graph that
>> takes or unshare a certain set of permissions for debugging purposes.
>> This patch adds this capability to blkdebug.
>>
>> (Note that you cannot make blkdebug release or share permissions that it
>> needs to take or cannot share, because this might result in assertion
>> failures in the block layer.  But if the blkdebug node has no parents,
>> it will not take any permissions and share everything by default, so you
>> can then freely choose what permissions to take and share.)
>>
>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>> ---
>>   qapi/block-core.json | 14 ++++++-
>>   block/blkdebug.c     | 91 +++++++++++++++++++++++++++++++++++++++++++-
>>   2 files changed, 103 insertions(+), 2 deletions(-)
>>
>> diff --git a/qapi/block-core.json b/qapi/block-core.json
>> index f66553aac7..054ce651de 100644
>> --- a/qapi/block-core.json
>> +++ b/qapi/block-core.json
>> @@ -3453,6 +3453,16 @@
>>   #
>>   # @set-state:       array of state-change descriptions
>>   #
>> +# @take-child-perms: Permissions to take on @image in addition to what
>> +#                    is necessary anyway (which depends on how the
>> +#                    blkdebug node is used).  Defaults to none.
>> +#                    (since 4.2)
>> +#
>> +# @unshare-child-perms: Permissions not to share on @image in addition
>> +#                       to what cannot be shared anyway (which depends
>> +#                       on how the blkdebug node is used).  Defaults
>> +#                       to none.  (since 4.2)
>> +#
>>   # Since: 2.9
>>   ##
>>   { 'struct': 'BlockdevOptionsBlkdebug',
>> @@ -3462,7 +3472,9 @@
>>               '*opt-write-zero': 'int32', '*max-write-zero': 'int32',
>>               '*opt-discard': 'int32', '*max-discard': 'int32',
>>               '*inject-error': ['BlkdebugInjectErrorOptions'],
>> -            '*set-state': ['BlkdebugSetStateOptions'] } }
>> +            '*set-state': ['BlkdebugSetStateOptions'],
>> +            '*take-child-perms': ['BlockPermission'],
>> +            '*unshare-child-perms': ['BlockPermission'] } }
>>   
>>   ##
>>   # @BlockdevOptionsBlklogwrites:
>> diff --git a/block/blkdebug.c b/block/blkdebug.c
>> index 5ae96c52b0..6807c03065 100644
>> --- a/block/blkdebug.c
>> +++ b/block/blkdebug.c
>> @@ -28,10 +28,14 @@
>>   #include "qemu/cutils.h"
>>   #include "qemu/config-file.h"
>>   #include "block/block_int.h"
>> +#include "block/qdict.h"
>>   #include "qemu/module.h"
>>   #include "qemu/option.h"
>> +#include "qapi/qapi-visit-block-core.h"
>>   #include "qapi/qmp/qdict.h"
>> +#include "qapi/qmp/qlist.h"
>>   #include "qapi/qmp/qstring.h"
>> +#include "qapi/qobject-input-visitor.h"
>>   #include "sysemu/qtest.h"
>>   
>>   typedef struct BDRVBlkdebugState {
>> @@ -44,6 +48,9 @@ typedef struct BDRVBlkdebugState {
>>       uint64_t opt_discard;
>>       uint64_t max_discard;
>>   
>> +    uint64_t take_child_perms;
>> +    uint64_t unshare_child_perms;
>> +
>>       /* For blkdebug_refresh_filename() */
>>       char *config_file;
>>   
>> @@ -344,6 +351,67 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
>>       qdict_put_str(options, "x-image", filename);
>>   }
>>   
>> +static int blkdebug_parse_perm_list(uint64_t *dest, QDict *options,
>> +                                    const char *prefix, Error **errp)
>> +{
>> +    int ret = 0;
>> +    QDict *subqdict = NULL;
>> +    QObject *crumpled_subqdict = NULL;
>> +    Visitor *v = NULL;
>> +    BlockPermissionList *perm_list = NULL, *element;
>> +    Error *local_err = NULL;
>> +
>> +    qdict_extract_subqdict(options, &subqdict, prefix);
>> +    if (!qdict_size(subqdict)) {
> 
> 
> Hmm, you consider it as a success, so you mean default. Then, it's safer to
> set *dest = 0 here.

“Safer” depends on the purpose of this function, and right now it’s
simply to set all fields that are given in the options; not to reset any
that aren’t.

I suppose there’s no harm in changing the purpose of the function, though.

>> +        goto out;
>> +    }
>> +
>> +    crumpled_subqdict = qdict_crumple(subqdict, errp);
>> +    if (!crumpled_subqdict) {
>> +        ret = -EINVAL;
>> +        goto out;
>> +    }
>> +
>> +    v = qobject_input_visitor_new(crumpled_subqdict);
>> +    visit_type_BlockPermissionList(v, NULL, &perm_list, &local_err);
>> +    if (local_err) {
>> +        error_propagate(errp, local_err);
>> +        ret = -EINVAL;
>> +        goto out;
>> +    }
>> +
> 
> I'd prefer explicitly set *dest = 0 here too.
> 
>> +    for (element = perm_list; element; element = element->next) {
>> +        *dest |= UINT64_C(1) << element->value;
> 
> Hmm, so, you rely on correct correspondence between generated BlockPermission enum
> and unnamed enum with BLK_PERM_* constants...
> 
> I'm afraid it's unsafe, so, in xdbg_graph_add_edge() special mapping variable is
> used + QEMU_BUILD_BUG_ON on BLK_PERM_ALL.
> 
> I think something like this should be done here.

I don’t really like it because I think it’s cool to have a 1-to-1
relationship between the BLK_PERM_* constants and what’s defined in
QAPI.  I don’t quite like assuming there isn’t, because there’s no
reason why they wouldn’t correspond.

In fact, I’d rather define the BLK_PERM_* constants based on the QAPI
enum, but I don’t know whether we want to include the QAPI header into
block.h.  ...Well, judging from a quick test it looks like the header is
included into it recursively anyway.  So maybe I’ll do that instead.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH v3 0/4] mirror: Do not dereference invalid pointers
  2019-10-14 15:39 [PATCH v3 0/4] mirror: Do not dereference invalid pointers Max Reitz
                   ` (3 preceding siblings ...)
  2019-10-14 15:39 ` [PATCH v3 4/4] iotests: Add test for failing mirror complete Max Reitz
@ 2019-10-28 10:50 ` Max Reitz
  4 siblings, 0 replies; 9+ messages in thread
From: Max Reitz @ 2019-10-28 10:50 UTC (permalink / raw)
  To: qemu-block
  Cc: Kevin Wolf, Vladimir Sementsov-Ogievskiy, John Snow, qemu-devel


[-- Attachment #1.1: Type: text/plain, Size: 1155 bytes --]

On 14.10.19 17:39, Max Reitz wrote:
> Hi,
> 
> v2’s cover letter should explain everything:
> 
> https://lists.nongnu.org/archive/html/qemu-block/2019-09/msg01079.html
> 
> 
> v3:
> - Patch 2: Use input visitor as proposed by Vladimir
> 
> git-backport-diff against v2:
> 
> Key:
> [----] : patches are identical
> [####] : number of functional differences between upstream/downstream patch
> [down] : patch is downstream-only
> The flags [FC] indicate (F)unctional and (C)ontextual differences, respectively
> 
> 001/4:[----] [--] 'mirror: Do not dereference invalid pointers'
> 002/4:[0041] [FC] 'blkdebug: Allow taking/unsharing permissions'
> 003/4:[----] [--] 'iotests: Add @error to wait_until_completed'
> 004/4:[----] [--] 'iotests: Add test for failing mirror complete'
> 
> 
> Max Reitz (4):
>   mirror: Do not dereference invalid pointers
>   blkdebug: Allow taking/unsharing permissions
>   iotests: Add @error to wait_until_completed
>   iotests: Add test for failing mirror complete

I’ve applied patch 1 to my block branch, and I’ll send a for-next series
(for-5.0, I suppose?) for the rest.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH v3 2/4] blkdebug: Allow taking/unsharing permissions
  2019-10-28 10:46     ` Max Reitz
@ 2019-10-28 11:30       ` Vladimir Sementsov-Ogievskiy
  0 siblings, 0 replies; 9+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-10-28 11:30 UTC (permalink / raw)
  To: Max Reitz, qemu-block; +Cc: Kevin Wolf, John Snow, qemu-devel

28.10.2019 13:46, Max Reitz wrote:
> On 16.10.19 13:13, Vladimir Sementsov-Ogievskiy wrote:
>> 14.10.2019 18:39, Max Reitz wrote:
>>> Sometimes it is useful to be able to add a node to the block graph that
>>> takes or unshare a certain set of permissions for debugging purposes.
>>> This patch adds this capability to blkdebug.
>>>
>>> (Note that you cannot make blkdebug release or share permissions that it
>>> needs to take or cannot share, because this might result in assertion
>>> failures in the block layer.  But if the blkdebug node has no parents,
>>> it will not take any permissions and share everything by default, so you
>>> can then freely choose what permissions to take and share.)
>>>
>>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>>> ---
>>>    qapi/block-core.json | 14 ++++++-
>>>    block/blkdebug.c     | 91 +++++++++++++++++++++++++++++++++++++++++++-
>>>    2 files changed, 103 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/qapi/block-core.json b/qapi/block-core.json
>>> index f66553aac7..054ce651de 100644
>>> --- a/qapi/block-core.json
>>> +++ b/qapi/block-core.json
>>> @@ -3453,6 +3453,16 @@
>>>    #
>>>    # @set-state:       array of state-change descriptions
>>>    #
>>> +# @take-child-perms: Permissions to take on @image in addition to what
>>> +#                    is necessary anyway (which depends on how the
>>> +#                    blkdebug node is used).  Defaults to none.
>>> +#                    (since 4.2)
>>> +#
>>> +# @unshare-child-perms: Permissions not to share on @image in addition
>>> +#                       to what cannot be shared anyway (which depends
>>> +#                       on how the blkdebug node is used).  Defaults
>>> +#                       to none.  (since 4.2)
>>> +#
>>>    # Since: 2.9
>>>    ##
>>>    { 'struct': 'BlockdevOptionsBlkdebug',
>>> @@ -3462,7 +3472,9 @@
>>>                '*opt-write-zero': 'int32', '*max-write-zero': 'int32',
>>>                '*opt-discard': 'int32', '*max-discard': 'int32',
>>>                '*inject-error': ['BlkdebugInjectErrorOptions'],
>>> -            '*set-state': ['BlkdebugSetStateOptions'] } }
>>> +            '*set-state': ['BlkdebugSetStateOptions'],
>>> +            '*take-child-perms': ['BlockPermission'],
>>> +            '*unshare-child-perms': ['BlockPermission'] } }
>>>    
>>>    ##
>>>    # @BlockdevOptionsBlklogwrites:
>>> diff --git a/block/blkdebug.c b/block/blkdebug.c
>>> index 5ae96c52b0..6807c03065 100644
>>> --- a/block/blkdebug.c
>>> +++ b/block/blkdebug.c
>>> @@ -28,10 +28,14 @@
>>>    #include "qemu/cutils.h"
>>>    #include "qemu/config-file.h"
>>>    #include "block/block_int.h"
>>> +#include "block/qdict.h"
>>>    #include "qemu/module.h"
>>>    #include "qemu/option.h"
>>> +#include "qapi/qapi-visit-block-core.h"
>>>    #include "qapi/qmp/qdict.h"
>>> +#include "qapi/qmp/qlist.h"
>>>    #include "qapi/qmp/qstring.h"
>>> +#include "qapi/qobject-input-visitor.h"
>>>    #include "sysemu/qtest.h"
>>>    
>>>    typedef struct BDRVBlkdebugState {
>>> @@ -44,6 +48,9 @@ typedef struct BDRVBlkdebugState {
>>>        uint64_t opt_discard;
>>>        uint64_t max_discard;
>>>    
>>> +    uint64_t take_child_perms;
>>> +    uint64_t unshare_child_perms;
>>> +
>>>        /* For blkdebug_refresh_filename() */
>>>        char *config_file;
>>>    
>>> @@ -344,6 +351,67 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
>>>        qdict_put_str(options, "x-image", filename);
>>>    }
>>>    
>>> +static int blkdebug_parse_perm_list(uint64_t *dest, QDict *options,
>>> +                                    const char *prefix, Error **errp)
>>> +{
>>> +    int ret = 0;
>>> +    QDict *subqdict = NULL;
>>> +    QObject *crumpled_subqdict = NULL;
>>> +    Visitor *v = NULL;
>>> +    BlockPermissionList *perm_list = NULL, *element;
>>> +    Error *local_err = NULL;
>>> +
>>> +    qdict_extract_subqdict(options, &subqdict, prefix);
>>> +    if (!qdict_size(subqdict)) {
>>
>>
>> Hmm, you consider it as a success, so you mean default. Then, it's safer to
>> set *dest = 0 here.
> 
> “Safer” depends on the purpose of this function, and right now it’s
> simply to set all fields that are given in the options; not to reset any
> that aren’t.
> 
> I suppose there’s no harm in changing the purpose of the function, though.
> 
>>> +        goto out;
>>> +    }
>>> +
>>> +    crumpled_subqdict = qdict_crumple(subqdict, errp);
>>> +    if (!crumpled_subqdict) {
>>> +        ret = -EINVAL;
>>> +        goto out;
>>> +    }
>>> +
>>> +    v = qobject_input_visitor_new(crumpled_subqdict);
>>> +    visit_type_BlockPermissionList(v, NULL, &perm_list, &local_err);
>>> +    if (local_err) {
>>> +        error_propagate(errp, local_err);
>>> +        ret = -EINVAL;
>>> +        goto out;
>>> +    }
>>> +
>>
>> I'd prefer explicitly set *dest = 0 here too.
>>
>>> +    for (element = perm_list; element; element = element->next) {
>>> +        *dest |= UINT64_C(1) << element->value;
>>
>> Hmm, so, you rely on correct correspondence between generated BlockPermission enum
>> and unnamed enum with BLK_PERM_* constants...
>>
>> I'm afraid it's unsafe, so, in xdbg_graph_add_edge() special mapping variable is
>> used + QEMU_BUILD_BUG_ON on BLK_PERM_ALL.
>>
>> I think something like this should be done here.
> 
> I don’t really like it because I think it’s cool to have a 1-to-1
> relationship between the BLK_PERM_* constants and what’s defined in
> QAPI.  I don’t quite like assuming there isn’t, because there’s no
> reason why they wouldn’t correspond.
> 
> In fact, I’d rather define the BLK_PERM_* constants based on the QAPI
> enum, but I don’t know whether we want to include the QAPI header into
> block.h.  ...Well, judging from a quick test it looks like the header is
> included into it recursively anyway.  So maybe I’ll do that instead.
> 

If BLK_PERM_* are defined in manner making the correspondence obvious, that's OK, of course.


-- 
Best regards,
Vladimir

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

end of thread, other threads:[~2019-10-28 11:31 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-14 15:39 [PATCH v3 0/4] mirror: Do not dereference invalid pointers Max Reitz
2019-10-14 15:39 ` [PATCH v3 1/4] " Max Reitz
2019-10-14 15:39 ` [PATCH v3 2/4] blkdebug: Allow taking/unsharing permissions Max Reitz
2019-10-16 11:13   ` Vladimir Sementsov-Ogievskiy
2019-10-28 10:46     ` Max Reitz
2019-10-28 11:30       ` Vladimir Sementsov-Ogievskiy
2019-10-14 15:39 ` [PATCH v3 3/4] iotests: Add @error to wait_until_completed Max Reitz
2019-10-14 15:39 ` [PATCH v3 4/4] iotests: Add test for failing mirror complete Max Reitz
2019-10-28 10:50 ` [PATCH v3 0/4] mirror: Do not dereference invalid pointers Max Reitz

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).