[pygobject/gtk-builder-from-string-unicode] overrides: Fix length parameter when passing non-ascii strings to Gtk.Builder.add_from_string/add_ob



commit 068b251def8a4716bbedbcbb41237522a321d251
Author: Christoph Reiter <reiter christoph gmail com>
Date:   Thu Oct 4 17:32:17 2018 +0200

    overrides: Fix length parameter when passing non-ascii strings to 
Gtk.Builder.add_from_string/add_objects_from_string
    
    It was passing the codepoint count instead of the length in bytes which lead to things
    reading past the provided memory and crashes. Also handle included NULL bytes by only
    passing the length up to the NULL byte (also leads to crashes as it's unexpected).
    
    Also use the same code for Gtk.UIManager.add_ui_from_string() which was already passing the
    right count but didn't handle embedded NULL bytes.
    
    Fixes #255

 gi/overrides/Gtk.py         | 15 ++++++++++++---
 tests/test_overrides_gtk.py | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+), 3 deletions(-)
---
diff --git a/gi/overrides/Gtk.py b/gi/overrides/Gtk.py
index 05da18aa..0c2fb2aa 100644
--- a/gi/overrides/Gtk.py
+++ b/gi/overrides/Gtk.py
@@ -412,7 +412,7 @@ if Gtk._version in ("2.0", "3.0"):
             if not isinstance(buffer, string_types):
                 raise TypeError('buffer must be a string')
 
-            length = len(buffer.encode('UTF-8'))
+            length = _get_utf8_length(buffer)
 
             return Gtk.UIManager.add_ui_from_string(self, buffer, length)
 
@@ -462,6 +462,15 @@ MenuItem = override(MenuItem)
 __all__.append('MenuItem')
 
 
+def _get_utf8_length(string):
+    if not isinstance(string, string_types):
+        raise TypeError('must be a string')
+
+    if not isinstance(string, bytes):
+        string = string.encode("utf-8")
+    return len(string.split(b"\x00", 1)[0])
+
+
 class Builder(Gtk.Builder):
     def connect_signals(self, obj_or_map):
         """Connect signals specified by this builder to a name, handler mapping.
@@ -481,7 +490,7 @@ class Builder(Gtk.Builder):
         if not isinstance(buffer, string_types):
             raise TypeError('buffer must be a string')
 
-        length = len(buffer)
+        length = _get_utf8_length(buffer)
 
         return Gtk.Builder.add_from_string(self, buffer, length)
 
@@ -489,7 +498,7 @@ class Builder(Gtk.Builder):
         if not isinstance(buffer, string_types):
             raise TypeError('buffer must be a string')
 
-        length = len(buffer)
+        length = _get_utf8_length(buffer)
 
         return Gtk.Builder.add_objects_from_string(self, buffer, length, object_ids)
 
diff --git a/tests/test_overrides_gtk.py b/tests/test_overrides_gtk.py
index 1e365525..11a96958 100644
--- a/tests/test_overrides_gtk.py
+++ b/tests/test_overrides_gtk.py
@@ -222,6 +222,11 @@ class TestGtk(unittest.TestCase):
         mi = ui.get_widget("/menubær1")
         self.assertEqual(type(mi), Gtk.MenuBar)
 
+    @unittest.skipIf(Gtk_version == "4.0", "not in gtk4")
+    def test_uimanager_nullterm(self):
+        ui = Gtk.UIManager()
+        ui.add_ui_from_string("<ui></ui>\x00\x00\x00")
+
     def test_window(self):
         # standard Window
         w = Gtk.Window()
@@ -822,6 +827,34 @@ class TestBuilder(unittest.TestCase):
                             []),
         }
 
+    def test_add_from_string(self):
+        builder = Gtk.Builder()
+        builder.add_from_string(u"")
+        builder.add_from_string("")
+        builder.add_from_string(u"\x00")
+        builder.add_from_string("\x00")
+
+        def get_example(string):
+            return u"""\
+<interface>
+  <menu id="appmenu">
+    <section>
+      <item>
+        <attribute name="label">%s</attribute>
+      </item>
+    </section>
+  </menu>
+</interface>""" % string
+
+        builder.add_from_string(get_example(u"ä" * 1000))
+
+        builder = Gtk.Builder()
+        builder.add_objects_from_string(u"", [''])
+        builder.add_objects_from_string("", [''])
+        builder.add_objects_from_string(u"\x00", [''])
+        builder.add_objects_from_string("\x00", [''])
+        builder.add_objects_from_string(get_example(u"ä" * 1000), [''])
+
     def test_extract_handler_and_args_object(self):
         class Obj():
             pass


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]