Re: [PATCH 06/19] block/stream: fix -Werror=maybe-uninitialized false-positives

2024-04-03 Thread Vladimir Sementsov-Ogievskiy

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

?

--
Best regards,
Vladimir




Re: [PATCH 06/19] block/stream: fix -Werror=maybe-uninitialized false-positives

2024-04-03 Thread Eric Blake
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.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH 06/19] block/stream: fix -Werror=maybe-uninitialized false-positives

2024-04-03 Thread Marc-André Lureau
Hi

On Wed, Apr 3, 2024 at 12:31 PM Vladimir Sementsov-Ogievskiy
 wrote:
>
> On 03.04.24 11:11, Marc-André Lureau wrote:
> > Hi
> >
> > On Tue, Apr 2, 2024 at 11:24 PM Vladimir Sementsov-Ogievskiy
> >  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:

Re: [PATCH 06/19] block/stream: fix -Werror=maybe-uninitialized false-positives

2024-04-03 Thread Vladimir Sementsov-Ogievskiy

On 03.04.24 11:11, Marc-André Lureau wrote:

Hi

On Tue, Apr 2, 2024 at 11:24 PM Vladimir Sementsov-Ogievskiy
 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.


--
Best regards,
Vladimir




Re: [PATCH 06/19] block/stream: fix -Werror=maybe-uninitialized false-positives

2024-04-03 Thread Marc-André Lureau
Hi

On Tue, Apr 2, 2024 at 11:24 PM Vladimir Sementsov-Ogievskiy
 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?





-- 
Marc-André Lureau



Re: [PATCH 06/19] block/stream: fix -Werror=maybe-uninitialized false-positives

2024-04-02 Thread Vladimir Sementsov-Ogievskiy

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)

--
Best regards,
Vladimir




Re: [PATCH 06/19] block/stream: fix -Werror=maybe-uninitialized false-positives

2024-04-02 Thread Eric Blake
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?

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org




Re: [PATCH 06/19] block/stream: fix -Werror=maybe-uninitialized false-positives

2024-04-02 Thread Vladimir Sementsov-Ogievskiy

On 02.04.24 12:12, Marc-André Lureau wrote:

Hi

On Fri, Mar 29, 2024 at 12:35 PM Vladimir Sementsov-Ogievskiy
 wrote:


On 28.03.24 13:20, marcandre.lur...@redhat.com wrote:

From: Marc-André Lureau 

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


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 


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







--
Best regards,
Vladimir




Re: [PATCH 06/19] block/stream: fix -Werror=maybe-uninitialized false-positives

2024-04-02 Thread Marc-André Lureau
Hi

On Fri, Mar 29, 2024 at 12:35 PM Vladimir Sementsov-Ogievskiy
 wrote:
>
> On 28.03.24 13:20, marcandre.lur...@redhat.com wrote:
> > From: Marc-André Lureau 
> >
> > ../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 
>
> 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 
>
> > ---
> >   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
>
>


-- 
Marc-André Lureau



Re: [PATCH 06/19] block/stream: fix -Werror=maybe-uninitialized false-positives

2024-03-29 Thread Vladimir Sementsov-Ogievskiy

On 28.03.24 13:20, marcandre.lur...@redhat.com wrote:

From: Marc-André Lureau 

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


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 


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