GNOME Bugzilla – Bug 784579
Calling g_dir_open on Missing Directory When Executable File Path Contains Unicode Hangs
Last modified: 2017-07-13 11:39:56 UTC
For my test case, I'm running with the NuGet GLIB package from Visual Studio 2015 on Windows 10. My two cases are running my executable from a directory that contains Unicode (assumed UTF-16 because of Windows) characters, and running from a directory that does not. My target directory path is not related in any way to where my executable is. If the directory exists, program behaves as expected in both cases. If the directory does not exist, we properly get a null directory for the non-Unicode executable path case. In the Unicode executable case, however, the program never finishes execution of g_dir_open.
NOTE: This is being called from inside of GStreamer
Have u tried with a newer glib? 2.36 is a bit old
The version of GStreamer we're running includes GLib 2.46.2 (still kinda old, I know) and we're seeing the issue here as well. I was just using GLib 2.36 for convenience since it was in NuGet. I'll attempt to run with the newest GLib and report back. Just wasn't sure if this was a known issue or a won't fix.
Does it matter which unicode characters are used in the directory name? Can you give an example string (that could be put into a testcase)? Does this depend on which system locale is currently set? What locale are you using?
My test case code is calling g_dir_open("C:\\Users\\<username>\\Desktop\\DNEDir", 0, NULL); from main. Aside from includes, this is all that's required. Note that the path we're looking at does not contain any Unicode characters, all differences in execution come from changing the executable directory name. If the directory exists, there is no problem. In my test project, I've been running out of directory named Release, and Release<testcharacter>. Test character can be anywhere in the directory name. If it's anywhere up to 255, it works fine. But the moment it goes to 256 (Ā) upwards in the Unicode range, it fails. Spaces also appear to work. Currently running in US English locale. I tried running with a Russian locale with Russian characters in the directory name, but no luck.
Can't reproduce with glib 2.38.0 (the oldest version i have at hand; glib from git master also works fine), Windows 10 x86_64, English locale and non-ASCII characters in the path to the executable.
Upon further investigation, I believe I have found the source of the problem by looking through the 2.50.0 source. I managed to get everything to run fine with Russian characters if I set my computer to a Russian locale, but adding other characters that would be outside of the default system codepage causes the issue to occur again. Program flow as I understand it goes like this. We call g_dir_open. If the directory does not exist, we call g_set_error. In localizing the "Error opening directory" line, we call glib_gettext(), which triggers a call to ensure_gettext_initialized. This function causes us to enter a g_once_init_enter block and then attempts to load the locale_dir for glib. Inside of this, we make a call to g_win32_locale_filename_from_utf8. A few calls down the stack (g_locale_from_utf8 -> g_convert -> g_convert_with_iconv), we fail to properly execute and get a length error "Partial character sequence at end of input". This seems to occur based on some Unicode characters that wouldn't be in the UTF8 codepage (U+2569 in my use case) This calls g_set_error_literal and uses the glib_gettext() Macro again, which goes through the above flow until it hits the g_once_init_enter block. Once here again, it waits on the init list to empty. Since this is occurring from the same thread, we are now in deadlock. Hopefully I am properly understanding the code and am making sense with this.
This happens when *glib* dll is in a directory with a weird name, not the program itself. I was able to reproduce it.
Created attachment 355473 [details] [review] W32 - don't use gettext & gcov during gettext init Non-representable characters during UTF16->locale conversion will cause gcov code to return an error, for which it will try to use gettext, so that the error message is localized. If such call is made while gettext is being initialized (there's a g_once_init_enter up the stack), the thread will hang forever. To solve this, use W32 API to do the UTF16->locale conversion and don't use gettext when it returns an error. Also optimize g_win32_locale_filename_from_utf8() a bit, as we need more UTF16 and less UTF8 now.
Review of attachment 355473 [details] [review]: Patch seems reasonable to me
Review of attachment 355473 [details] [review]: Hi, I was not able to get this to reproduce, even after following the items described in the bug--just wondering what the gettext/intl*.dll version used is. But if this fixes the issue (and since it is not that easier to get intl.dll to build under Visual Studio, although it is entirely possible), I think we should go ahead with this, as this patch looks reasonable to me as well. With blessings, thank you, and cheers!
Attachment 355473 [details] pushed into master as commit 75fa8c2afbab4f414d2eb03684d9f807bd690aef For posterity: this bug is reproduced by dumping the test application and the minimum amount of DLLs (iconv, intl, glib, winpthreads) into the same weirdly-named directory. However, debugging it like this is an absolute pain, because gdb can't work with files in that directory either (surprise!). To reproduce it in more debuggable surroundings i had to hack my version of glib and deliberately subvert g_win32_get_package_installation_directory_of_module() implementation to use not the actual directory where glib DLL is located, but a pre-defined UTF-16 string that it reads from a specific disk file. Now, should this be backported? If yes, then how far?
Let's backport it just to the last stable release
Cherry-picked into glib-2-52 mclasen said that there won't be any glib releases for older branches.
Can you also backport the DWORD warning you just fixed?
You mean the GetLastError() %lu fix? It only applies to master, because the code it fixes (QPC-based timers) is only available in master.