passenger 5.3.1 → 5.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +19 -0
  3. data/build/cxx_tests.rb +3 -1
  4. data/build/support/cxx_dependency_map.rb +120 -27
  5. data/dev/configkit-schemas/index.json +15 -3
  6. data/src/agent/Core/AdminPanelConnector.h +5 -2
  7. data/src/agent/Core/ApplicationPool/Group/StateInspection.cpp +2 -0
  8. data/src/agent/Core/Config.h +2 -1
  9. data/src/agent/Core/Controller/Config.h +6 -1
  10. data/src/agent/Core/Controller/InitRequest.cpp +6 -1
  11. data/src/agent/Core/CoreMain.cpp +26 -60
  12. data/src/agent/Core/SpawningKit/DirectSpawner.h +18 -6
  13. data/src/agent/Core/SpawningKit/ErrorRenderer.h +8 -8
  14. data/src/agent/Core/SpawningKit/Handshake/Perform.h +217 -61
  15. data/src/agent/Core/SpawningKit/Handshake/Prepare.h +57 -8
  16. data/src/agent/Core/SpawningKit/Handshake/Session.h +34 -1
  17. data/src/agent/Core/SpawningKit/Handshake/WorkDir.h +20 -4
  18. data/src/agent/Core/SpawningKit/SmartSpawner.h +90 -27
  19. data/src/agent/ExecHelper/ExecHelperMain.cpp +3 -0
  20. data/src/agent/Shared/ApiAccountUtils.h +2 -2
  21. data/src/agent/SpawnEnvSetupper/SpawnEnvSetupperMain.cpp +14 -4
  22. data/src/agent/Watchdog/Config.h +2 -1
  23. data/src/agent/Watchdog/WatchdogMain.cpp +38 -0
  24. data/src/apache2_module/Hooks.cpp +1 -0
  25. data/src/cxx_supportlib/ConfigKit/IN_PRACTICE.md +1 -1
  26. data/src/cxx_supportlib/ConfigKit/README.md +1 -1
  27. data/src/cxx_supportlib/Constants.h +6 -1
  28. data/src/cxx_supportlib/FileTools/FileManip.cpp +34 -2
  29. data/src/cxx_supportlib/FileTools/FileManip.h +58 -1
  30. data/src/cxx_supportlib/FileTools/PathManip.cpp +3 -2
  31. data/src/cxx_supportlib/FileTools/PathSecurityCheck.cpp +99 -0
  32. data/src/cxx_supportlib/FileTools/PathSecurityCheck.h +69 -0
  33. data/src/cxx_supportlib/Utils.cpp +37 -6
  34. data/src/cxx_supportlib/Utils.h +6 -0
  35. data/src/cxx_supportlib/Utils/AsyncSignalSafeUtils.h +14 -0
  36. data/src/cxx_supportlib/Utils/IOUtils.cpp +10 -18
  37. data/src/cxx_supportlib/Utils/IOUtils.h +10 -9
  38. data/src/cxx_supportlib/Utils/JsonUtils.h +12 -8
  39. data/src/cxx_supportlib/Utils/SystemMetricsCollector.h +4 -4
  40. data/src/cxx_supportlib/Utils/SystemTime.h +1 -1
  41. data/src/cxx_supportlib/WebSocketCommandReverseServer.h +3 -3
  42. data/src/cxx_supportlib/oxt/system_calls.cpp +25 -1
  43. data/src/cxx_supportlib/oxt/system_calls.hpp +3 -1
  44. data/src/helper-scripts/meteor-loader.rb +115 -28
  45. data/src/helper-scripts/rack-preloader.rb +1 -1
  46. data/src/nginx_module/ConfigGeneral/AutoGeneratedDefinitions.c +4 -4
  47. data/src/nginx_module/ConfigGeneral/AutoGeneratedSetterFuncs.c +4 -4
  48. data/src/nginx_module/LocationConfig/AutoGeneratedCreateFunction.c +0 -10
  49. data/src/nginx_module/LocationConfig/AutoGeneratedHeaderSerialization.c +0 -42
  50. data/src/nginx_module/LocationConfig/AutoGeneratedMergeFunction.c +0 -6
  51. data/src/nginx_module/LocationConfig/AutoGeneratedStruct.h +0 -8
  52. data/src/nginx_module/MainConfig/AutoGeneratedCreateFunction.c +10 -0
  53. data/src/nginx_module/MainConfig/AutoGeneratedManifestGeneration.c +22 -0
  54. data/src/nginx_module/MainConfig/AutoGeneratedStruct.h +8 -0
  55. data/src/nginx_module/ngx_http_passenger_module.c +6 -5
  56. data/src/ruby_supportlib/phusion_passenger.rb +1 -1
  57. data/src/ruby_supportlib/phusion_passenger/apache2/config_options.rb +0 -1
  58. data/src/ruby_supportlib/phusion_passenger/common_library.rb +3 -0
  59. data/src/ruby_supportlib/phusion_passenger/config/installation_utils.rb +3 -3
  60. data/src/ruby_supportlib/phusion_passenger/constants.rb +5 -0
  61. data/src/ruby_supportlib/phusion_passenger/nginx/config_options.rb +4 -2
  62. data/src/ruby_supportlib/phusion_passenger/platform_info.rb +3 -3
  63. data/src/ruby_supportlib/phusion_passenger/request_handler.rb +1 -1
  64. data/src/ruby_supportlib/phusion_passenger/vendor/daemon_controller.rb +1 -1
  65. metadata +4 -2
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2016-2017 Phusion Holding B.V.
3
+ * Copyright (c) 2016-2018 Phusion Holding B.V.
4
4
  *
5
5
  * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
6
  * trademarks of Phusion Holding B.V.
@@ -33,6 +33,7 @@
33
33
  #include <vector>
34
34
  #include <stdexcept>
35
35
  #include <algorithm>
36
+ #include <utility>
36
37
  #include <cerrno>
37
38
  #include <cstddef>
38
39
  #include <cassert>
@@ -143,7 +144,7 @@ private:
143
144
 
144
145
  void createWorkDir() {
145
146
  TRACE_POINT();
146
- session.workDir.reset(new HandshakeWorkDir(session.uid, session.gid));
147
+ session.workDir.reset(new HandshakeWorkDir());
147
148
 
148
149
  session.envDumpDir = session.workDir->getPath() + "/envdump";
149
150
  makeDirTree(session.envDumpDir,
@@ -216,6 +217,43 @@ private:
216
217
  }
217
218
  }
218
219
 
220
+ // Open various workdir subdirectories because we'll use these file descriptors later in
221
+ // safeReadFile() calls.
222
+ void openWorkDirSubdirFds() {
223
+ session.workDirFd = openDirFd(session.workDir->getPath());
224
+ session.responseDirFd = openDirFd(session.responseDir);
225
+ session.responseErrorDirFd = openDirFd(session.responseDir + "/error");
226
+ session.envDumpDirFd = openDirFd(session.envDumpDir);
227
+ session.envDumpAnnotationsDirFd = openDirFd(session.envDumpDir + "/annotations");
228
+ openJourneyStepDirFds(getFirstSubprocessJourneyStep(),
229
+ getLastSubprocessJourneyStep());
230
+ openJourneyStepDirFds(getFirstPreloaderJourneyStep(),
231
+ JourneyStep((int) getLastPreloaderJourneyStep() + 1));
232
+ }
233
+
234
+ void openJourneyStepDirFds(JourneyStep firstStep, JourneyStep lastStep) {
235
+ JourneyStep step;
236
+
237
+ for (step = firstStep; step < lastStep; step = JourneyStep((int) step + 1)) {
238
+ if (!session.journey.hasStep(step)) {
239
+ continue;
240
+ }
241
+
242
+ string stepString = journeyStepToStringLowerCase(step);
243
+ string stepDir = session.responseDir + "/steps/" + stepString;
244
+ session.stepDirFds.insert(make_pair(step, openDirFd(stepDir)));
245
+ }
246
+ }
247
+
248
+ int openDirFd(const string &path) {
249
+ int fd = open(path.c_str(), O_RDONLY);
250
+ if (fd == -1) {
251
+ int e = errno;
252
+ throw FileSystemException("Cannot open " + path, e, path);
253
+ }
254
+ return fd;
255
+ }
256
+
219
257
  void initializeResult() {
220
258
  session.result.initialize(*context, config);
221
259
  }
@@ -266,10 +304,14 @@ private:
266
304
  TRACE_POINT();
267
305
  P_DEBUG("[App spawn arg] " << args.toStyledString());
268
306
 
307
+ // The workDir is a new random dir. the files that we create here
308
+ // should not exist, so if any exist then have createFile()
309
+ // throw an error because it could be a bug or an attack.
310
+
269
311
  createFile(session.workDir->getPath() + "/args.json",
270
312
  args.toStyledString(), 0600,
271
313
  session.uid, session.gid,
272
- true, __FILE__, __LINE__);
314
+ false, __FILE__, __LINE__);
273
315
 
274
316
  const string dir = session.workDir->getPath() + "/args";
275
317
  makeDirTree(dir, "u=rwx,g=,o=",
@@ -289,14 +331,14 @@ private:
289
331
  case Json::booleanValue:
290
332
  createFile(dir + "/" + it.name(),
291
333
  jsonValueToString(*it),
292
- 0644, session.uid, session.gid,
293
- true, __FILE__, __LINE__);
334
+ 0600, session.uid, session.gid,
335
+ false, __FILE__, __LINE__);
294
336
  break;
295
337
  default:
296
338
  createFile(dir + "/" + it.name() + ".json",
297
339
  jsonValueToString(*it),
298
- 0644, session.uid, session.gid,
299
- true, __FILE__, __LINE__);
340
+ 0600, session.uid, session.gid,
341
+ false, __FILE__, __LINE__);
300
342
  break;
301
343
  }
302
344
  }
@@ -533,7 +575,7 @@ public:
533
575
  assert(_session.config != NULL);
534
576
  }
535
577
 
536
- void execute() {
578
+ HandshakePrepare &execute() {
537
579
  TRACE_POINT();
538
580
 
539
581
  // We do not set SPAWNING_KIT_PREPARATION to the IN_PROGRESS or
@@ -545,6 +587,7 @@ public:
545
587
 
546
588
  resolveUserAndGroup();
547
589
  createWorkDir();
590
+ openWorkDirSubdirFds();
548
591
  initializeResult();
549
592
 
550
593
  UPDATE_TRACE_POINT();
@@ -572,6 +615,12 @@ public:
572
615
  session.journey.setStepErrored(SPAWNING_KIT_PREPARATION);
573
616
  throw SpawnException(e, session.journey, config).finalize();
574
617
  }
618
+
619
+ return *this;
620
+ }
621
+
622
+ void finalize() {
623
+ session.workDir->finalize(session.uid, session.gid);
575
624
  }
576
625
  };
577
626
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2016-2017 Phusion Holding B.V.
3
+ * Copyright (c) 2016-2018 Phusion Holding B.V.
4
4
  *
5
5
  * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
6
  * trademarks of Phusion Holding B.V.
@@ -28,6 +28,7 @@
28
28
 
29
29
  #include <boost/scoped_ptr.hpp>
30
30
  #include <string>
31
+ #include <map>
31
32
 
32
33
  #include <Utils.h>
33
34
  #include <Core/SpawningKit/Context.h>
@@ -50,6 +51,12 @@ struct HandshakeSession {
50
51
  boost::scoped_ptr<HandshakeWorkDir> workDir;
51
52
  string responseDir;
52
53
  string envDumpDir;
54
+ int workDirFd;
55
+ int responseDirFd;
56
+ int responseErrorDirFd;
57
+ int envDumpDirFd;
58
+ int envDumpAnnotationsDirFd;
59
+ map<JourneyStep, int> stepDirFds;
53
60
  Journey journey;
54
61
  Result result;
55
62
 
@@ -69,6 +76,11 @@ struct HandshakeSession {
69
76
  HandshakeSession(Context &_context, Config &_config, JourneyType journeyType)
70
77
  : context(&_context),
71
78
  config(&_config),
79
+ workDirFd(-1),
80
+ responseDirFd(-1),
81
+ responseErrorDirFd(-1),
82
+ envDumpDirFd(-1),
83
+ envDumpAnnotationsDirFd(-1),
72
84
  journey(journeyType, !_config.genericApp && _config.startsUsingWrapper),
73
85
  uid(USER_NOT_GIVEN),
74
86
  gid(GROUP_NOT_GIVEN),
@@ -77,6 +89,27 @@ struct HandshakeSession {
77
89
  { }
78
90
 
79
91
  ~HandshakeSession() {
92
+ if (workDirFd != -1) {
93
+ safelyClose(workDirFd, true);
94
+ }
95
+ if (responseDirFd != -1) {
96
+ safelyClose(responseDirFd, true);
97
+ }
98
+ if (responseErrorDirFd != -1) {
99
+ safelyClose(responseErrorDirFd, true);
100
+ }
101
+ if (envDumpDirFd != -1) {
102
+ safelyClose(envDumpDirFd, true);
103
+ }
104
+ if (envDumpAnnotationsDirFd != -1) {
105
+ safelyClose(envDumpAnnotationsDirFd, true);
106
+ }
107
+
108
+ map<JourneyStep, int>::iterator it, end = stepDirFds.end();
109
+ for (it = stepDirFds.begin(); it != end; it++) {
110
+ safelyClose(it->second);
111
+ }
112
+
80
113
  if (config->debugWorkDir && workDir != NULL) {
81
114
  string path = workDir->dontRemoveOnDestruction();
82
115
  P_NOTICE("Work directory " << path << " preserved for debugging");
@@ -54,7 +54,7 @@ private:
54
54
  string path;
55
55
 
56
56
  public:
57
- HandshakeWorkDir(uid_t uid, gid_t gid) {
57
+ HandshakeWorkDir() {
58
58
  char buf[PATH_MAX + 1];
59
59
  char *pos = buf;
60
60
  const char *end = buf + PATH_MAX;
@@ -70,9 +70,6 @@ public:
70
70
  "in the format of '" + StaticString(buf) + "'", e);
71
71
  } else {
72
72
  path = result;
73
- boost::this_thread::disable_interruption di;
74
- boost::this_thread::disable_syscall_interruption dsi;
75
- syscalls::chown(result, uid, gid);
76
73
  }
77
74
  }
78
75
 
@@ -86,11 +83,30 @@ public:
86
83
  return path;
87
84
  }
88
85
 
86
+ void finalize(uid_t uid, gid_t gid) {
87
+ finalize(path, uid, gid);
88
+ }
89
+
89
90
  string dontRemoveOnDestruction() {
90
91
  string result = path;
91
92
  path.clear();
92
93
  return result;
93
94
  }
95
+
96
+ static void finalize(const string &path, uid_t uid, gid_t gid) {
97
+ // We do not chown() the work dir until:
98
+ //
99
+ // - HandshakePrepare is done populating the work dir,
100
+ // - SpawnEnvSetupperMain is done reading from and modifying the work dir
101
+ //
102
+ // This way, the application user cannot perform symlink attacks
103
+ // inside the work dir until we are done (at which point the
104
+ // follow-up code will only perform read/write operations after
105
+ // dropping root privileges).
106
+ boost::this_thread::disable_interruption di;
107
+ boost::this_thread::disable_syscall_interruption dsi;
108
+ syscalls::chown(path.c_str(), uid, gid);
109
+ }
94
110
  };
95
111
 
96
112
 
@@ -39,7 +39,7 @@
39
39
  #include <sys/wait.h>
40
40
  #include <sys/stat.h>
41
41
  #include <signal.h>
42
- #include <cstdio>
42
+ #include <unistd.h>
43
43
  #include <cstring>
44
44
  #include <cassert>
45
45
 
@@ -50,12 +50,13 @@
50
50
  #include <Exceptions.h>
51
51
  #include <DataStructures/StringKeyTable.h>
52
52
  #include <ProcessManagement/Utils.h>
53
+ #include <FileTools/FileManip.h>
53
54
  #include <Utils/SystemTime.h>
54
- #include <Utils/IOUtils.h>
55
55
  #include <Utils/BufferedIO.h>
56
56
  #include <Utils/JsonUtils.h>
57
57
  #include <Utils/ScopeGuard.h>
58
58
  #include <Utils/ProcessMetricsCollector.h>
59
+ #include <Utils/AsyncSignalSafeUtils.h>
59
60
  #include <LveLoggingDecorator.h>
60
61
  #include <Core/SpawningKit/Spawner.h>
61
62
  #include <Core/SpawningKit/Exceptions.h>
@@ -177,6 +178,8 @@ private:
177
178
  }
178
179
 
179
180
  struct StdChannelsAsyncOpenState {
181
+ const int workDirFd;
182
+
180
183
  oxt::thread *stdinOpenThread;
181
184
  FileDescriptor stdinFd;
182
185
  int stdinOpenErrno;
@@ -187,8 +190,9 @@ private:
187
190
 
188
191
  BackgroundIOCapturerPtr stdoutAndErrCapturer;
189
192
 
190
- StdChannelsAsyncOpenState()
191
- : stdinOpenThread(NULL),
193
+ StdChannelsAsyncOpenState(int _workDirFd)
194
+ : workDirFd(_workDirFd),
195
+ stdinOpenThread(NULL),
192
196
  stdoutAndErrOpenThread(NULL)
193
197
  { }
194
198
 
@@ -211,7 +215,8 @@ private:
211
215
  StdChannelsAsyncOpenStatePtr openStdChannelsFifosAsynchronously(
212
216
  HandshakeSession &session)
213
217
  {
214
- StdChannelsAsyncOpenStatePtr state = boost::make_shared<StdChannelsAsyncOpenState>();
218
+ StdChannelsAsyncOpenStatePtr state = boost::make_shared<StdChannelsAsyncOpenState>(
219
+ session.workDirFd);
215
220
  state->stdinOpenThread = new oxt::thread(boost::bind(
216
221
  openStdinChannel, state, session.workDir->getPath()),
217
222
  "FIFO opener: " + session.workDir->getPath() + "/stdin", 1024 * 128);
@@ -282,7 +287,7 @@ private:
282
287
  static void openStdinChannel(StdChannelsAsyncOpenStatePtr state,
283
288
  const string &workDir)
284
289
  {
285
- int fd = syscalls::open((workDir + "/stdin").c_str(), O_WRONLY | O_APPEND);
290
+ int fd = syscalls::openat(state->workDirFd, "stdin", O_WRONLY | O_APPEND | O_NOFOLLOW);
286
291
  int e = errno;
287
292
  state->stdinFd.assign(fd, __FILE__, __LINE__);
288
293
  state->stdinOpenErrno = e;
@@ -291,7 +296,7 @@ private:
291
296
  static void openStdoutAndErrChannel(StdChannelsAsyncOpenStatePtr state,
292
297
  const string &workDir)
293
298
  {
294
- int fd = syscalls::open((workDir + "/stdout_and_err").c_str(), O_RDONLY);
299
+ int fd = syscalls::openat(state->workDirFd, "stdout_and_err", O_RDONLY | O_NOFOLLOW);
295
300
  int e = errno;
296
301
  state->stdoutAndErrFd.assign(fd, __FILE__, __LINE__);
297
302
  state->stdoutAndErrOpenErrno = e;
@@ -357,8 +362,11 @@ private:
357
362
 
358
363
  pid_t pid = syscalls::fork();
359
364
  if (pid == 0) {
360
- purgeStdio(stdout);
361
- purgeStdio(stderr);
365
+ int e;
366
+ char buf[1024];
367
+ const char *end = buf + sizeof(buf);
368
+ namespace ASSU = AsyncSignalSafeUtils;
369
+
362
370
  resetSignalHandlersAndMask();
363
371
  disableMallocDebugging();
364
372
  int stdinCopy = dup2(stdinChannel.first, 3);
@@ -367,6 +375,7 @@ private:
367
375
  dup2(stdoutAndErrCopy, 1);
368
376
  dup2(stdoutAndErrCopy, 2);
369
377
  closeAllFileDescriptors(2, true);
378
+
370
379
  execlp(agentFilename.c_str(),
371
380
  agentFilename.c_str(),
372
381
  "spawn-env-setupper",
@@ -374,10 +383,16 @@ private:
374
383
  "--before",
375
384
  NULL);
376
385
 
377
- int e = errno;
378
- fprintf(stderr, "Cannot execute \"%s\": %s (errno=%d)\n",
379
- agentFilename.c_str(), strerror(e), e);
380
- fflush(stderr);
386
+ char *pos = buf;
387
+ e = errno;
388
+ pos = ASSU::appendData(pos, end, "Cannot execute \"");
389
+ pos = ASSU::appendData(pos, end, agentFilename.data(), agentFilename.size());
390
+ pos = ASSU::appendData(pos, end, "\": ");
391
+ pos = ASSU::appendData(pos, end, ASSU::limitedStrerror(e));
392
+ pos = ASSU::appendData(pos, end, " (errno=");
393
+ pos = ASSU::appendInteger<int, 10>(pos, end, e);
394
+ pos = ASSU::appendData(pos, end, ")\n");
395
+ ASSU::printError(buf, pos - buf);
381
396
  _exit(1);
382
397
 
383
398
  } else if (pid == -1) {
@@ -419,7 +434,7 @@ private:
419
434
  // - bottom of stopPreloader()
420
435
  // - addPreloaderEnvDumps()
421
436
  HandshakePerform::loadBasicInfoFromEnvDumpDir(session.envDumpDir,
422
- envvars, userInfo, ulimits);
437
+ session.envDumpDirFd, envvars, userInfo, ulimits);
423
438
  string socketAddress = findPreloaderCommandSocketAddress(session);
424
439
 
425
440
  {
@@ -431,7 +446,7 @@ private:
431
446
  this->preloaderUserInfo = userInfo;
432
447
  this->preloaderUlimits = ulimits;
433
448
  this->preloaderAnnotations = loadAnnotationsFromEnvDumpDir(
434
- session.envDumpDir);
449
+ session.envDumpDir, session.envDumpAnnotationsDirFd);
435
450
  }
436
451
 
437
452
  PipeWatcherPtr watcher = boost::make_shared<PipeWatcher>(
@@ -825,16 +840,56 @@ private:
825
840
  {
826
841
  TRACE_POINT();
827
842
  pid_t spawnedPid = doc["pid"].asInt();
828
- ScopeGuard guard(boost::bind(nonInterruptableKillAndWaitpid, spawnedPid));
829
-
830
- UPDATE_TRACE_POINT();
831
- waitForStdChannelFifosToBeOpenedByPeer(stdChannelsAsyncOpenState,
832
- session, spawnedPid);
833
843
 
834
- UPDATE_TRACE_POINT();
835
844
  // How do we know the preloader actually forked a process
836
845
  // instead of reporting the PID of a random other existing process?
837
- // For security reasons we perform a UID check.
846
+ // For security reasons we perform a bunch of sanity checks,
847
+ // including checking the PID's UID.
848
+
849
+ if (spawnedPid < 1) {
850
+ UPDATE_TRACE_POINT();
851
+ session.journey.setStepErrored(SPAWNING_KIT_PROCESS_RESPONSE_FROM_PRELOADER);
852
+
853
+ SpawnException e(INTERNAL_ERROR, session.journey, session.config);
854
+ addPreloaderEnvDumps(e);
855
+ e.setSummary("The the preloader said it spawned a process with PID "
856
+ + toString(spawnedPid) + ", which is not allowed.");
857
+ e.setSubprocessPid(spawnedPid);
858
+ e.setStdoutAndErrData(getBackgroundIOCapturerData(
859
+ stdChannelsAsyncOpenState->stdoutAndErrCapturer));
860
+ e.setProblemDescriptionHTML(
861
+ "<h2>Application process has unexpected PID</h2>"
862
+ "<p>The " PROGRAM_NAME " application server tried"
863
+ " to start the web application by communicating with a"
864
+ " helper process that we call a \"preloader\". However,"
865
+ " the preloader reported that it started a process with"
866
+ " a PID of " + toString(spawnedPid) + ", which is not"
867
+ " allowed.</p>");
868
+ if (!session.config->genericApp && session.config->startsUsingWrapper
869
+ && session.config->wrapperSuppliedByThirdParty)
870
+ {
871
+ e.setSolutionDescriptionHTML(
872
+ "<h2>Please report this bug</h2>"
873
+ "<p class=\"sole-solution\">"
874
+ "This is probably a bug in the preloader process. The preloader "
875
+ "wrapper program is not written by the " PROGRAM_NAME " authors, "
876
+ "but by a third party. Please report this bug to the author of "
877
+ "the preloader wrapper program."
878
+ "</p>");
879
+ } else {
880
+ e.setSolutionDescriptionHTML(
881
+ "<h2>Please report this bug</h2>"
882
+ "<p class=\"sole-solution\">"
883
+ "This is probably a bug in the preloader process. The preloader "
884
+ "is an internal tool part of " PROGRAM_NAME ". Please "
885
+ "<a href=\"" SUPPORT_URL "\">"
886
+ "report this bug</a>."
887
+ "</p>");
888
+ }
889
+ throw e.finalize();
890
+ }
891
+
892
+ UPDATE_TRACE_POINT();
838
893
  uid_t spawnedUid = getProcessUid(session, spawnedPid,
839
894
  stdChannelsAsyncOpenState->stdoutAndErrCapturer);
840
895
  if (spawnedUid != session.uid) {
@@ -882,6 +937,11 @@ private:
882
937
  throw e.finalize();
883
938
  }
884
939
 
940
+ UPDATE_TRACE_POINT();
941
+ ScopeGuard guard(boost::bind(nonInterruptableKillAndWaitpid, spawnedPid));
942
+ waitForStdChannelFifosToBeOpenedByPeer(stdChannelsAsyncOpenState,
943
+ session, spawnedPid);
944
+
885
945
  UPDATE_TRACE_POINT();
886
946
  string alreadyReadStdoutAndErrData;
887
947
  if (stdChannelsAsyncOpenState->stdoutAndErrCapturer != NULL) {
@@ -1135,7 +1195,9 @@ private:
1135
1195
  return string();
1136
1196
  }
1137
1197
 
1138
- static StringKeyTable<string> loadAnnotationsFromEnvDumpDir(const string &envDumpDir) {
1198
+ static StringKeyTable<string> loadAnnotationsFromEnvDumpDir(const string &envDumpDir,
1199
+ int envDumpAnnotationsDirFd)
1200
+ {
1139
1201
  string path = envDumpDir + "/annotations";
1140
1202
  DIR *dir = opendir(path.c_str());
1141
1203
  if (dir == NULL) {
@@ -1147,9 +1209,8 @@ private:
1147
1209
  struct dirent *ent;
1148
1210
  while ((ent = readdir(dir)) != NULL) {
1149
1211
  if (ent->d_name[0] != '.') {
1150
- result.insert(ent->d_name, strip(
1151
- Passenger::readAll(path + "/" + ent->d_name)),
1152
- true);
1212
+ result.insert(ent->d_name, strip(safeReadFile(envDumpAnnotationsDirFd,
1213
+ ent->d_name, SPAWNINGKIT_MAX_SUBPROCESS_ENVDUMP_SIZE).first), true);
1153
1214
  }
1154
1215
  }
1155
1216
 
@@ -1241,8 +1302,10 @@ public:
1241
1302
 
1242
1303
  try {
1243
1304
  UPDATE_TRACE_POINT();
1244
- HandshakePrepare(session, extraArgs).execute();
1305
+ HandshakePrepare prepare(session, extraArgs);
1306
+ prepare.execute();
1245
1307
  createStdChannelFifos(session);
1308
+ prepare.finalize();
1246
1309
  session.journey.setStepPerformed(SPAWNING_KIT_PREPARATION, true);
1247
1310
 
1248
1311
  UPDATE_TRACE_POINT();