| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
| char* cstr = strdup(nodeopt.c_str()); | ||
| // [0] is expected to be the program name, fill it in from the real argv. | ||
| argv_from_env[argc_from_env++] = argv[0]; | ||
| // XXX(sam) can I use strtok or strtok_r? |
There was a problem hiding this comment.
@bnoordhuis ----^
Sorry, something went wrong.
There was a problem hiding this comment.
Why would you not be able to use strtok_r?
Sorry, something went wrong.
There was a problem hiding this comment.
why not use std::string::find and substr?
Sorry, something went wrong.
There was a problem hiding this comment.
strtok_r isn't ANSI C, not sure it exists on Windows or even all the unixen.
Sorry, something went wrong.
There was a problem hiding this comment.
@sam-github My version of the manpage says it’s part of the recent POSIX standards, so I’d take that as a good sign. If you want to be sure whether it’s okay you could just kick of a CI run that uses it.
(You can also wait for Ben’s answer, of course, but I got the impression that he’s a bit more busy than usual right now…)
Sorry, something went wrong.
There was a problem hiding this comment.
I thought the little one wasn't due for a while. :-) Anyhow, Windows is not so POSIX, but I'll just do it and if it works in CI I guess its good for us. Of course, strtok is everywhere, but its not clear to me whether we care about it not being thread safe. Node doesn't here, but it might matter when its used as a library.
Sorry, something went wrong.
| const char** argv, | ||
| int* exec_argc, | ||
| const char*** exec_argv, | ||
| bool is_nodeopt = false) { |
There was a problem hiding this comment.
Can you align the arguments vertically?
Sorry, something went wrong.
| char* cstr = strdup(nodeopt.c_str()); | ||
| // [0] is expected to be the program name, fill it in from the real argv. | ||
| argv_from_env[argc_from_env++] = argv[0]; | ||
| // XXX(sam) can I use strtok or strtok_r? |
There was a problem hiding this comment.
Why would you not be able to use strtok_r?
Sorry, something went wrong.
| if (SafeGetenv("NODEOPT", &nodeopt)) { | ||
| const char** argv_from_env = new const char*[(nodeopt.length()+1) / 2]; | ||
| int argc_from_env = 0; | ||
| char* cstr = strdup(nodeopt.c_str()); |
There was a problem hiding this comment.
Why the copy? nodeopt will have its own memory anyway
Sorry, something went wrong.
There was a problem hiding this comment.
Suspicion, the c_str() docs aren't amazingly clear about the lifetime of the underlying mem. I'll try without (though I've a mem bug somewhere anyway that I have to find).
Sorry, something went wrong.
There was a problem hiding this comment.
@sam-github c_str() is an alias for data() nowadays, it lives as long as the string instance itself
Sorry, something went wrong.
There was a problem hiding this comment.
can't non-const methods on the string reallocate the memory invalidating the last pointer?
Sorry, something went wrong.
There was a problem hiding this comment.
They can, but you’re not doing that? Indexed accesses seem to be excluded from invalidating the pointer (according to http://en.cppreference.com/w/cpp/string/basic_string/c_str), which makes sense
Sorry, something went wrong.
| if (debug_options.ParseOption(arg)) { | ||
| // Done, consumed by DebugOptions::ParseOption(). | ||
| } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) { | ||
| DisallowInNodeopts(argv[0], is_nodeopt, arg); |
There was a problem hiding this comment.
Probably worth listing all the options you've excluded in the first comment.
Sorry, something went wrong.
There was a problem hiding this comment.
You mean in git commit body, list the options? I can do.
Sorry, something went wrong.
There was a problem hiding this comment.
Yeah, I suspect a large amount of the discussion will be around which options we should enable under what conditions etc.
Sorry, something went wrong.
There was a problem hiding this comment.
I added the list and reasons to the description of this item for visibility, and will add them back in later.
Sorry, something went wrong.
There was a problem hiding this comment.
when you say you will add them back in later. Do you mean add the reasons for the exclusion as comments in the code ? I think a comment in the code as well is good in terms of longer term history when people look and wonder why its not allowed. I do see you have the comments for some of them.
Sorry, something went wrong.
There was a problem hiding this comment.
added comments in source, will add to the commit message when the list stabilizes
Sorry, something went wrong.
|
Couple of discussion points on this:
|
Sorry, something went wrong.
|
Sorry, something went wrong.
|
For (1) .. good point :-) The only other one that I would definitely rule out, however, is the --security-revert= flag. It's a purposefully undocumented flag that is a very special case. It's use needs to be explicitly opt-in in every situation. |
Sorry, something went wrong.
|
Right, forgot --security-revert (I'd read through the code and it didn't do anything, there aren't any reverts at the moment, so I forgot). See c0836e5 |
Sorry, something went wrong.
|
+1 ... yeah, --security-revert is a special snowflake that is reset with every semver-major. We haven't had any CVE's that needed to be included in this since 4.x I think so it's a non-op at the current time. Hopefully it'll be able to remain a non-op forever :-) |
Sorry, something went wrong.
|
On stripping of NODEOPT env var during spawn: I think it takes away from the usefullness of NODEOPT (specifically, it means we can't use NODEOPT="-r node-report" npm install ... to debug install scripts), intrudes confusingly into the child_process API, and goes against the basic philosophy of environment, which is that environment is inherited by children. However, I'd rather have at least a one-level NODEOPT with this confusing caveat than none at all. @jasnell, can you make a case for why you want this non-inheritable env var behaviour? And what specifically you propose? For example: require('child_process').exec('node -p process.env.NODE_PATH', function(err, out) {
console.log(err || out);
});
require('child_process').exec('node -p process.env.NODEOPT', function(err, out) {
console.log(err || out);
});That NODE_PATH=/some/path NODEOPT=--prof node echo.js should print: /some/path instead of /some/path --prof I would find confusing, is that what you propose? And what do you propose that the following code would print? process.env.NODEOPT = '-r node-report';
require('child_process').exec('node -p process.env.NODEOPT', function(err, out) {
console.log(err || out);
});
I would expect it to print the value of NODEOPT, do you propose it print something else? |
Sorry, something went wrong.
|
@sam-github ... the concern is largely about backwards compatibility and the possibility of NODEOPT settings altering the behavior of child processes in ways that could break. For instance, imagine a child process that uses fs.readFileSync() internally to read a file and return it's contents to the parent. The parent is written to scan stdout / stderr to determine whatever status it needs to know. The child process is launched specifically assuming that flags like --trace-sync-io are not passed (because it is not passed in the args). Using NODEOPT=--trace-sync-io, however, causes a number of warnings to be printed to stderr in the child process potentially causing existing code to break. (Essentially, passing NODEOPT automatically to child processes breaks the existing API contract around args passing to forked child processes.) My proposal is that NODEOPT would be simply removed from the env passed to the child process unless an option is set, e.g. fork(path, {nodeopt: true}). |
Sorry, something went wrong.
|
But don't we already have that issue with existing environment variables, e.g. NODE_DEBUG? |
Sorry, something went wrong.
|
NODE_TTY_UNSAFE_ASYNC is also inherited, and modifies API contract in ways that would break child processes, there seems to be some precedence for these "use at own risk" env vars. If there is widespread concern, I'd rather disable V8 options that output to stdio then break the inheritance, it leaves the behaviour much more predictable. |
Sorry, something went wrong.
|
NODE_TTY_UNSAFE_ASYNC is in the process of being removed entirely, however. There is also extremely little evidence that it's actually used by anyone. |
Sorry, something went wrong.
|
NODE_DEBUG isn't getting removed, and has the same characteristics as you describe for NODEOPT=--trace-sync-io: it can be useful, it is inherited, and it will (unsprisingly) break any code that doesn't expect extra output. |
Sorry, something went wrong.
|
I would be much more comfortable if this was a whitelist instead of a blacklist. It would prevent accidentally harmful flags from being exposed. |
Sorry, something went wrong.
|
NODE_DEBUG has been around since v0.1.32 and is well established as an environment variable. It predates the child_process API and it's side effects and inheritance by spawned child processes is well known. It also does not alter arbitrary command line arguments passed into the child process. There is no backwards compatibility concern there and therefore is not directly comparable. |
Sorry, something went wrong.
|
@jasnell , what about if we have NODEOPT and NODEOPT_INHERIT and let people choose the behavior they want. NODEOPT_INHERIT would be passed to child processes while NODEOPT would not. In that way you can use the environment to set options and chose if they are inherited or not. I think having the ability to pass on options to child processes is important and this would be one way to let the end user make a specific choice. |
Sorry, something went wrong.
|
@jasnell Before I implement this, lets be really clear:
child_process.spawn(process.execPath, {env: {NODEOPT='xxx'}}, function(...
You propose NODEOPT to not be set in the child process's env? @bmeck, Do you mean for node options, or V8 options? I can switch it around to opt-in for node, I think there is a way I can do that, but there are 417 V8 options as of this moment on master, and they all look like the kinds of things someone might want to set via env to tune performance or diagnose mis-performance, and there don't appear to be anything we'd want to blacklist. |
Sorry, something went wrong.
There was a problem hiding this comment.
Personally, I'd rather that C++ std lib classes are used to avoid all the manual allocations. And you shouldn't have to worry about if the C++ std lib is available on a certain platform. And you don't need to rely on the non-reentrant legacy c strtok.
std::vector<std::string> env_args;
std::string::size_type pos = 0, pos_next = 0;
while (pos_next < nodeopt.size())
{
pos_next = nodeopt.find(" ", pos);
if (pos_next == std::string::npos)
{
pos_next = nodeopt.size();
}
if (pos_next > pos)
{
env_args.push_back(nodeopt.substr(pos, pos_next - pos));
}
pos = pos_next + 1;
}
std::vector<const char *> argv_from_env;
argv_from_env.reserve(args.size() + 2);
argv_from_env.push_back(argv[0]);
for (size_t i = 0; i < args.size(); i++)
{
argv_from_env.push_back(env_args[i].c_str());
}
argv_from_env.push_back(nullptr);and now you can call ProcessArgv:
ProcessArgv(&argc_from_env, &argv_from_env[0], &exec_argc_, &exec_argv_, true );
Sorry, something went wrong.
|
@sam-github ... no, if NODEOPT is explicitly passed in the call to fork like in your example, it would be used. What I'm saying is that env is not explicitly given and the default process.env is used, then it's NODEOPT would not be inherited. In other words... NODEOPT is used: child_process.spawn(process.execPath, {env: {NODEOPT:'...'}}, () => {});
process.env.NODEOPT is used: child_process.spawn(process.execPath, {nodeopt: true}, () => {});
process.env.NODEOPT is not used: child_process.spawn(process.execPath, () => {});
@mhdawson ... as long as there is an explicit opt-in for the options to be used in child processes I'm fine with however it happens. A second environment variable would not be my first choice tho but I could live with it. |
Sorry, something went wrong.
|
Note: that if we went with the nodeopt:true option to spawn/fork now, we can always choose to switch the default later (making the option a non-op) if it can be shown that inheriting the NODEOPT env does not break anyone in practice. |
Sorry, something went wrong.
There was a problem hiding this comment.
does this handle multiple spaces? like "-a -b".
Sorry, something went wrong.
There was a problem hiding this comment.
yes
Sorry, something went wrong.
|
@jasnell @mhdawson If the main risk you have in mind is security, it won’t matter whether you pass one or two environment variables. (But as I’ve said before, using environment variables that are not inherited by child processes seems pointless by definition.) |
Sorry, something went wrong.
|
@sam-github I am definitely thinking about more than just node arguments. Things like --harmony-* would prevent some polyfill detection and use incomplete/broken v8 implementations if they are passed. |
Sorry, something went wrong.
|
Another note: the documentation on this should need to be clear that: (a) command line arguments passed directly on the command line take precedence over those passed in NODEOPT and To illustrate the kind of impact this has consider the following example: $ NODEOPT="--icu-data-dir=a" ./node --icu-data-dir=b
> process.binding('config');
{ hasIntl: true, hasSmallICU: true, icuDataDir: 'b' }
> var m = child_process.spawn('./node', ['-pe', 'console.log(process.binding("config"))'], {stdio:['inherit','inherit', 'inherit']})
undefined
> { hasIntl: true, hasSmallICU: true, icuDataDir: 'a' }
To be certain, this is not a criticism, just something that should be noted. (This is also true when using the NODE_ICU_DATA environment variable) Note that the current CLI/env documentation (https://nodejs.org/dist/latest-v7.x/docs/api/cli.html) currently does not explain that arguments would take precedence over env vars. |
Sorry, something went wrong.
|
I agree with @addaleax that environment variables should be passed to child processes by default. |
Sorry, something went wrong.
|
Another test case that will need be looked at: NODEOPT="--inspect=localhost:1234" ./node
Debugger listening on port 1234.
Warning: This is an experimental feature and could change at any time.
To start debugging, open the following URL in Chrome:
chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=localhost:1234/f8884219-6fd2-442e-aa5e-257cb686e3a5
> var m = child_process.spawn('./node', {stdio:['inherit', 'inherit', 'inherit']})
undefined
> Starting inspector on localhost:1234 failed: address already in use
|
Sorry, something went wrong.
Not all CLI options are supported, those that are problematic from a security or implementation point of view are disallowed, as are ones that are inappropriate (for example, -e, -p, --i), or that only make sense when changed with code changes (such as options that change the javascript syntax or add new APIs). PR-URL: nodejs#12028 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com> Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Not all CLI options are supported, those that are problematic from a security or implementation point of view are disallowed, as are ones that are inappropriate (for example, -e, -p, --i), or that only make sense when changed with code changes (such as options that change the javascript syntax or add new APIs). PR-URL: nodejs#12028 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com> Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Not all CLI options are supported, those that are problematic from a security or implementation point of view are disallowed, as are ones that are inappropriate (for example, -e, -p, --i), or that only make sense when changed with code changes (such as options that change the javascript syntax or add new APIs). PR-URL: nodejs#12028 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com> Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Not all CLI options are supported, those that are problematic from a security or implementation point of view are disallowed, as are ones that are inappropriate (for example, -e, -p, --i), or that only make sense when changed with code changes (such as options that change the javascript syntax or add new APIs). Backport-PR-URL: #12677 PR-URL: #12028 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com> Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
| Back | FazBrowse Home | New Git URL |
Not all CLI options are supported, those that are problematic from a
security or implementation point of view are disallowed, as are ones
that are inappropriate.
Disallowed because they don't make any sense to inject, they change node behaviour to fundamentally to be useful:
Disallowed because of security concerns:
Disallowed because of implementation difficulties:
V8 options:
Tests are still missing, and I'm in the process of testing -r, the most important option, but it seems basically workable.
Fix: #11997
Replace: #11888
Reference: #881
NODEOPT=--expose_gc ./node -p 'typeof gc' NODEOPT=--expose_gc ./node --no-expose_gc -p 'typeof gc' NODEOPT=hi ./node --no-expose_gc -p 'typeof gc' NODEOPT=--expose-internals ./node -p "require('internals')" NODEOPT=--throw-deprecation ./node -p "require('_linklist')"Checklist
Affected core subsystem(s)
src