]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
windows: Fix concurrency in __archive_create_child 3109/head
authorTobias Stoeckmann <tobias@stoeckmann.org>
Tue, 2 Jun 2026 19:13:11 +0000 (21:13 +0200)
committerTobias Stoeckmann <tobias@stoeckmann.org>
Tue, 2 Jun 2026 19:33:07 +0000 (21:33 +0200)
Use proper memory barrier while checking for availability of function
WaitForInputIdle.

Since Vista, InitOnceExecuteOnce is a very simple idiom to handle such a
singleton setup. For earlier setups, accept a possible data race and
recover gracefully if two concurrent threads performed the initial
setup.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
libarchive/filter_fork_windows.c

index 9e49c5655f1d4883e5a1390852a18e4b6f03d2f1..a59b81813aaeb37fb23d9a29c3293225a61c88ec 100644 (file)
 /* There are some editions of Windows ("nano server," for example) that
  * do not host user32.dll. If we want to keep running on those editions,
  * we need to delay-load WaitForInputIdle. */
-static void *
-la_GetFunctionUser32(const char *name)
+
+static int
+failing_wait(HANDLE hProcess, DWORD dwMilliseconds) {
+       /* An inability to wait for input idle is
+        * not _good_, but it is not catastrophic. */
+       (void)hProcess; /* UNUSED */
+       (void)dwMilliseconds; /* UNUSED */
+       return WAIT_FAILED;
+}
+
+# if _WIN32_WINNT < _WIN32_WINNT_VISTA
+static int
+la_WaitForInputIdle(HANDLE hProcess, DWORD dwMilliseconds)
 {
-       static HINSTANCE lib;
-       static int set;
-       if (!set) {
-               set = 1;
+       static DWORD (WINAPI * volatile f)(HANDLE, DWORD);
+
+       if (f == NULL) {
+               HINSTANCE lib;
+               void *old;
+               DWORD (WINAPI *tmp)(HANDLE, DWORD);
+
                lib = LoadLibrary(TEXT("user32.dll"));
+               tmp = (lib != NULL) ?
+                   (PVOID)GetProcAddress(lib, "WaitForInputIdle") :
+                   failing_wait;
+               old = InterlockedCompareExchangePointer((volatile PVOID *)&f,
+                   tmp, NULL);
+               if (old != NULL && lib != NULL)
+                       FreeLibrary(lib);
        }
-       if (lib == NULL) {
-               return NULL;
-       }
-       return (void *)GetProcAddress(lib, name);
+
+       return (*f)(hProcess, dwMilliseconds);
+}
+# else
+static BOOL CALLBACK
+load_WaitForInputIdle(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) {
+       HMODULE user32 = LoadLibrary(TEXT("user32.dll"));
+
+       (void)InitOnce; /* UNUSED */
+       (void)Parameter; /* UNUSED */
+
+       *Context = (user32 != NULL) ?
+           (PVOID)GetProcAddress(user32, "WaitForInputIdle") : failing_wait;
+
+       return TRUE;
 }
 
 static int
 la_WaitForInputIdle(HANDLE hProcess, DWORD dwMilliseconds)
 {
        static DWORD (WINAPI *f)(HANDLE, DWORD);
-       static int set;
+       static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
 
-       if (!set) {
-               set = 1;
-               f = la_GetFunctionUser32("WaitForInputIdle");
-       }
+       InitOnceExecuteOnce(&once, load_WaitForInputIdle, NULL, (PVOID)&f);
 
-       if (!f) {
-               /* An inability to wait for input idle is
-                * not _good_, but it is not catastrophic. */
-               return WAIT_FAILED;
-       }
        return (*f)(hProcess, dwMilliseconds);
 }
+# endif
 
 int
 __archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,