From a1a00a3071803a06e9908a1fb9504b79f47eda25 Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Mon, 10 Jul 2023 09:29:03 -0700 Subject: [PATCH] gdbus-codegen: Error on invalid dbus types Introduce slightly modified (dbus specific) versions of variant_is_signature and variant_type_string_scan in order to fail early on bad type strings. --- gio/gdbus-2.0/codegen/dbustypes.py | 64 ++++++++++++++++++++++++++++++ gio/tests/codegen.py | 58 +++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/gio/gdbus-2.0/codegen/dbustypes.py b/gio/gdbus-2.0/codegen/dbustypes.py index ee11fa2ad..e16ea52f9 100644 --- a/gio/gdbus-2.0/codegen/dbustypes.py +++ b/gio/gdbus-2.0/codegen/dbustypes.py @@ -23,6 +23,69 @@ from . import utils from .utils import print_error +# See: variant_type_string_scan_internal() +def variant_type_string_scan(signature: str, depth_limit: int, i=0): + beg_char = signature[i] + i += 1 + if beg_char == "(": + while signature[i] != ")": + if depth_limit == 0: + raise ValueError( + f'Bad signature "{signature}". Too much recursion beginning at {i}.' + ) + i = variant_type_string_scan(signature, depth_limit - 1, i) + i += 1 + elif beg_char == "{": + if depth_limit == 0: + raise ValueError( + f'Bad signature "{signature}". Too much recursion beginning at {i}.' + ) + elif signature[i] not in "bynqihuxtdsog?": + raise ValueError( + f'Bad signature "{signature}". "{signature[i]}" is not a valid type for dictionary keys at position {i}.' + ) + i += 1 + i = variant_type_string_scan(signature, depth_limit - 1, i) + if signature[i] != "}": + raise ValueError( + f'Bad signature "{signature}". Dict must end with "}}" at position {i}.' + ) + i += 1 + elif beg_char == "a": + if depth_limit == 0: + raise ValueError( + f'Bad signature "{signature}". Too much recursion beginning at {i}.' + ) + i = variant_type_string_scan(signature, depth_limit - 1, i) + elif beg_char not in "bynqiuxtdsogvr*?h": + raise ValueError( + f'Bad signature "{signature}". Unexpected value "{beg_char}" at position {i}.' + ) + return i + + +# variant_check_signature() does not perform a strict validation check and +# should not be used in security sensitive contexts. +def variant_check_signature(signature: str): + # See: gvariant-internal.h + G_VARIANT_MAX_RECURSION_DEPTH = 128 + if len(signature) > 255: + print_error("D-Bus maximum signature length of 255 exceeded.") + for s in signature: + if s not in "ybnqiuxthdvasog(){}": + print_error( + f'Bad signature "{signature}". "{s}" is not a valid D-Bus type.' + ) + try: + variant_type_string_scan(signature, G_VARIANT_MAX_RECURSION_DEPTH) + except IndexError: + print_error( + f'Bad signature "{signature}". Error parsing string or brackets not closed.' + ) + except ValueError as e: + print_error(e.args[0]) + + class Annotation: def __init__(self, key, value): self.key = key @@ -88,6 +151,7 @@ class Arg: self.gvalue_set = "g_value_take_variant" self.gclosure_marshaller = "g_cclosure_marshal_VOID__VARIANT" self.array_annotation = "" + variant_check_signature(self.signature) if not utils.lookup_annotation( self.annotations, "org.gtk.GDBus.C.ForceGVariant" diff --git a/gio/tests/codegen.py b/gio/tests/codegen.py index 34e7e95d8..deaa78fff 100644 --- a/gio/tests/codegen.py +++ b/gio/tests/codegen.py @@ -627,6 +627,64 @@ G_END_DECLS "2.64", ) + @unittest.skipIf(on_win32(), "requires /dev/stdout") + def test_dbus_types(self): + bad_types = [ + "{vs}", # Bad dictionary key type + "(ss(s{{sv}s}))", # Bad dictionary key types + "{s", # Unterminated dictionary + "(s{sss})", # Unterminated dictionary + "z", # Bad type + "(ssms)", # Bad type + "(", # Unterminated tuple + "(((ss))", # Unterminated tuple + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaas", # Too much recursion + "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((" + "(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((s))" + "))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))" + "))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))", # Too much recursion + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{sv}" + "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}" + "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", # Too much recursion + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaa{sv})", # Too much recursion + "(ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss" + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss" + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss" + "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssss)", # Too long + ] + for t in bad_types: + interface_xml = f""" + + + + + """ + with self.assertRaises(subprocess.CalledProcessError): + self.runCodegenWithInterface( + interface_xml, "--output", "/dev/stdout", "--body" + ) + good_types = [ + "si{s{b(ybnqiuxtdh)}}{yv}{nv}{dv}", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaas", # 128 Levels of recursion + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaa{sv})", # 128 Levels of recursion + ] + for t in good_types: + interface_xml = f""" + + + + + """ + result = self.runCodegenWithInterface( + interface_xml, "--output", "/dev/stdout", "--body" + ) + self.assertEqual("", result.err) + @unittest.skipIf(on_win32(), "requires /dev/stdout") def test_unix_fd_types_and_annotations(self): """Test an interface with `h` arguments, no annotation, and GLib < 2.64.