Skip to contentSkip to conversation

migration: don't use uninitialized variables

PATCH Superseded Pawit Pornkitprasan 6 participants 13 messages Jul 19, 2013 – Jul 30, 2013

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 migration: don't use uninitialized variables

The qmp_migrate method uses the 'blk' and 'inc' parameter without checking if they're valid or not (they may be uninitialized if command is received via QMP)

---
 migration.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
migration.c +2 -2
index 9f5a423..f3d1ff7 100644
@@ -385,8 +385,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
     MigrationParams params;
     const char *p;
 
-    params.blk = blk;
-    params.shared = inc;
+    params.blk = has_blk && blk;
+    params.shared = has_inc && inc;
 
     if (s->state == MIG_STATE_ACTIVE) {
         error_set(errp, QERR_MIGRATION_ACTIVE);
Signature · 1 line
1.8.1.2
Reviewed-by on v1 1/1
#1 lore raw
Quoted 8 lines · Pawit Pornkitprasan

The qmp_migrate method uses the 'blk' and 'inc' parameter without checking if they're valid or not (they may be uninitialized if command is received via QMP)

---
 migration.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

However, wouldn't it be nice if we improved the qapi generator to guarantee a sane default value for optional parameters, even when has_value is false?

Quoted 17 lines
migration.c +2 -2
index 9f5a423..f3d1ff7 100644
@@ -385,8 +385,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
     MigrationParams params;
     const char *p;
 
-    params.blk = blk;
-    params.shared = inc;
+    params.blk = has_blk && blk;
+    params.shared = has_inc && inc;
 
     if (s->state == MIG_STATE_ACTIVE) {
         error_set(errp, QERR_MIGRATION_ACTIVE);
Signature · 2 lines
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#2 lore raw
Quoted 5 lines · Eric Blake

However, wouldn't it be nice if we improved the qapi generator to guarantee a sane default value for optional parameters, even when has_value is false?

I'm not a qemu expert or anything, but wouldn't that destroy the point of has_value in the first place?

--
Pawit

#3 lore raw
Quoted 9 lines · Pawit Pornkitprasan
Quoted 5 lines · Eric Blake

However, wouldn't it be nice if we improved the qapi generator to guarantee a sane default value for optional parameters, even when has_value is false?

I'm not a qemu expert or anything, but wouldn't that destroy the point of has_value in the first place?

There are two uses for has_value. One: on output, the user knows whether the variable has been set to anything sane. Two: on input, the receiver knows if the caller passed an optional value. For output, has_value is always needed. But for input, we could probably simplify a LOT of code if 'value' already had a sane default of 0, without having to first check 'has_value', instead of the current state where 'value' must be treated as uninitialized if 'has_value' is false. Improving the qapi generator to guarantee 0 value of optional parameters that were not specified in JSON should not block your patch (it would be a series by itself), it's just a question of whether it is worth making the change and the resulting simplifications we can make to code that can assume sane default values.

Signature · 2 lines
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#4 lore raw
In reply to Eric Blake
Quoted 10 lines · On Fri, 19 Jul 2013 04:57:51 -0600 Eric Blake <[email protected]> wrote:
Quoted 8 lines · Pawit Pornkitprasan

The qmp_migrate method uses the 'blk' and 'inc' parameter without checking if they're valid or not (they may be uninitialized if command is received via QMP)

---
 migration.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
Quoted 5 lines

However, wouldn't it be nice if we improved the qapi generator to guarantee a sane default value for optional parameters, even when has_value is false?

We could do that for bool and pointers, but this wouldn't help integers and enums. Also, even if we had default values, I guess I'd enforce a common idiom for handling optionals as this is also a good practice for preventing bugs.

#5 lore raw
Quoted 15 lines · Luiz Capitulino
Quoted 12 lines · On Fri, 19 Jul 2013 04:57:51 -0600 Eric Blake <[email protected]> wrote:
Quoted 8 lines · Pawit Pornkitprasan

The qmp_migrate method uses the 'blk' and 'inc' parameter without checking if they're valid or not (they may be uninitialized if command is received via QMP)

---
 migration.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
Quoted 8 lines
Quoted 3 lines

However, wouldn't it be nice if we improved the qapi generator to guarantee a sane default value for optional parameters, even when has_value is false?

We could do that for bool and pointers, but this wouldn't help integers and enums. Also, even if we had default values, I guess I'd enforce a common idiom for handling optionals as this is also a good practice for preventing bugs.

I disagree on pointers.

"have_ptr && ptr->..." is a stupid idiom. The common idiom for safe dereference is "ptr && ptr->...".

"bool have_ptr, FOO *ptr" in a parameter list is unidiomatic C. Idiomatic is just "FOO *ptr".

Conciseness is a virtue.

For non-pointers, there's no special "don't have one" value, so we'd have to declare a default value in the schema to get rid of the have_FOO.

#6 lore raw
Quoted 31 lines · Markus Armbruster
Quoted 15 lines · Luiz Capitulino
Quoted 12 lines · On Fri, 19 Jul 2013 04:57:51 -0600 Eric Blake <[email protected]> wrote:
Quoted 8 lines · Pawit Pornkitprasan

The qmp_migrate method uses the 'blk' and 'inc' parameter without checking if they're valid or not (they may be uninitialized if command is received via QMP)

---
 migration.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
Quoted 8 lines
Quoted 3 lines

However, wouldn't it be nice if we improved the qapi generator to guarantee a sane default value for optional parameters, even when has_value is false?

We could do that for bool and pointers, but this wouldn't help integers and enums. Also, even if we had default values, I guess I'd enforce a common idiom for handling optionals as this is also a good practice for preventing bugs.

I disagree on pointers.

"have_ptr && ptr->..." is a stupid idiom. The common idiom for safe dereference is "ptr && ptr->...".

Quoted 8 lines

"bool have_ptr, FOO *ptr" in a parameter list is unidiomatic C. Idiomatic is just "FOO *ptr".

Conciseness is a virtue.

For non-pointers, there's no special "don't have one" value, so we'd have to declare a default value in the schema to get rid of the have_FOO.

The problem is the protocol. There are cases where the absence of a value is different than having a default value for the parameter.

This was part of the discussion way back when this all was first introduced. Since everything was open coded and we had to preserve the semantics, that was the only choice we had.

Regards.

Anthony Liguori

#7 lore raw
Quoted 41 lines · Anthony Liguori
Quoted 31 lines · Markus Armbruster
Quoted 15 lines · Luiz Capitulino
Quoted 12 lines · On Fri, 19 Jul 2013 04:57:51 -0600 Eric Blake <[email protected]> wrote:
Quoted 8 lines · Pawit Pornkitprasan

The qmp_migrate method uses the 'blk' and 'inc' parameter without checking if they're valid or not (they may be uninitialized if command is received via QMP)

---
 migration.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
Quoted 8 lines
Quoted 3 lines

However, wouldn't it be nice if we improved the qapi generator to guarantee a sane default value for optional parameters, even when has_value is false?

We could do that for bool and pointers, but this wouldn't help integers and enums. Also, even if we had default values, I guess I'd enforce a common idiom for handling optionals as this is also a good practice for preventing bugs.

I disagree on pointers.

"have_ptr && ptr->..." is a stupid idiom. The common idiom for safe dereference is "ptr && ptr->...".

Quoted 8 lines

"bool have_ptr, FOO *ptr" in a parameter list is unidiomatic C. Idiomatic is just "FOO *ptr".

Conciseness is a virtue.

For non-pointers, there's no special "don't have one" value, so we'd have to declare a default value in the schema to get rid of the have_FOO.

Quoted 6 lines

The problem is the protocol. There are cases where the absence of a value is different than having a default value for the parameter.

This was part of the discussion way back when this all was first introduced. Since everything was open coded and we had to preserve the semantics, that was the only choice we had.

Yes, there is a difference between an optional parameter with a default value and one without.

If you got a value QMP clients cannot send, the difference can be
eliminated: just pass this value to stand for "nothing". This is the case for pointers: just pass NULL. Like everybody else does when passing "nothing".

When "nothing" is to be interpreted just like a context-independent default value, then the difference doesn't matter. The client doesn't care, and the function handling the message becomes simpler.

Only when "nothing" can mean different things do you really need both have_FOO and FOO parameters.

I can't see why we couldn't introduce default values into the schema if we wanted, and use them to simplify generated code.

For pointers, we don't even need schema extensions to simplify away the silly have_FOOs.

#8 lore raw
Quoted 14 lines · Markus Armbruster
Quoted 3 lines · Anthony Liguori

This was part of the discussion way back when this all was first introduced. Since everything was open coded and we had to preserve the semantics, that was the only choice we had.

Yes, there is a difference between an optional parameter with a default value and one without.

If you got a value QMP clients cannot send, the difference can be
eliminated: just pass this value to stand for "nothing". This is the case for pointers: just pass NULL. Like everybody else does when passing "nothing".

Quoted 6 lines

When "nothing" is to be interpreted just like a context-independent default value, then the difference doesn't matter. The client doesn't care, and the function handling the message becomes simpler.

Only when "nothing" can mean different things do you really need both have_FOO and FOO parameters.

Right, and I'm saying that we have cases like this (especially with bools). Unfortunately, there are places in the protocol that have tristate values for bool parameters and the behavior is distinctly different for false, true, and not-present.

So yes, we could special case those bits and use sentinel values for everything else. Just realize that we need to handle the above case.

Quoted 5 lines

I can't see why we couldn't introduce default values into the schema if we wanted, and use them to simplify generated code.

For pointers, we don't even need schema extensions to simplify away the silly have_FOOs.

Except you have to carefully audit all users to make sure of that.

Regards,

Anthony Liguori

#9 lore raw
Quoted 19 lines · Anthony Liguori
Quoted 18 lines · Markus Armbruster
Quoted 3 lines · Anthony Liguori

This was part of the discussion way back when this all was first introduced. Since everything was open coded and we had to preserve the semantics, that was the only choice we had.

Yes, there is a difference between an optional parameter with a default value and one without.

If you got a value QMP clients cannot send, the difference can be
eliminated: just pass this value to stand for "nothing". This is the case for pointers: just pass NULL. Like everybody else does when passing "nothing".

When "nothing" is to be interpreted just like a context-independent default value, then the difference doesn't matter. The client doesn't care, and the function handling the message becomes simpler.

Quoted 5 lines
Quoted 2 lines

Only when "nothing" can mean different things do you really need both have_FOO and FOO parameters.

Right, and I'm saying that we have cases like this (especially with bools). Unfortunately, there are places in the protocol that have

No argument there.

Quoted 5 lines

tristate values for bool parameters and the behavior is distinctly different for false, true, and not-present.

So yes, we could special case those bits and use sentinel values for everything else. Just realize that we need to handle the above case.

Yes, we do, and it's understandable that only the most general case got implemented.

Quoted 7 lines
Quoted 5 lines

I can't see why we couldn't introduce default values into the schema if we wanted, and use them to simplify generated code.

For pointers, we don't even need schema extensions to simplify away the silly have_FOOs.

Except you have to carefully audit all users to make sure of that.

For pointers, the audit should be trivial: change generator to omit the have_FOO, compile, fix up the compile errors (drop have_FOO arguments, replace have_FOO tests be FOO tests), done.

Reviewed-by on v1 1/1
#10 lore raw
In reply to Pawit Pornkitprasan
Quoted 15 lines · Pawit Pornkitprasan

The qmp_migrate method uses the 'blk' and 'inc' parameter without checking if they're valid or not (they may be uninitialized if command is received via QMP)

---
 migration.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
migration.c +0 -0
index 9f5a423..f3d1ff7 100644

migration.c void qmp_migrate(const char *uri, bool has_blk, bool blk, · line 392

Quoted 10 lines
migration.c +2 -2
@@ -385,8 +385,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
     MigrationParams params;
     const char *p;
 
-    params.blk = blk;
-    params.shared = inc;
+    params.blk = has_blk && blk;
+    params.shared = has_inc && inc;
 
     if (s->state == MIG_STATE_ACTIVE) {
         error_set(errp, QERR_MIGRATION_ACTIVE);
#11 lore raw
In reply to Pawit Pornkitprasan
Quoted 14 lines · On Fri, 19 Jul 2013 11:36:41 +0900 Pawit Pornkitprasan <[email protected]>…

The qmp_migrate method uses the 'blk' and 'inc' parameter without checking if they're valid or not (they may be uninitialized if command is received via QMP)

---
 migration.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
migration.c +0 -0
index 9f5a423..f3d1ff7 100644

migration.c void qmp_migrate(const char *uri, bool has_blk, bool blk, · line 389

Quoted 7 lines
migration.c +2 -2
@@ -385,8 +385,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
     MigrationParams params;
     const char *p;
 
-    params.blk = blk;
-    params.shared = inc;
+    params.blk = has_blk && blk;
+    params.shared = has_inc && inc;

This doesn't apply anymore, can you rebase and resend?

By grepping around I can see that there are several instances of this bug in other commands. I'm surprised we never got a single bug report about this... Would you mind to fix all the instances?

Quoted 3 lines
migration.c +0 -0
     if (s->state == MIG_STATE_ACTIVE) {
         error_set(errp, QERR_MIGRATION_ACTIVE);
#12 lore raw
Quoted 3 lines · Luiz Capitulino

This doesn't apply anymore, can you rebase and resend?

Sure.

Quoted 4 lines

By grepping around I can see that there are several instances of this bug in other commands. I'm surprised we never got a single bug report about this... Would you mind to fix all the instances?

It's a bug I've found during my internship. As my internship is ending, I won't be able to fix and test the other instances. Sorry about that.