passenger 5.3.1 → 5.3.2

Sign up to get free protection for your applications and to get access to all the features.
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();