winex11.drv: Handle arbitrary length composed input. [take 2]

Kusanagi Kouichi slash at ma.neweb.ne.jp
Sun Apr 20 17:42:20 CDT 2008


If XmbLookupString returns XBufferOverflow, client should recall the function
with a buffer of adequate size. But wine doesn't do so. Therefore an input is
ignored if it is longer than 24 bytes. A number of characters which can be
stored in 24 bytes varies from encoding to encoding.
There is another limit in X11DRV_XIMLookupChars. It takes 64 chars only.
If a string is longer than 64 chars, it is ignored.

Fix bug #9838. http://bugs.winehq.org/show_bug.cgi?id=9838
---
 dlls/winex11.drv/keyboard.c |  114 +++++++++++++++++++++++++++++++------------
 dlls/winex11.drv/xim.c      |   18 +++++--
 2 files changed, 95 insertions(+), 37 deletions(-)

diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c
index 5d48ce7..fd5574d 100644
--- a/dlls/winex11.drv/keyboard.c
+++ b/dlls/winex11.drv/keyboard.c
@@ -1311,6 +1311,74 @@ static void update_lock_state(BYTE vkey, WORD scan, DWORD time)
     X11DRV_send_keyboard_input( vkey, scan, flags ^ KEYEVENTF_KEYUP, time, 0, 0 );
 }
 
+static int lookup_string(XIC xic, XKeyEvent *event, char **str,
+                         KeySym *sym, Status *status)
+{
+    char *buf;
+    int len;
+
+    /* Clients should pass only KeyPress events to XmbLookupString */
+    if (xic && event->type == KeyPress)
+    {
+        Status st;
+
+        buf = NULL;
+        wine_tsx11_lock();
+        len = XmbLookupString(xic, event, NULL, 0, sym, &st);
+        if (st == XBufferOverflow)
+        {
+            if (len > 0)
+            {
+                buf = HeapAlloc(GetProcessHeap(), 0, len);
+                if (!buf)
+                {
+                    len = 0;
+                    *status = XLookupChars;
+                }
+                else
+                    XmbLookupString(xic, event, buf, len, sym, status);
+            }
+            else /* Xlib bug */
+            {
+                len = 16;
+                while (1)
+                {
+                    buf = HeapAlloc(GetProcessHeap(), 0, len);
+                    if (!buf)
+                    {
+                        len = 0;
+                        *status = XLookupChars;
+                        break;
+                    }
+
+                    len = XmbLookupString(xic, event, buf, len, sym, &st);
+                    if (st != XBufferOverflow)
+                        break;
+
+                    HeapFree(GetProcessHeap(), 0, buf);
+                    len *= 4;
+                }
+            }
+        }
+        wine_tsx11_unlock();
+    }
+    else
+    {
+        len = 24;
+        buf = HeapAlloc(GetProcessHeap(), 0, len);
+        if (!buf)
+            len = 0;
+
+        wine_tsx11_lock();
+        len = XLookupString(event, buf, len, sym, NULL);
+        wine_tsx11_unlock();
+        *status = 0;
+    }
+    *str = buf;
+    TRACE_(key)("nbyte = %d, status 0x%x\n", len, *status);
+    return len;
+}
+
 /***********************************************************************
  *           X11DRV_KeyEvent
  *
@@ -1319,7 +1387,7 @@ static void update_lock_state(BYTE vkey, WORD scan, DWORD time)
 void X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
 {
     XKeyEvent *event = &xev->xkey;
-    char Str[24];
+    char *Str;
     KeySym keysym = 0;
     WORD vkey = 0, bScan;
     DWORD dwFlags;
@@ -1331,22 +1399,14 @@ void X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
     TRACE_(key)("type %d, window %lx, state 0x%04x, keycode 0x%04x\n",
 		event->type, event->window, event->state, event->keycode);
 
-    wine_tsx11_lock();
-    /* Clients should pass only KeyPress events to XmbLookupString */
-    if (xic && event->type == KeyPress)
-        ascii_chars = XmbLookupString(xic, event, Str, sizeof(Str), &keysym, &status);
-    else
-        ascii_chars = XLookupString(event, Str, sizeof(Str), &keysym, NULL);
-    wine_tsx11_unlock();
-
-    TRACE_(key)("nbyte = %d, status 0x%x\n", ascii_chars, status);
-
-    if (status == XBufferOverflow)
-        ERR("Buffer Overflow need %i!\n",ascii_chars);
-
+    ascii_chars = lookup_string(xic, event, &Str, &keysym, &status);
     if (status == XLookupChars)
     {
-        X11DRV_XIMLookupChars( Str, ascii_chars );
+        if (Str)
+        {
+            X11DRV_XIMLookupChars( Str, ascii_chars );
+            HeapFree(GetProcessHeap(), 0, Str);
+        }
         return;
     }
 
@@ -1374,6 +1434,8 @@ void X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
                     (event->type == KeyPress) ? "KeyPress" : "KeyRelease",
                     keysym, ksname, ascii_chars, debugstr_an(Str, ascii_chars));
     }
+    if (Str)
+        HeapFree(GetProcessHeap(), 0, Str);
 
     wine_tsx11_lock();
     vkey = EVENT_event_to_vkey(xic,event);
@@ -2347,10 +2409,10 @@ INT X11DRV_ToUnicodeEx(UINT virtKey, UINT scanCode, LPBYTE lpKeyState,
     KeySym keysym = 0;
     INT ret;
     int keyc;
-    char lpChar[10];
+    char *lpChar;
     HWND focus;
     XIC xic;
-    Status status = 0;
+    Status status;
 
     if (scanCode & 0x8000)
     {
@@ -2431,10 +2493,10 @@ INT X11DRV_ToUnicodeEx(UINT virtKey, UINT scanCode, LPBYTE lpKeyState,
     if (virtKey==VK_SEPARATOR)
         e.keycode = XKeysymToKeycode(e.display, XK_KP_Separator);
 
+    wine_tsx11_unlock();
     if (!e.keycode && virtKey != VK_NONAME)
       {
 	WARN("Unknown virtual key %X !!!\n", virtKey);
-        wine_tsx11_unlock();
 	return 0;
       }
     else TRACE("Found keycode %d (0x%2X)\n",e.keycode,e.keycode);
@@ -2442,19 +2504,7 @@ INT X11DRV_ToUnicodeEx(UINT virtKey, UINT scanCode, LPBYTE lpKeyState,
     TRACE_(key)("type %d, window %lx, state 0x%04x, keycode 0x%04x\n",
 		e.type, e.window, e.state, e.keycode);
 
-    /* Clients should pass only KeyPress events to XmbLookupString,
-     * e.type was set to KeyPress above.
-     */
-    if (xic)
-        ret = XmbLookupString(xic, &e, lpChar, sizeof(lpChar), &keysym, &status);
-    else
-        ret = XLookupString(&e, lpChar, sizeof(lpChar), &keysym, NULL);
-    wine_tsx11_unlock();
-
-    TRACE_(key)("nbyte = %d, status 0x%x\n", ret, status);
-
-    if (status == XBufferOverflow)
-        ERR("Buffer Overflow need %d!\n", ret);
+    ret = lookup_string(xic, &e, &lpChar, &keysym, &status);
 
     if (TRACE_ON(key))
     {
@@ -2583,6 +2633,8 @@ INT X11DRV_ToUnicodeEx(UINT virtKey, UINT scanCode, LPBYTE lpKeyState,
     }
 
 found:
+    if (lpChar)
+        HeapFree(GetProcessHeap(), 0, lpChar);
     TRACE_(key)("ToUnicode about to return %d with char %x %s\n",
 		ret, (ret && bufW) ? bufW[0] : 0, bufW ? "" : "(no buffer)");
     return ret;
diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c
index 265f9c8..3bc2564 100644
--- a/dlls/winex11.drv/xim.c
+++ b/dlls/winex11.drv/xim.c
@@ -178,15 +178,21 @@ static BOOL X11DRV_ImmSetInternalString(DWORD dwIndex, DWORD dwOffset,
 void X11DRV_XIMLookupChars( const char *str, DWORD count )
 {
     DWORD dwOutput;
-    WCHAR wcOutput[64];
-    HWND focus;
+    LPWSTR wcOutput;
 
-    dwOutput = MultiByteToWideChar(CP_UNIXCP, 0, str, count, wcOutput, sizeof(wcOutput)/sizeof(WCHAR));
+    dwOutput = MultiByteToWideChar(CP_UNIXCP, 0, str, count, NULL, 0);
+    wcOutput = HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR) * dwOutput);
+    if (wcOutput)
+    {
+        HWND focus;
 
-    if ((focus = GetFocus()))
-        IME_UpdateAssociation(focus);
+        if ((focus = GetFocus()))
+            IME_UpdateAssociation(focus);
 
-    X11DRV_ImmSetInternalString(GCS_RESULTSTR,0,0,wcOutput,dwOutput);
+        MultiByteToWideChar(CP_UNIXCP, 0, str, count, wcOutput, dwOutput);
+        X11DRV_ImmSetInternalString(GCS_RESULTSTR, 0, 0, wcOutput, dwOutput);
+        HeapFree(GetProcessHeap(), 0, wcOutput);
+    }
 }
 
 static void X11DRV_ImmSetOpenStatus(BOOL fOpen)
-- 
1.5.5


-- 
Kusanagi Kouichi



More information about the wine-patches mailing list