[PATCH v5 3/6] winedbg: Escape XML special characters in qXfer reply.

Jinoh Kang jinoh.kang.kr at gmail.com
Sat Nov 20 15:12:22 CST 2021


Loaded image paths that contain XML special characters may break GDB's
XML parser.

Fix this by escaping all dynamic strings that go into the XML replies.

Signed-off-by: Jinoh Kang <jinoh.kang.kr at gmail.com>
---

Notes:
    strcspn() uses lookup tables as well, but as of Wine 6.22
    msvcrt!strcspn allocates 1024 bytes (sizeof(BOOL)*256) of table
    on the stack and populates it on the fly.  It would be slower and
    less cache-friendly than a preallocated, tiny static lookup table.

 programs/winedbg/gdbproxy.c | 63 ++++++++++++++++++++++++++++++++-----
 1 file changed, 56 insertions(+), 7 deletions(-)

diff --git a/programs/winedbg/gdbproxy.c b/programs/winedbg/gdbproxy.c
index a82588fd8b8..2534e2525b0 100644
--- a/programs/winedbg/gdbproxy.c
+++ b/programs/winedbg/gdbproxy.c
@@ -279,6 +279,55 @@ static inline void reply_buffer_append_uinthex(struct reply_buffer* reply, ULONG
     reply_buffer_append(reply, ptr, len * 2);
 }
 
+static const unsigned char xml_special_chars_lookup_table[16] = {
+    /* The characters should be sorted by its value modulo table length. */
+
+    0x00,       /* NUL */
+    0,
+    0x22,       /* ": 0010|0010 */
+    0, 0, 0,
+    0x26,       /* &: 0010|0110 */
+    0x27,       /* ': 0010|0111 */
+    0, 0, 0, 0,
+    0x3C,       /* <: 0011|1100 */
+    0,
+    0x3E,       /* >: 0011|1110 */
+    0
+};
+
+static inline BOOL is_nul_or_xml_special_char(unsigned char val)
+{
+    const size_t length = ARRAY_SIZE(xml_special_chars_lookup_table);
+    return xml_special_chars_lookup_table[val % length] == val;
+}
+
+static void reply_buffer_append_xmlstr(struct reply_buffer* reply, const char* str)
+{
+    const char *ptr = str, *curr;
+
+    for (;;)
+    {
+        curr = ptr;
+
+        while (!is_nul_or_xml_special_char((unsigned char)*ptr))
+            ptr++;
+
+        reply_buffer_append(reply, curr, ptr - curr);
+
+        switch (*ptr++)
+        {
+        case '"': reply_buffer_append_str(reply, """); break;
+        case '&': reply_buffer_append_str(reply, "&"); break;
+        case '\'': reply_buffer_append_str(reply, "'"); break;
+        case '<': reply_buffer_append_str(reply, "<"); break;
+        case '>': reply_buffer_append_str(reply, ">"); break;
+        case '\0':
+        default:
+            return;
+        }
+    }
+}
+
 static unsigned char checksum(const void* data, int len)
 {
     unsigned cksum = 0;
@@ -1618,9 +1667,9 @@ static BOOL CALLBACK packet_query_libraries_cb(PCSTR mod_name, DWORD64 base, PVO
 
     reply_buffer_append_str(reply, "<library name=\"");
     if (strcmp(mod.LoadedImageName, "[vdso].so") == 0)
-        reply_buffer_append_str(reply, "linux-vdso.so.1");
+        reply_buffer_append_xmlstr(reply, "linux-vdso.so.1");
     else if (mod.LoadedImageName[0] == '/')
-        reply_buffer_append_str(reply, mod.LoadedImageName);
+        reply_buffer_append_xmlstr(reply, mod.LoadedImageName);
     else
     {
         UNICODE_STRING nt_name;
@@ -1635,10 +1684,10 @@ static BOOL CALLBACK packet_query_libraries_cb(PCSTR mod_name, DWORD64 base, PVO
             if (IsWow64Process(gdbctx->process->handle, &is_wow64) &&
                 is_wow64 && (tmp = strstr(unix_path, "system32")))
                 memcpy(tmp, "syswow64", 8);
-            reply_buffer_append_str(reply, unix_path);
+            reply_buffer_append_xmlstr(reply, unix_path);
         }
         else
-            reply_buffer_append_str(reply, mod.LoadedImageName);
+            reply_buffer_append_xmlstr(reply, mod.LoadedImageName);
 
         HeapFree(GetProcessHeap(), 0, unix_path);
         RtlFreeUnicodeString(&nt_name);
@@ -1755,8 +1804,8 @@ static void packet_query_target_xml(struct gdb_context* gdbctx, struct backend_c
             feature = cpu->gdb_register_map[i].feature;
 
             reply_buffer_append_str(reply, "<feature name=\"");
-            if (feature_prefix) reply_buffer_append_str(reply, feature_prefix);
-            reply_buffer_append_str(reply, feature);
+            if (feature_prefix) reply_buffer_append_xmlstr(reply, feature_prefix);
+            reply_buffer_append_xmlstr(reply, feature);
             reply_buffer_append_str(reply, "\">");
 
             if (strcmp(feature_prefix, "org.gnu.gdb.i386.") == 0 &&
@@ -1823,7 +1872,7 @@ static void packet_query_target_xml(struct gdb_context* gdbctx, struct backend_c
         if (cpu->gdb_register_map[i].type)
         {
             reply_buffer_append_str(reply, " type=\"");
-            reply_buffer_append_str(reply, cpu->gdb_register_map[i].type);
+            reply_buffer_append_xmlstr(reply, cpu->gdb_register_map[i].type);
             reply_buffer_append_str(reply, "\"");
         }
 
-- 
2.31.1




More information about the wine-devel mailing list