From 767d1c66b549f276f887fac562ec2c6168545637 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 29 Apr 2016 12:03:48 +0200 Subject: [PATCH 1/2] src: only disable PIE on OS X when -prof is passed This is a follow-up to commit a5012a0 ("build: unbreak -prof, disable PIE on OS X") that disabled PIE (and therefore ASLR) completely on OS X. We now scan the command line options and re-exec the process with the _POSIX_SPAWN_DISABLE_ASLR flag set when `-prof` is specified. The options parser is smart enough to understand any combination of `-prof` and `-noprof`, including alternative spellings like `--no_prof`. Fixes: https://github.com/nodejs/node/issues/6466 --- common.gypi | 1 - src/node.cc | 57 +++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/common.gypi b/common.gypi index 10da7abccc44..d852a3e5327e 100644 --- a/common.gypi +++ b/common.gypi @@ -327,7 +327,6 @@ ['_type!="static_library"', { 'xcode_settings': { 'OTHER_LDFLAGS': [ - '-Wl,-no_pie', '-Wl,-search_paths_first', ], }, diff --git a/src/node.cc b/src/node.cc index 479130230b7c..978d15370936 100644 --- a/src/node.cc +++ b/src/node.cc @@ -85,6 +85,7 @@ typedef int mode_t; #ifdef __APPLE__ #include +#include #define environ (*_NSGetEnviron()) #elif !defined(_MSC_VER) extern char **environ; @@ -3906,7 +3907,31 @@ static void DebugEnd(const FunctionCallbackInfo& args) { } -inline void PlatformInit() { +inline void PlatformInit(int argc, char** argv) { +#ifdef __APPLE__ + // ASLR makes the output of `-prof` useless for C++ code so re-exec the + // process with ASLR disabled. Note that _POSIX_SPAWN_DISABLE_ASLR is + // ignored for setuid/setgid binaries; in that particular case the only + // option is to recompile with `make LINK="c++ -Wl,-no_pie"` to disable + // PIE (and therefore ASLR) altogether. + if (v8_is_profiling) { + // Use a key that is unlikely to have been set by the user. + static const char kRespawnEnvKey[] = "\x01NODE_MAC_RESPAWN_FOR_PROFILING"; + if (getenv(kRespawnEnvKey)) { + CHECK_EQ(0, unsetenv(kRespawnEnvKey)); + } else { + const bool overwrite = true; + CHECK_EQ(0, setenv(kRespawnEnvKey, "1", overwrite)); + posix_spawnattr_t attr; + CHECK_EQ(0, posix_spawnattr_init(&attr)); + // POSIX_SPAWN_SETEXEC + _POSIX_SPAWN_DISABLE_ASLR + CHECK_EQ(0, posix_spawnattr_setflags(&attr, 64 + 256)); + pid_t pid; + CHECK_EQ(0, posix_spawnp(&pid, argv[0], nullptr, &attr, argv, environ)); + UNREACHABLE(); + } + } +#endif // __APPLE__ #ifdef __POSIX__ sigset_t sigmask; sigemptyset(&sigmask); @@ -3998,17 +4023,6 @@ void Init(int* argc, const char** v8_argv; ParseArgs(argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv); - // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler - // manually? That would give us a little more control over its runtime - // behavior but it could also interfere with the user's intentions in ways - // we fail to anticipate. Dillema. - for (int i = 1; i < v8_argc; ++i) { - if (strncmp(v8_argv[i], "--prof", sizeof("--prof") - 1) == 0) { - v8_is_profiling = true; - break; - } - } - #ifdef __POSIX__ // Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the // performance penalty of frequent EINTR wakeups when the profiler is running. @@ -4350,7 +4364,24 @@ static void StartNodeInstance(void* arg) { } int Start(int argc, char** argv) { - PlatformInit(); + // Search until the first non-flag argument for `-prof` and `-noprof`. + // `--prof` and `--noprof` are allowed as alternative spellings, as are + // any variation of `-no-prof` and `-no_prof`. + for (int i = 1; i < argc && argv[i][0] == '-'; i += 1) { + const char* arg = &argv[i][1]; + if (*arg == '-') + arg += 1; + const bool off = !strncmp(arg, "no", 2); + if (off) { + arg += 2; + if (*arg == '-' || *arg == '_') + arg += 1; + } + if (!strcmp(arg, "prof")) + v8_is_profiling = !off; + } + + PlatformInit(argc, argv); CHECK_GT(argc, 0); From 7c009b0b4952a282a9a77ff8f3282fd61c542368 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 29 Apr 2016 19:04:53 +0200 Subject: [PATCH 2/2] fixup! only re-exec when vm slide != 0 --- src/node.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/node.cc b/src/node.cc index 978d15370936..bc8760444036 100644 --- a/src/node.cc +++ b/src/node.cc @@ -85,6 +85,7 @@ typedef int mode_t; #ifdef __APPLE__ #include +#include #include #define environ (*_NSGetEnviron()) #elif !defined(_MSC_VER) @@ -3914,7 +3915,7 @@ inline void PlatformInit(int argc, char** argv) { // ignored for setuid/setgid binaries; in that particular case the only // option is to recompile with `make LINK="c++ -Wl,-no_pie"` to disable // PIE (and therefore ASLR) altogether. - if (v8_is_profiling) { + if (v8_is_profiling && _dyld_get_image_vmaddr_slide(0) != 0) { // Use a key that is unlikely to have been set by the user. static const char kRespawnEnvKey[] = "\x01NODE_MAC_RESPAWN_FOR_PROFILING"; if (getenv(kRespawnEnvKey)) {