diff mbox

[v11,03/15] qapi: Forbid 'any' inside an alternate

Message ID 1455778109-6278-4-git-send-email-eblake@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Eric Blake Feb. 18, 2016, 6:48 a.m. UTC
The whole point of an alternate is to allow some type-safety while
still accepting more than one JSON type.  Meanwhile, the 'any'
type exists to bypass type-safety altogether.  The two are
incompatible: you can't accept every type, and still tell which
branch of the alternate to use for the parse; fix this to give a
sane error instead of a Python stack trace.

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v11: new patch
---
 scripts/qapi.py                      | 5 ++++-
 tests/Makefile                       | 1 +
 tests/qapi-schema/alternate-any.err  | 1 +
 tests/qapi-schema/alternate-any.exit | 1 +
 tests/qapi-schema/alternate-any.json | 4 ++++
 tests/qapi-schema/alternate-any.out  | 0
 6 files changed, 11 insertions(+), 1 deletion(-)
 create mode 100644 tests/qapi-schema/alternate-any.err
 create mode 100644 tests/qapi-schema/alternate-any.exit
 create mode 100644 tests/qapi-schema/alternate-any.json
 create mode 100644 tests/qapi-schema/alternate-any.out

diff --git a/tests/qapi-schema/alternate-any.out b/tests/qapi-schema/alternate-any.out
new file mode 100644
index 0000000..e69de29

Comments

Markus Armbruster Feb. 18, 2016, 12:05 p.m. UTC | #1
Eric Blake <eblake@redhat.com> writes:

> The whole point of an alternate is to allow some type-safety while
> still accepting more than one JSON type.  Meanwhile, the 'any'
> type exists to bypass type-safety altogether.  The two are
> incompatible: you can't accept every type, and still tell which
> branch of the alternate to use for the parse; fix this to give a
> sane error instead of a Python stack trace.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v11: new patch
> ---
>  scripts/qapi.py                      | 5 ++++-
>  tests/Makefile                       | 1 +
>  tests/qapi-schema/alternate-any.err  | 1 +
>  tests/qapi-schema/alternate-any.exit | 1 +
>  tests/qapi-schema/alternate-any.json | 4 ++++
>  tests/qapi-schema/alternate-any.out  | 0
>  6 files changed, 11 insertions(+), 1 deletion(-)
>  create mode 100644 tests/qapi-schema/alternate-any.err
>  create mode 100644 tests/qapi-schema/alternate-any.exit
>  create mode 100644 tests/qapi-schema/alternate-any.json
>  create mode 100644 tests/qapi-schema/alternate-any.out
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index f97236f..17bf633 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -629,7 +629,10 @@ def check_alternate(expr, expr_info):
>                     value,
>                     allow_metas=['built-in', 'union', 'struct', 'enum'])
>          qtype = find_alternate_member_qtype(value)
> -        assert qtype
> +        if not qtype:
> +            raise QAPIExprError(expr_info,
> +                                "Alternate '%s' member '%s' cannot use "
> +                                "type '%s'" % (name, key, value))
>          if qtype in types_seen:
>              raise QAPIExprError(expr_info,
>                                  "Alternate '%s' member '%s' can't "

Interestingly, find_alternate_member_qtype(T) can return None in two
ways: when builtin_types[T] is None (only for T == 'any'), and when T is
neither built-in, struct, enum or union (it must be alternate then).

This leads to the question whether this patch catches exactly 'any', as
the commit message claims, or alternate as well.

> diff --git a/tests/Makefile b/tests/Makefile
> index c1c605f..7c66d16 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -241,6 +241,7 @@ check-qtest-xtensaeb-y = $(check-qtest-xtensa-y)
>
>  check-qtest-generic-y += tests/qom-test$(EXESUF)
>
> +qapi-schema += alternate-any.json
>  qapi-schema += alternate-array.json
>  qapi-schema += alternate-base.json
>  qapi-schema += alternate-clash.json
> diff --git a/tests/qapi-schema/alternate-any.err b/tests/qapi-schema/alternate-any.err
> new file mode 100644
> index 0000000..aaa0154
> --- /dev/null
> +++ b/tests/qapi-schema/alternate-any.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/alternate-any.json:2: Alternate 'Alt' member 'one' cannot use type 'any'
> diff --git a/tests/qapi-schema/alternate-any.exit b/tests/qapi-schema/alternate-any.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/alternate-any.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/alternate-any.json b/tests/qapi-schema/alternate-any.json
> new file mode 100644
> index 0000000..e47a73a
> --- /dev/null
> +++ b/tests/qapi-schema/alternate-any.json
> @@ -0,0 +1,4 @@
> +# we do not allow the 'any' type as an alternate branch
> +{ 'alternate': 'Alt',
> +  'data': { 'one': 'any',
> +            'two': 'int' } }
> diff --git a/tests/qapi-schema/alternate-any.out b/tests/qapi-schema/alternate-any.out
> new file mode 100644
> index 0000000..e69de29

Could use a test for alternate member of alternate type.
Eric Blake Feb. 18, 2016, 2:40 p.m. UTC | #2
On 02/18/2016 05:05 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> The whole point of an alternate is to allow some type-safety while
>> still accepting more than one JSON type.  Meanwhile, the 'any'
>> type exists to bypass type-safety altogether.  The two are
>> incompatible: you can't accept every type, and still tell which
>> branch of the alternate to use for the parse; fix this to give a
>> sane error instead of a Python stack trace.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>

> 
> Interestingly, find_alternate_member_qtype(T) can return None in two
> ways: when builtin_types[T] is None (only for T == 'any'), and when T is
> neither built-in, struct, enum or union (it must be alternate then).
> 
> This leads to the question whether this patch catches exactly 'any', as
> the commit message claims, or alternate as well.
> 

>> 
>> @@ -629,7 +629,10 @@ def check_alternate(expr, expr_info):
>>                     value,
>>                     allow_metas=['built-in', 'union', 'struct', 'enum'])
>>          qtype = find_alternate_member_qtype(value)
> 

> 
> Could use a test for alternate member of alternate type.

One step ahead of you: commit 3d0c4829 added the test
alternate-nested.json, and commits 44bd1276 and dd883c6f fixed the
parser to reject it (first by a hard-coded check, then via allow_metas[]
excluding alternates).  'any' is the only value that could sneak
through, because it is a subset of 'built-in' which allow_metas[]
whitelisted.

If you want to squash any of this information into the commit message,
though, I don't mind.
Markus Armbruster Feb. 18, 2016, 5:03 p.m. UTC | #3
Eric Blake <eblake@redhat.com> writes:

> On 02/18/2016 05:05 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> The whole point of an alternate is to allow some type-safety while
>>> still accepting more than one JSON type.  Meanwhile, the 'any'
>>> type exists to bypass type-safety altogether.  The two are
>>> incompatible: you can't accept every type, and still tell which
>>> branch of the alternate to use for the parse; fix this to give a
>>> sane error instead of a Python stack trace.
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>>
>
>> 
>> Interestingly, find_alternate_member_qtype(T) can return None in two
>> ways: when builtin_types[T] is None (only for T == 'any'), and when T is
>> neither built-in, struct, enum or union (it must be alternate then).
>> 
>> This leads to the question whether this patch catches exactly 'any', as
>> the commit message claims, or alternate as well.
>> 
>
>>> 
>>> @@ -629,7 +629,10 @@ def check_alternate(expr, expr_info):
>>>                     value,
>>>                     allow_metas=['built-in', 'union', 'struct', 'enum'])
>>>          qtype = find_alternate_member_qtype(value)
>> 
>
>> 
>> Could use a test for alternate member of alternate type.
>
> One step ahead of you: commit 3d0c4829 added the test
> alternate-nested.json, and commits 44bd1276 and dd883c6f fixed the
> parser to reject it (first by a hard-coded check, then via allow_metas[]
> excluding alternates).  'any' is the only value that could sneak
> through, because it is a subset of 'built-in' which allow_metas[]
> whitelisted.

Then find_alternate_member_qtype()'s final return None is unreachable,
correct?

> If you want to squash any of this information into the commit message,
> though, I don't mind.

I'll consider it when I apply.
diff mbox

Patch

diff --git a/scripts/qapi.py b/scripts/qapi.py
index f97236f..17bf633 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -629,7 +629,10 @@  def check_alternate(expr, expr_info):
                    value,
                    allow_metas=['built-in', 'union', 'struct', 'enum'])
         qtype = find_alternate_member_qtype(value)
-        assert qtype
+        if not qtype:
+            raise QAPIExprError(expr_info,
+                                "Alternate '%s' member '%s' cannot use "
+                                "type '%s'" % (name, key, value))
         if qtype in types_seen:
             raise QAPIExprError(expr_info,
                                 "Alternate '%s' member '%s' can't "
diff --git a/tests/Makefile b/tests/Makefile
index c1c605f..7c66d16 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -241,6 +241,7 @@  check-qtest-xtensaeb-y = $(check-qtest-xtensa-y)

 check-qtest-generic-y += tests/qom-test$(EXESUF)

+qapi-schema += alternate-any.json
 qapi-schema += alternate-array.json
 qapi-schema += alternate-base.json
 qapi-schema += alternate-clash.json
diff --git a/tests/qapi-schema/alternate-any.err b/tests/qapi-schema/alternate-any.err
new file mode 100644
index 0000000..aaa0154
--- /dev/null
+++ b/tests/qapi-schema/alternate-any.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/alternate-any.json:2: Alternate 'Alt' member 'one' cannot use type 'any'
diff --git a/tests/qapi-schema/alternate-any.exit b/tests/qapi-schema/alternate-any.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/alternate-any.exit
@@ -0,0 +1 @@ 
+1
diff --git a/tests/qapi-schema/alternate-any.json b/tests/qapi-schema/alternate-any.json
new file mode 100644
index 0000000..e47a73a
--- /dev/null
+++ b/tests/qapi-schema/alternate-any.json
@@ -0,0 +1,4 @@ 
+# we do not allow the 'any' type as an alternate branch
+{ 'alternate': 'Alt',
+  'data': { 'one': 'any',
+            'two': 'int' } }