Message ID | 20240328102052.3499331-7-marcandre.lureau@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | -Werror=maybe-uninitialized fixes | expand |
On 28.03.24 13:20, marcandre.lureau@redhat.com wrote: > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > ../block/stream.c:193:19: error: ‘unfiltered_bs’ may be used uninitialized [-Werror=maybe-uninitialized] > ../block/stream.c:176:5: error: ‘len’ may be used uninitialized [-Werror=maybe-uninitialized] > trace/trace-block.h:906:9: error: ‘ret’ may be used uninitialized [-Werror=maybe-uninitialized] > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Again, same false-positives, because of WITH_GRAPH_RDLOCK_GUARD().. Didn't you try to change WITH_ macros somehow, so that compiler believe in our good intentions? Actually, "unused variable initialization" is bad thing too. Anyway, if no better solution for now: Acked-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> > --- > block/stream.c | 6 +++--- > 1 file changed, 3 insertions(+), 3 deletions(-) > > diff --git a/block/stream.c b/block/stream.c > index 7031eef12b..9076203193 100644 > --- a/block/stream.c > +++ b/block/stream.c > @@ -155,8 +155,8 @@ static void stream_clean(Job *job) > static int coroutine_fn stream_run(Job *job, Error **errp) > { > StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); > - BlockDriverState *unfiltered_bs; > - int64_t len; > + BlockDriverState *unfiltered_bs = NULL; > + int64_t len = -1; > int64_t offset = 0; > int error = 0; > int64_t n = 0; /* bytes */ > @@ -177,7 +177,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp) > > for ( ; offset < len; offset += n) { > bool copy; > - int ret; > + int ret = -1; > > /* Note that even when no rate limit is applied we need to yield > * with no pending I/O here so that bdrv_drain_all() returns.
Hi On Fri, Mar 29, 2024 at 12:35 PM Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> wrote: > > On 28.03.24 13:20, marcandre.lureau@redhat.com wrote: > > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > > > ../block/stream.c:193:19: error: ‘unfiltered_bs’ may be used uninitialized [-Werror=maybe-uninitialized] > > ../block/stream.c:176:5: error: ‘len’ may be used uninitialized [-Werror=maybe-uninitialized] > > trace/trace-block.h:906:9: error: ‘ret’ may be used uninitialized [-Werror=maybe-uninitialized] > > > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> > > Again, same false-positives, because of WITH_GRAPH_RDLOCK_GUARD().. > > Didn't you try to change WITH_ macros somehow, so that compiler believe in our good intentions? > #define WITH_QEMU_LOCK_GUARD_(x, var) \ for (g_autoptr(QemuLockable) var = \ qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))); \ var; \ qemu_lockable_auto_unlock(var), var = NULL) I can't think of a clever way to rewrite this. The compiler probably thinks the loop may not run, due to the "var" condition. But how to convince it otherwise? it's hard to introduce another variable too.. > Actually, "unused variable initialization" is bad thing too. > > Anyway, if no better solution for now: > Acked-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> > > > --- > > block/stream.c | 6 +++--- > > 1 file changed, 3 insertions(+), 3 deletions(-) > > > > diff --git a/block/stream.c b/block/stream.c > > index 7031eef12b..9076203193 100644 > > --- a/block/stream.c > > +++ b/block/stream.c > > @@ -155,8 +155,8 @@ static void stream_clean(Job *job) > > static int coroutine_fn stream_run(Job *job, Error **errp) > > { > > StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); > > - BlockDriverState *unfiltered_bs; > > - int64_t len; > > + BlockDriverState *unfiltered_bs = NULL; > > + int64_t len = -1; > > int64_t offset = 0; > > int error = 0; > > int64_t n = 0; /* bytes */ > > @@ -177,7 +177,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp) > > > > for ( ; offset < len; offset += n) { > > bool copy; > > - int ret; > > + int ret = -1; > > > > /* Note that even when no rate limit is applied we need to yield > > * with no pending I/O here so that bdrv_drain_all() returns. > > -- > Best regards, > Vladimir > >
On 02.04.24 12:12, Marc-André Lureau wrote: > Hi > > On Fri, Mar 29, 2024 at 12:35 PM Vladimir Sementsov-Ogievskiy > <vsementsov@yandex-team.ru> wrote: >> >> On 28.03.24 13:20, marcandre.lureau@redhat.com wrote: >>> From: Marc-André Lureau <marcandre.lureau@redhat.com> >>> >>> ../block/stream.c:193:19: error: ‘unfiltered_bs’ may be used uninitialized [-Werror=maybe-uninitialized] >>> ../block/stream.c:176:5: error: ‘len’ may be used uninitialized [-Werror=maybe-uninitialized] >>> trace/trace-block.h:906:9: error: ‘ret’ may be used uninitialized [-Werror=maybe-uninitialized] >>> >>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> >> >> Again, same false-positives, because of WITH_GRAPH_RDLOCK_GUARD().. >> >> Didn't you try to change WITH_ macros somehow, so that compiler believe in our good intentions? >> > > > #define WITH_QEMU_LOCK_GUARD_(x, var) \ > for (g_autoptr(QemuLockable) var = \ > qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))); \ > var; \ > qemu_lockable_auto_unlock(var), var = NULL) > > I can't think of a clever way to rewrite this. The compiler probably > thinks the loop may not run, due to the "var" condition. But how to > convince it otherwise? it's hard to introduce another variable too.. hmm. maybe like this? #define WITH_QEMU_LOCK_GUARD_(x, var) \ for (g_autoptr(QemuLockable) var = \ qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))), \ var2 = (void *)(true); \ var2; \ qemu_lockable_auto_unlock(var), var2 = NULL) probably, it would be simpler for compiler to understand the logic this way. Could you check? (actually, will also need to construct var2 name same way as for var) > >> Actually, "unused variable initialization" is bad thing too. >> >> Anyway, if no better solution for now: >> Acked-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> >> >>> --- >>> block/stream.c | 6 +++--- >>> 1 file changed, 3 insertions(+), 3 deletions(-) >>> >>> diff --git a/block/stream.c b/block/stream.c >>> index 7031eef12b..9076203193 100644 >>> --- a/block/stream.c >>> +++ b/block/stream.c >>> @@ -155,8 +155,8 @@ static void stream_clean(Job *job) >>> static int coroutine_fn stream_run(Job *job, Error **errp) >>> { >>> StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); >>> - BlockDriverState *unfiltered_bs; >>> - int64_t len; >>> + BlockDriverState *unfiltered_bs = NULL; >>> + int64_t len = -1; >>> int64_t offset = 0; >>> int error = 0; >>> int64_t n = 0; /* bytes */ >>> @@ -177,7 +177,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp) >>> >>> for ( ; offset < len; offset += n) { >>> bool copy; >>> - int ret; >>> + int ret = -1; >>> >>> /* Note that even when no rate limit is applied we need to yield >>> * with no pending I/O here so that bdrv_drain_all() returns. >> >> -- >> Best regards, >> Vladimir >> >> > >
On Tue, Apr 02, 2024 at 12:58:43PM +0300, Vladimir Sementsov-Ogievskiy wrote: > > > Again, same false-positives, because of WITH_GRAPH_RDLOCK_GUARD().. > > > > > > Didn't you try to change WITH_ macros somehow, so that compiler believe in our good intentions? > > > > > > > > > #define WITH_QEMU_LOCK_GUARD_(x, var) \ > > for (g_autoptr(QemuLockable) var = \ > > qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))); \ > > var; \ > > qemu_lockable_auto_unlock(var), var = NULL) > > > > I can't think of a clever way to rewrite this. The compiler probably > > thinks the loop may not run, due to the "var" condition. But how to > > convince it otherwise? it's hard to introduce another variable too.. > > > hmm. maybe like this? > > #define WITH_QEMU_LOCK_GUARD_(x, var) \ > for (g_autoptr(QemuLockable) var = \ > qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))), \ > var2 = (void *)(true); \ > var2; \ > qemu_lockable_auto_unlock(var), var2 = NULL) > > > probably, it would be simpler for compiler to understand the logic this way. Could you check? Wouldn't that attach __attribute__((cleanup(xxx))) to var2, at which point we could cause the compiler to call xxx((void*)(true)) if the user does an early return inside the lock guard, with disastrous consequences? Or is the __attribute__ applied only to the first out of two declarations in a list?
On 02.04.24 18:34, Eric Blake wrote: > On Tue, Apr 02, 2024 at 12:58:43PM +0300, Vladimir Sementsov-Ogievskiy wrote: >>>> Again, same false-positives, because of WITH_GRAPH_RDLOCK_GUARD().. >>>> >>>> Didn't you try to change WITH_ macros somehow, so that compiler believe in our good intentions? >>>> >>> >>> >>> #define WITH_QEMU_LOCK_GUARD_(x, var) \ >>> for (g_autoptr(QemuLockable) var = \ >>> qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))); \ >>> var; \ >>> qemu_lockable_auto_unlock(var), var = NULL) >>> >>> I can't think of a clever way to rewrite this. The compiler probably >>> thinks the loop may not run, due to the "var" condition. But how to >>> convince it otherwise? it's hard to introduce another variable too.. >> >> >> hmm. maybe like this? >> >> #define WITH_QEMU_LOCK_GUARD_(x, var) \ >> for (g_autoptr(QemuLockable) var = \ >> qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))), \ >> var2 = (void *)(true); \ >> var2; \ >> qemu_lockable_auto_unlock(var), var2 = NULL) >> >> >> probably, it would be simpler for compiler to understand the logic this way. Could you check? > > Wouldn't that attach __attribute__((cleanup(xxx))) to var2, at which > point we could cause the compiler to call xxx((void*)(true)) if the > user does an early return inside the lock guard, with disastrous > consequences? Or is the __attribute__ applied only to the first out > of two declarations in a list? > Oh, most probably you are right, seems g_autoptr apply it to both variables. Also, we don't need qemu_lockable_auto_unlock(var) separate call, if we zero-out another variable. So, me fixing: #define WITH_QEMU_LOCK_GUARD_(x, var) \ for (QemuLockable *var __attribute__((cleanup(qemu_lockable_auto_unlock))) = qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))), \ *var2 = (void *)(true); \ var2; \ var2 = NULL) (and we'll need to modify qemu_lockable_auto_unlock() to take "QemuLockable **x" argument)
Hi On Tue, Apr 2, 2024 at 11:24 PM Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> wrote: > > On 02.04.24 18:34, Eric Blake wrote: > > On Tue, Apr 02, 2024 at 12:58:43PM +0300, Vladimir Sementsov-Ogievskiy wrote: > >>>> Again, same false-positives, because of WITH_GRAPH_RDLOCK_GUARD().. > >>>> > >>>> Didn't you try to change WITH_ macros somehow, so that compiler believe in our good intentions? > >>>> > >>> > >>> > >>> #define WITH_QEMU_LOCK_GUARD_(x, var) \ > >>> for (g_autoptr(QemuLockable) var = \ > >>> qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))); \ > >>> var; \ > >>> qemu_lockable_auto_unlock(var), var = NULL) > >>> > >>> I can't think of a clever way to rewrite this. The compiler probably > >>> thinks the loop may not run, due to the "var" condition. But how to > >>> convince it otherwise? it's hard to introduce another variable too.. > >> > >> > >> hmm. maybe like this? > >> > >> #define WITH_QEMU_LOCK_GUARD_(x, var) \ > >> for (g_autoptr(QemuLockable) var = \ > >> qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))), \ > >> var2 = (void *)(true); \ > >> var2; \ > >> qemu_lockable_auto_unlock(var), var2 = NULL) > >> > >> > >> probably, it would be simpler for compiler to understand the logic this way. Could you check? > > > > Wouldn't that attach __attribute__((cleanup(xxx))) to var2, at which > > point we could cause the compiler to call xxx((void*)(true)) if the > > user does an early return inside the lock guard, with disastrous > > consequences? Or is the __attribute__ applied only to the first out > > of two declarations in a list? > > > > Oh, most probably you are right, seems g_autoptr apply it to both variables. Also, we don't need qemu_lockable_auto_unlock(var) separate call, if we zero-out another variable. So, me fixing: > > #define WITH_QEMU_LOCK_GUARD_(x, var) \ > for (QemuLockable *var __attribute__((cleanup(qemu_lockable_auto_unlock))) = qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))), \ > *var2 = (void *)(true); \ > var2; \ > var2 = NULL) > > (and we'll need to modify qemu_lockable_auto_unlock() to take "QemuLockable **x" argument) > That's almost good enough. I fixed a few things to generate var2. I applied a similar approach to WITH_GRAPH_RDLOCK_GUARD macro: --- a/include/block/graph-lock.h +++ b/include/block/graph-lock.h @@ -224,13 +224,22 @@ graph_lockable_auto_unlock(GraphLockable *x) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockable, graph_lockable_auto_unlock) -#define WITH_GRAPH_RDLOCK_GUARD_(var) \ - for (g_autoptr(GraphLockable) var = graph_lockable_auto_lock(GML_OBJ_()); \ - var; \ - graph_lockable_auto_unlock(var), var = NULL) +static inline void TSA_NO_TSA coroutine_fn +graph_lockable_auto_cleanup(GraphLockable **x) +{ + graph_lockable_auto_unlock(*x); +} + +#define WITH_GRAPH_RDLOCK_GUARD__(var) \ + GraphLockable *var \ + __attribute__((cleanup(graph_lockable_auto_cleanup))) G_GNUC_UNUSED = \ + graph_lockable_auto_lock(GML_OBJ_()) + +#define WITH_GRAPH_RDLOCK_GUARD_(var, var2) \ + for (WITH_GRAPH_RDLOCK_GUARD__(var), *var2 = (void *)true; var2; var2 = NULL) #define WITH_GRAPH_RDLOCK_GUARD() \ - WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__)) + WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__), glue(graph_lockable_auto, __COUNTER__)) Unfortunately, it doesn't work in all cases. It seems to have issues with some guards: ../block/stream.c: In function ‘stream_run’: ../block/stream.c:216:12: error: ‘ret’ may be used uninitialized [-Werror=maybe-uninitialized] 216 | if (ret < 0) { What should we do? change the macros + cherry-pick the missing false-positives, or keep this series as is?
On 03.04.24 11:11, Marc-André Lureau wrote: > Hi > > On Tue, Apr 2, 2024 at 11:24 PM Vladimir Sementsov-Ogievskiy > <vsementsov@yandex-team.ru> wrote: >> >> On 02.04.24 18:34, Eric Blake wrote: >>> On Tue, Apr 02, 2024 at 12:58:43PM +0300, Vladimir Sementsov-Ogievskiy wrote: >>>>>> Again, same false-positives, because of WITH_GRAPH_RDLOCK_GUARD().. >>>>>> >>>>>> Didn't you try to change WITH_ macros somehow, so that compiler believe in our good intentions? >>>>>> >>>>> >>>>> >>>>> #define WITH_QEMU_LOCK_GUARD_(x, var) \ >>>>> for (g_autoptr(QemuLockable) var = \ >>>>> qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))); \ >>>>> var; \ >>>>> qemu_lockable_auto_unlock(var), var = NULL) >>>>> >>>>> I can't think of a clever way to rewrite this. The compiler probably >>>>> thinks the loop may not run, due to the "var" condition. But how to >>>>> convince it otherwise? it's hard to introduce another variable too.. >>>> >>>> >>>> hmm. maybe like this? >>>> >>>> #define WITH_QEMU_LOCK_GUARD_(x, var) \ >>>> for (g_autoptr(QemuLockable) var = \ >>>> qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))), \ >>>> var2 = (void *)(true); \ >>>> var2; \ >>>> qemu_lockable_auto_unlock(var), var2 = NULL) >>>> >>>> >>>> probably, it would be simpler for compiler to understand the logic this way. Could you check? >>> >>> Wouldn't that attach __attribute__((cleanup(xxx))) to var2, at which >>> point we could cause the compiler to call xxx((void*)(true)) if the >>> user does an early return inside the lock guard, with disastrous >>> consequences? Or is the __attribute__ applied only to the first out >>> of two declarations in a list? >>> >> >> Oh, most probably you are right, seems g_autoptr apply it to both variables. Also, we don't need qemu_lockable_auto_unlock(var) separate call, if we zero-out another variable. So, me fixing: >> >> #define WITH_QEMU_LOCK_GUARD_(x, var) \ >> for (QemuLockable *var __attribute__((cleanup(qemu_lockable_auto_unlock))) = qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))), \ >> *var2 = (void *)(true); \ >> var2; \ >> var2 = NULL) >> >> (and we'll need to modify qemu_lockable_auto_unlock() to take "QemuLockable **x" argument) >> > > That's almost good enough. I fixed a few things to generate var2. > > I applied a similar approach to WITH_GRAPH_RDLOCK_GUARD macro: > > --- a/include/block/graph-lock.h > +++ b/include/block/graph-lock.h > @@ -224,13 +224,22 @@ graph_lockable_auto_unlock(GraphLockable *x) > > G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockable, graph_lockable_auto_unlock) > > -#define WITH_GRAPH_RDLOCK_GUARD_(var) \ > - for (g_autoptr(GraphLockable) var = graph_lockable_auto_lock(GML_OBJ_()); \ > - var; \ > - graph_lockable_auto_unlock(var), var = NULL) > +static inline void TSA_NO_TSA coroutine_fn > +graph_lockable_auto_cleanup(GraphLockable **x) > +{ > + graph_lockable_auto_unlock(*x); > +} > + > +#define WITH_GRAPH_RDLOCK_GUARD__(var) \ > + GraphLockable *var \ > + __attribute__((cleanup(graph_lockable_auto_cleanup))) G_GNUC_UNUSED = \ > + graph_lockable_auto_lock(GML_OBJ_()) > + > +#define WITH_GRAPH_RDLOCK_GUARD_(var, var2) \ > + for (WITH_GRAPH_RDLOCK_GUARD__(var), *var2 = (void *)true; var2; > var2 = NULL) > > #define WITH_GRAPH_RDLOCK_GUARD() \ > - WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__)) > + WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__), > glue(graph_lockable_auto, __COUNTER__)) > > Unfortunately, it doesn't work in all cases. It seems to have issues > with some guards: > ../block/stream.c: In function ‘stream_run’: > ../block/stream.c:216:12: error: ‘ret’ may be used uninitialized > [-Werror=maybe-uninitialized] > 216 | if (ret < 0) { > > So, updated macro helps in some cases, but doesn't help here? Intersting, why. > What should we do? change the macros + cherry-pick the missing > false-positives, or keep this series as is? > > I think marco + missing is better. No reason to add dead-initializations in cases where new macros helps. Still, would be good to understand, what's the difference, why it help on some cases and not help in another.
Hi On Wed, Apr 3, 2024 at 12:31 PM Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> wrote: > > On 03.04.24 11:11, Marc-André Lureau wrote: > > Hi > > > > On Tue, Apr 2, 2024 at 11:24 PM Vladimir Sementsov-Ogievskiy > > <vsementsov@yandex-team.ru> wrote: > >> > >> On 02.04.24 18:34, Eric Blake wrote: > >>> On Tue, Apr 02, 2024 at 12:58:43PM +0300, Vladimir Sementsov-Ogievskiy wrote: > >>>>>> Again, same false-positives, because of WITH_GRAPH_RDLOCK_GUARD().. > >>>>>> > >>>>>> Didn't you try to change WITH_ macros somehow, so that compiler believe in our good intentions? > >>>>>> > >>>>> > >>>>> > >>>>> #define WITH_QEMU_LOCK_GUARD_(x, var) \ > >>>>> for (g_autoptr(QemuLockable) var = \ > >>>>> qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))); \ > >>>>> var; \ > >>>>> qemu_lockable_auto_unlock(var), var = NULL) > >>>>> > >>>>> I can't think of a clever way to rewrite this. The compiler probably > >>>>> thinks the loop may not run, due to the "var" condition. But how to > >>>>> convince it otherwise? it's hard to introduce another variable too.. > >>>> > >>>> > >>>> hmm. maybe like this? > >>>> > >>>> #define WITH_QEMU_LOCK_GUARD_(x, var) \ > >>>> for (g_autoptr(QemuLockable) var = \ > >>>> qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))), \ > >>>> var2 = (void *)(true); \ > >>>> var2; \ > >>>> qemu_lockable_auto_unlock(var), var2 = NULL) > >>>> > >>>> > >>>> probably, it would be simpler for compiler to understand the logic this way. Could you check? > >>> > >>> Wouldn't that attach __attribute__((cleanup(xxx))) to var2, at which > >>> point we could cause the compiler to call xxx((void*)(true)) if the > >>> user does an early return inside the lock guard, with disastrous > >>> consequences? Or is the __attribute__ applied only to the first out > >>> of two declarations in a list? > >>> > >> > >> Oh, most probably you are right, seems g_autoptr apply it to both variables. Also, we don't need qemu_lockable_auto_unlock(var) separate call, if we zero-out another variable. So, me fixing: > >> > >> #define WITH_QEMU_LOCK_GUARD_(x, var) \ > >> for (QemuLockable *var __attribute__((cleanup(qemu_lockable_auto_unlock))) = qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))), \ > >> *var2 = (void *)(true); \ > >> var2; \ > >> var2 = NULL) > >> > >> (and we'll need to modify qemu_lockable_auto_unlock() to take "QemuLockable **x" argument) > >> > > > > That's almost good enough. I fixed a few things to generate var2. > > > > I applied a similar approach to WITH_GRAPH_RDLOCK_GUARD macro: > > > > --- a/include/block/graph-lock.h > > +++ b/include/block/graph-lock.h > > @@ -224,13 +224,22 @@ graph_lockable_auto_unlock(GraphLockable *x) > > > > G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockable, graph_lockable_auto_unlock) > > > > -#define WITH_GRAPH_RDLOCK_GUARD_(var) \ > > - for (g_autoptr(GraphLockable) var = graph_lockable_auto_lock(GML_OBJ_()); \ > > - var; \ > > - graph_lockable_auto_unlock(var), var = NULL) > > +static inline void TSA_NO_TSA coroutine_fn > > +graph_lockable_auto_cleanup(GraphLockable **x) > > +{ > > + graph_lockable_auto_unlock(*x); > > +} > > + > > +#define WITH_GRAPH_RDLOCK_GUARD__(var) \ > > + GraphLockable *var \ > > + __attribute__((cleanup(graph_lockable_auto_cleanup))) G_GNUC_UNUSED = \ > > + graph_lockable_auto_lock(GML_OBJ_()) > > + > > +#define WITH_GRAPH_RDLOCK_GUARD_(var, var2) \ > > + for (WITH_GRAPH_RDLOCK_GUARD__(var), *var2 = (void *)true; var2; > > var2 = NULL) > > > > #define WITH_GRAPH_RDLOCK_GUARD() \ > > - WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__)) > > + WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__), > > glue(graph_lockable_auto, __COUNTER__)) > > > > Unfortunately, it doesn't work in all cases. It seems to have issues > > with some guards: > > ../block/stream.c: In function ‘stream_run’: > > ../block/stream.c:216:12: error: ‘ret’ may be used uninitialized > > [-Werror=maybe-uninitialized] > > 216 | if (ret < 0) { > > > > > > So, updated macro helps in some cases, but doesn't help here? Intersting, why. > > > What should we do? change the macros + cherry-pick the missing > > false-positives, or keep this series as is? > > > > > > I think marco + missing is better. No reason to add dead-initializations in cases where new macros helps. Ok > Still, would be good to understand, what's the difference, why it help on some cases and not help in another. I don't know, it's like if the analyzer was lazy for this particular case, although there is nothing much different from other usages. If I replace: for (... *var2 = (void *)true; var2; with: for (... *var2 = (void *)true; var2 || true; then it doesn't warn.. Interestingly as well, if I change: for (... *var2 = (void *)true; var2; var2 = NULL) for: for (... *var2 = GML_OBJ_(); var2; var2 = NULL) GML_OBJ_() simply being &(GraphLockable) { }), an empty compound literal, then it doesn't work, in all usages. All in all, I am not sure the trick of using var2 is really reliable either.
On Wed, Apr 03, 2024 at 01:24:11PM +0400, Marc-André Lureau wrote: > > > Unfortunately, it doesn't work in all cases. It seems to have issues > > > with some guards: > > > ../block/stream.c: In function ‘stream_run’: > > > ../block/stream.c:216:12: error: ‘ret’ may be used uninitialized > > > [-Werror=maybe-uninitialized] > > > 216 | if (ret < 0) { > > > That one looks like: int ret; WITH_GRAPH_RDLOCK_GUARD() { ret = ...; } if (copy) { ret = ...; } if (ret < 0) I suspect the compiler is seeing the uncertainty possible from the second conditional, and letting it take priority over the certainty that the tweaked macro provided for the first conditional. > > > > > > > So, updated macro helps in some cases, but doesn't help here? Intersting, why. > > > > > What should we do? change the macros + cherry-pick the missing > > > false-positives, or keep this series as is? An uglier macro, with sufficient comments as to why it is ugly (in order to let us have fewer false positives where we have to add initializers) may be less churn in the code base, but I'm not necessarily sold on the ugly macro. Let's see if anyone else expresses an opinion. > > > > > > > > > > I think marco + missing is better. No reason to add dead-initializations in cases where new macros helps. > > Ok > > > Still, would be good to understand, what's the difference, why it help on some cases and not help in another. > > I don't know, it's like if the analyzer was lazy for this particular > case, although there is nothing much different from other usages. > > If I replace: > for (... *var2 = (void *)true; var2; > with: > for (... *var2 = (void *)true; var2 || true; > > then it doesn't warn.. but it also doesn't work. We want the body to execute exactly once, not infloop. > > Interestingly as well, if I change: > for (... *var2 = (void *)true; var2; var2 = NULL) > for: > for (... *var2 = GML_OBJ_(); var2; var2 = NULL) > > GML_OBJ_() simply being &(GraphLockable) { }), an empty compound > literal, then it doesn't work, in all usages. So the compiler is not figuring out that the compound literal is sufficient for an unconditional one time through the for loop body. What's worse, different compiler versions will change behavior over time. Making the code ugly to pacify a particular compiler, when that compiler might improve in the future, is a form of chasing windmills. > > All in all, I am not sure the trick of using var2 is really reliable either. And that's my biggest argument for not making the macro not more complex than it already is.
On 03.04.24 20:50, Eric Blake wrote: > On Wed, Apr 03, 2024 at 01:24:11PM +0400, Marc-André Lureau wrote: >>>> Unfortunately, it doesn't work in all cases. It seems to have issues >>>> with some guards: >>>> ../block/stream.c: In function ‘stream_run’: >>>> ../block/stream.c:216:12: error: ‘ret’ may be used uninitialized >>>> [-Werror=maybe-uninitialized] >>>> 216 | if (ret < 0) { >>>> > > That one looks like: > > int ret; > WITH_GRAPH_RDLOCK_GUARD() { > ret = ...; > } > if (copy) { > ret = ...; > } > if (ret < 0) > > I suspect the compiler is seeing the uncertainty possible from the > second conditional, and letting it take priority over the certainty > that the tweaked macro provided for the first conditional. > >>>> >>> >>> So, updated macro helps in some cases, but doesn't help here? Intersting, why. >>> >>>> What should we do? change the macros + cherry-pick the missing >>>> false-positives, or keep this series as is? > > An uglier macro, with sufficient comments as to why it is ugly (in > order to let us have fewer false positives where we have to add > initializers) may be less churn in the code base, but I'm not > necessarily sold on the ugly macro. Let's see if anyone else > expresses an opinion. > > >>>> >>>> >>> >>> I think marco + missing is better. No reason to add dead-initializations in cases where new macros helps. >> >> Ok >> >>> Still, would be good to understand, what's the difference, why it help on some cases and not help in another. >> >> I don't know, it's like if the analyzer was lazy for this particular >> case, although there is nothing much different from other usages. >> >> If I replace: >> for (... *var2 = (void *)true; var2; >> with: >> for (... *var2 = (void *)true; var2 || true; >> >> then it doesn't warn.. > > but it also doesn't work. We want the body to execute exactly once, > not infloop. > > >> >> Interestingly as well, if I change: >> for (... *var2 = (void *)true; var2; var2 = NULL) >> for: >> for (... *var2 = GML_OBJ_(); var2; var2 = NULL) >> >> GML_OBJ_() simply being &(GraphLockable) { }), an empty compound >> literal, then it doesn't work, in all usages. > > So the compiler is not figuring out that the compound literal is > sufficient for an unconditional one time through the for loop body. > > What's worse, different compiler versions will change behavior over > time. Making the code ugly to pacify a particular compiler, when that > compiler might improve in the future, is a form of chasing windmills. > >> >> All in all, I am not sure the trick of using var2 is really reliable either. > > And that's my biggest argument for not making the macro not more > complex than it already is. > All sounds reasonable, I'm not sure now. I still don't like an idea to satisfy compiler false-positive warnings by extra initializations.. Interesting that older versions do have unitialized-use warnings, but don't fail here (or nobody check?). Is it necessary to fix them at all? Older versions of compiler don't produce these warnings? Is it possible that some optimizations in new GCC version somehow breaks our WITH_ hack, so that it really lead to uninitialized behavior? And we just mask real problem with these patches? Wouldn't it more correct to just drop WITH_ hack, and move to a bit uglier but more gcc-native and fair { QEMU_LOCK_GUARD(lock); ... } ?
diff --git a/block/stream.c b/block/stream.c index 7031eef12b..9076203193 100644 --- a/block/stream.c +++ b/block/stream.c @@ -155,8 +155,8 @@ static void stream_clean(Job *job) static int coroutine_fn stream_run(Job *job, Error **errp) { StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); - BlockDriverState *unfiltered_bs; - int64_t len; + BlockDriverState *unfiltered_bs = NULL; + int64_t len = -1; int64_t offset = 0; int error = 0; int64_t n = 0; /* bytes */ @@ -177,7 +177,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp) for ( ; offset < len; offset += n) { bool copy; - int ret; + int ret = -1; /* Note that even when no rate limit is applied we need to yield * with no pending I/O here so that bdrv_drain_all() returns.