[Libreoffice-commits] core.git: Branch 'libreoffice-5-4' - desktop/win32

Michael Stahl mstahl at redhat.com
Mon Sep 4 07:25:54 UTC 2017


 desktop/win32/source/guiloader/genericloader.cxx   |    2 
 desktop/win32/source/loader.cxx                    |   53 +++++++++++++++++----
 desktop/win32/source/loader.hxx                    |    4 +
 desktop/win32/source/officeloader/officeloader.cxx |    2 
 4 files changed, 50 insertions(+), 11 deletions(-)

New commits:
commit e510fbc21f6dec877cda04e17f1433f09fa00066
Author: Michael Stahl <mstahl at redhat.com>
Date:   Fri Sep 1 23:12:05 2017 +0200

    tdf#109241 desktop: Win32: prepend "program" dir to $PATH
    
    The problem is that python modules (*.pyd) find DLLs in the wrong
    places.
    
    This is because sal_detail_initialize() calls SetDllDirectoryW(""),
    which removes (sometimes?) the "current directory" from the DLL
    search order, which is deliberately initialized to the "program"
    dir by CreateProcess() calls in officewrapper.cxx.
    
    Loading DLLs still works for LO's own DLLs since they are all
    in the "program" directory, which is the same directory where
    all the executables are, so it is searched first.
    
    But CPython loads its modules with LOAD_WITH_ALTERED_SEARCH_PATH,
    which doesn't search the directory of the executable but
    the directory of the immediately loaded DLL i.e. the *.pyd file
    instead, i.e. python-core-X.Y.Z/lib.
    
    It would be possible to call SetDllDirectory(".../program")
    instead but probably that would require patching python
    since it needs to be done in the real exectuable, not in
    the wrapper executable.
    
    So overwrite the $PATH again (like was done in the days of
    the office of the holy trinity) in the officewrapper.cxx and
    genericloader.cxx to prepend "program" and get priority
    over the rest of $PATH.
    
    This still doesn't protect against C:/Windows/System32/LIBEAY32.DLL
    since that has higher priority than $PATH but hopefully nobody
    is *that* stupid.
    
    This patch fixes soffice.exe, swriter.exe etc., and unopkg.exe.
    
    The python.exe wrapper already prepends "program" to $PATH.
    
    Change-Id: If03f07eba9a2c7fc6cf44f82f639b5d0b4c62e20
    (cherry picked from commit 9990e98d67bf14003cde8f0138d2dcfa804406ac)
    Reviewed-on: https://gerrit.libreoffice.org/41816
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Noel Grandin <noel.grandin at collabora.co.uk>
    Reviewed-by: Stephan Bergmann <sbergman at redhat.com>

diff --git a/desktop/win32/source/guiloader/genericloader.cxx b/desktop/win32/source/guiloader/genericloader.cxx
index cdad10687b0e..db66ee39445b 100644
--- a/desktop/win32/source/guiloader/genericloader.cxx
+++ b/desktop/win32/source/guiloader/genericloader.cxx
@@ -47,7 +47,7 @@ static int GenericMain()
     TCHAR               szIniDirectory[MAX_PATH];
     STARTUPINFO         aStartupInfo;
 
-    desktop_win32::getPaths(szTargetFileName, szIniDirectory);
+    desktop_win32::extendLoaderEnvironment(szTargetFileName, szIniDirectory);
 
     ZeroMemory( &aStartupInfo, sizeof(aStartupInfo) );
     aStartupInfo.cb = sizeof(aStartupInfo);
diff --git a/desktop/win32/source/loader.cxx b/desktop/win32/source/loader.cxx
index 948d370987b5..9cb133d1d573 100644
--- a/desktop/win32/source/loader.cxx
+++ b/desktop/win32/source/loader.cxx
@@ -33,17 +33,28 @@
 
 #include "loader.hxx"
 
+#include <cassert>
+
+namespace {
+
+void fail()
+{
+    LPWSTR buf = nullptr;
+    FormatMessageW(
+        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr,
+        GetLastError(), 0, reinterpret_cast< LPWSTR >(&buf), 0, nullptr);
+    MessageBoxW(nullptr, buf, nullptr, MB_OK | MB_ICONERROR);
+    LocalFree(buf);
+    TerminateProcess(GetCurrentProcess(), 255);
+}
+
+}
+
 namespace desktop_win32 {
 
-void getPaths(WCHAR * binPath, WCHAR * iniDirectory) {
+void extendLoaderEnvironment(WCHAR * binPath, WCHAR * iniDirectory) {
     if (!GetModuleFileNameW(nullptr, iniDirectory, MAX_PATH)) {
-        LPWSTR buf = nullptr;
-        FormatMessageW(
-            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr,
-            GetLastError(), 0, reinterpret_cast< LPWSTR >(&buf), 0, nullptr);
-        MessageBoxW(nullptr, buf, nullptr, MB_OK | MB_ICONERROR);
-        LocalFree(buf);
-        TerminateProcess(GetCurrentProcess(), 255);
+        fail();
     }
     WCHAR * iniDirEnd = tools::filename(iniDirectory);
     WCHAR name[MAX_PATH + MY_LENGTH(L".bin")];
@@ -65,6 +76,32 @@ void getPaths(WCHAR * binPath, WCHAR * iniDirectory) {
     nameEnd[-1] = 'n';
     tools::buildPath(binPath, iniDirectory, iniDirEnd, name, nameEnd - name);
     *iniDirEnd = L'\0';
+    std::size_t const maxEnv = 32767;
+    WCHAR env[maxEnv];
+    DWORD n = GetEnvironmentVariableW(L"PATH", env, maxEnv);
+    if ((n >= maxEnv || n == 0) && GetLastError() != ERROR_ENVVAR_NOT_FOUND) {
+        fail();
+    }
+    // must be first in PATH to override other entries
+    assert(*(iniDirEnd - 1) == L'\\'); // hence -1 below
+    if (wcsncmp(env, iniDirectory, iniDirEnd - iniDirectory - 1) != 0
+        || env[iniDirEnd - iniDirectory - 1] != L';')
+    {
+        WCHAR pad[MAX_PATH + maxEnv];
+            // hopefully std::size_t is large enough to not overflow
+        WCHAR * p = commandLineAppend(pad, iniDirectory, iniDirEnd - iniDirectory - 1);
+        if (n != 0) {
+            *p++ = L';';
+            for (DWORD i = 0; i <= n; ++i) {
+                *p++ = env[i];
+            }
+        } else {
+            *p++ = L'\0';
+        }
+        if (!SetEnvironmentVariableW(L"PATH", pad)) {
+            fail();
+        }
+    }
 }
 
 }
diff --git a/desktop/win32/source/loader.hxx b/desktop/win32/source/loader.hxx
index e8a987650036..365afa637ffe 100644
--- a/desktop/win32/source/loader.hxx
+++ b/desktop/win32/source/loader.hxx
@@ -68,6 +68,8 @@ inline WCHAR * commandLineAppendEncoded(WCHAR * buffer, WCHAR const * text) {
     return buffer;
 }
 
+// Set the PATH environment variable in the current (loader) process, so that a
+// following CreateProcess has the necessary environment:
 // @param binPath
 // Must point to an array of size at least MAX_PATH.  Is filled with the null
 // terminated full path to the "bin" file corresponding to the current
@@ -76,7 +78,7 @@ inline WCHAR * commandLineAppendEncoded(WCHAR * buffer, WCHAR const * text) {
 // Must point to an array of size at least MAX_PATH.  Is filled with the null
 // terminated full directory path (ending in "\") to the "ini" file
 // corresponding to the current executable.
-void getPaths(WCHAR * binPath, WCHAR * iniDirectory);
+void extendLoaderEnvironment(WCHAR * binPath, WCHAR * iniDirectory);
 
 }
 
diff --git a/desktop/win32/source/officeloader/officeloader.cxx b/desktop/win32/source/officeloader/officeloader.cxx
index 935cc7e42bb8..78c4bc1bfcec 100644
--- a/desktop/win32/source/officeloader/officeloader.cxx
+++ b/desktop/win32/source/officeloader/officeloader.cxx
@@ -62,7 +62,7 @@ int WINAPI _tWinMain( HINSTANCE, HINSTANCE, LPTSTR, int )
     TCHAR               szIniDirectory[MAX_PATH];
     STARTUPINFO         aStartupInfo;
 
-    desktop_win32::getPaths(szTargetFileName, szIniDirectory);
+    desktop_win32::extendLoaderEnvironment(szTargetFileName, szIniDirectory);
 
     ZeroMemory( &aStartupInfo, sizeof(aStartupInfo) );
     aStartupInfo.cb = sizeof(aStartupInfo);


More information about the Libreoffice-commits mailing list