summaryrefslogtreecommitdiffstats
path: root/toolkit/xre/nsWindowsWMain.cpp
blob: ea14a59b80bbfbaa17d7569734b8409d9d21fcde (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef nsWindowsWMain_cpp
#define nsWindowsWMain_cpp

// This file is a .cpp file meant to be included in nsBrowserApp.cpp and other
// similar bootstrap code. It converts wide-character windows wmain into UTF-8
// narrow-character strings.

#ifndef XP_WIN
#  error This file only makes sense on Windows.
#endif

#include "mozilla/Char16.h"
#include "nsUTF8Utils.h"
#include "nsWindowsHelpers.h"

#include <windows.h>
#include <versionhelpers.h>

#ifdef __MINGW32__

/* MingW currently does not implement a wide version of the
   startup routines.  Workaround is to implement something like
   it ourselves.  See bug 411826 */

#  include <shellapi.h>

int wmain(int argc, WCHAR** argv);

int main(int argc, char** argv) {
  LPWSTR commandLine = GetCommandLineW();
  int argcw = 0;
  LPWSTR* argvw = CommandLineToArgvW(commandLine, &argcw);
  if (!argvw) return 127;

  int result = wmain(argcw, argvw);
  LocalFree(argvw);
  return result;
}
#endif /* __MINGW32__ */

#define main NS_internal_main

#ifndef XRE_WANT_ENVIRON
int main(int argc, char** argv);
#else
int main(int argc, char** argv, char** envp);
#endif

// SanitizeEnvironmentVariables()
//
// Mitigate CVE-2007-6753 (binary planting via unexpanded environment variable
// references) by forcibly expanding all environment variable references in
// %PATH%.
//
// CVE-2007-6753 is documented to have affected all active mainline Windows
// versions at the time of its announcement (i.e., up to Windows 7). Microsoft
// has never formally stated that later versions of Windows are free of this
// issue; out of an abundance of caution, we continue to mitigate it on Windows
// 8 and beyond.
static void SanitizeEnvironmentVariables() {
  DWORD bufferSize = GetEnvironmentVariableW(L"PATH", nullptr, 0);
  if (bufferSize) {
    wchar_t* originalPath = new wchar_t[bufferSize];
    if (bufferSize - 1 ==
        GetEnvironmentVariableW(L"PATH", originalPath, bufferSize)) {
      bufferSize = ExpandEnvironmentStringsW(originalPath, nullptr, 0);
      // The maximum length of a Windows environment variable and the maximum
      // length of a string returned by ExpandEnvironmentStringsW are both
      // documented to be 32,767 characters. Under some versions of Windows,
      // however, both may be longer, with delayed but deleterious consequences.
      //
      // We therefore cap the size manually, in case the user has a sufficiently
      // problematic %PATH%. (See bug 1753910.) This is unlikely to occur unless
      // there is some form of recursive referencing involved, in which case
      // expansion is no mitigation anyway.
      if (bufferSize && bufferSize < 32768) {
        wchar_t* newPath = new wchar_t[bufferSize];
        if (ExpandEnvironmentStringsW(originalPath, newPath, bufferSize)) {
          SetEnvironmentVariableW(L"PATH", newPath);
        }
        delete[] newPath;
      }
    }
    delete[] originalPath;
  }
}

static char* AllocConvertUTF16toUTF8(char16ptr_t arg) {
  // be generous... UTF16 units can expand up to 3 UTF8 units
  size_t len = wcslen(arg);
  // ConvertUTF16toUTF8 requires +1. Let's do that here, too, lacking
  // knowledge of Windows internals.
  size_t dstLen = len * 3 + 1;
  char* s = new char[dstLen + 1];  // Another +1 for zero terminator
  if (!s) return nullptr;

  int written =
      ::WideCharToMultiByte(CP_UTF8, 0, arg, len, s, dstLen, nullptr, nullptr);
  s[written] = 0;
  return s;
}

static void FreeAllocStrings(int argc, char** argv) {
  while (argc) {
    --argc;
    delete[] argv[argc];
  }

  delete[] argv;
}

int wmain(int argc, WCHAR** argv) {
  // In Windows 7 32-bit, user32.dll must be mapped to the same virtual
  // address in all processes.  Otherwise, win32k's user-mode callback causes
  // crash.  Since we delayload user32.dll, if our code or injected code
  // reserves the user32 address before user32.dll is loaded, it is loaded
  // to a new address and we crash.  To mitigate this problem, we explicitly
  // load user32.dll as early as possible. See bug 1730033 for details.
  if (!IsWindows8OrGreater()) {
    SYSTEM_INFO sysInfo;
    ::GetNativeSystemInfo(&sysInfo);
    if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) {
      LoadLibrarySystem32(L"user32.dll");
    }
  }

  SanitizeEnvironmentVariables();
  SetDllDirectoryW(L"");

  // Only run this code if LauncherProcessWin.h was included beforehand, thus
  // signalling that the hosting process should support launcher mode.
#if defined(mozilla_LauncherProcessWin_h)
  mozilla::Maybe<int> launcherResult =
      mozilla::LauncherMain(argc, argv, sAppData);
  if (launcherResult) {
    return launcherResult.value();
  }
#endif  // defined(mozilla_LauncherProcessWin_h)

  char** argvConverted = new char*[argc + 1];
  if (!argvConverted) return 127;

  for (int i = 0; i < argc; ++i) {
    argvConverted[i] = AllocConvertUTF16toUTF8(argv[i]);
    if (!argvConverted[i]) {
      return 127;
    }
  }
  argvConverted[argc] = nullptr;

  // need to save argvConverted copy for later deletion.
  char** deleteUs = new char*[argc + 1];
  if (!deleteUs) {
    FreeAllocStrings(argc, argvConverted);
    return 127;
  }
  for (int i = 0; i < argc; i++) deleteUs[i] = argvConverted[i];
#ifndef XRE_WANT_ENVIRON
  int result = main(argc, argvConverted);
#else
  // Force creation of the multibyte _environ variable.
  getenv("PATH");
  int result = main(argc, argvConverted, _environ);
#endif

  delete[] argvConverted;
  FreeAllocStrings(argc, deleteUs);

  return result;
}

#endif  // nsWindowsWMain_cpp