Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No error was thrown when spawn command was existed but non-executable #26852

Closed
chinesedfan opened this issue Mar 22, 2019 · 5 comments · Fixed by #27696
Closed

No error was thrown when spawn command was existed but non-executable #26852

chinesedfan opened this issue Mar 22, 2019 · 5 comments · Fixed by #27696
Labels
child_process Issues and PRs related to the child_process subsystem.

Comments

@chinesedfan
Copy link

chinesedfan commented Mar 22, 2019

  • Version:

10.2.0/11.12.0

  • Platform:

Darwin localhost 16.7.0 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 x86_64

  • Details:

Suppose we have following test script and file notexe exist in the same folder. But notexe has no executable permission.

const spawn = require('child_process').spawn;
const path = require('path');

function test(name) {
    console.log('===', name);

    try {
        const child = spawn(name);
        console.log('spawn.returned - child.stdin', !!child.stdin);
    } catch (e) {
        console.log('spawn.error', e.message);
    }
}

// always spawn with child.stdin equals true, and crash with ENOENT
test('notexe');  // can't find in PATH
test('/notexe'); // can't find in root folder

// in Node 8.9.0, spawn throws EACCES and no crash
// in Node 10.2.0/11.12.0, spawn throws no error with child.stdin equals false, and crash
test('./notexe');
test(path.resolve(__dirname, './notexe'));

So, why did Node 10+ change spawn behaviors? On purpose or regressions?

@targos
Copy link
Member

targos commented Mar 25, 2019

IMO going from a throw to an emitted error is a good thing, since spawn is supposed to be async.

Let me show what I think is a real bug in Node.js.

With this script located in /tmp/child_process, and an empty, non-executable file /tmp/child_process/y:

'use strict';

const { spawn } = require('child_process');
const { existsSync } = require('fs');
const { resolve } = require('path');

function test(path) {
  const exists = existsSync(path);
  const child = spawn(path);
  const hasStdin = !!child.stdin;
  child.on('error', (e) => {
    console.log(`Path: "${path}".\nExists: ${exists}.\nHas stdin: ${hasStdin}\n\n`);
  });
}

test('x');
test('/tmp/child_process/x');
test('./x');
test('y');
test('/tmp/child_process/y');
test('./y');
Path: "x".
Exists: false.
Has stdin: true


Path: "/tmp/child_process/x".
Exists: false.
Has stdin: true


Path: "./x".
Exists: false.
Has stdin: true


Path: "y".
Exists: true.
Has stdin: true


Path: "/tmp/child_process/y".
Exists: true.
Has stdin: false


Path: "./y".
Exists: true.
Has stdin: false

We have a clear inconsistency. If the file does not exist, the object returned by spawn always has the stdin property. If the file exists but is not executable, stdin is only present if the path doesn't start with ./ or /.

/cc @nodejs/child_process

@targos
Copy link
Member

targos commented May 6, 2019

@bnoordhuis What do you think?

@cjihrig
Copy link
Contributor

cjihrig commented May 14, 2019

So, why did Node 10+ change spawn behaviors? On purpose or regressions?

The behavior change comes from #19294, which began classifying EACCES as a runtime error (emitting vs. throwing).

If the file exists but is not executable, stdin is only present if the path doesn't start with ./ or /

I think this is more about the error that's occurring. The two cases where "has stdin" is false are both EACCES, while the rest are ENOENT.

ENOENT gets special treatment here. Since that comment was originally written, we've started handling UV_EAGAIN and UV_EACCES too, but they should be treated the same as UV_ENOENT in my opinion.

We can:

  • Remove the special handling of UV_ENOENT, and return all runtime errors. This is definitely a breaking change, as it makes one test fail. I prefer this option because that comment acknowledges its own silliness, and it's kind of wasteful to set up stdio for no reason. I'm willing to PR this change.
  • Include UV_EACCES and UV_EAGAIN in the special handling with UV_ENOENT. I didn't test this variation. It's also a breaking change, even if our test suite doesn't catch it.
  • Do something else, including nothing.

@cjihrig
Copy link
Contributor

cjihrig commented May 14, 2019

Actually, on second thought, we probably do want to setup stdio to prevent situations like nodejs/help#1769.

@gireeshpunathil
Copy link
Member

having stdio setup in all runtime error scenarios (though am unable to figure out what needs to be done in the code to meet this) looks to be the right way forward for me. That way, from programmer's perspective, they could meaningfully tap both stdio and error streams of the child without breaking the program.

cjihrig added a commit to cjihrig/node that referenced this issue May 20, 2019
As more spawn() errors are classified as runtime errors, it's
no longer appropriate to only check UV_ENOENT when determining
if stdio can be setup. This commit reverses the check to look
for EMFILE and ENFILE specifically.

PR-URL: nodejs#27696
Fixes: nodejs#26852
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
BridgeAR pushed a commit that referenced this issue May 21, 2019
As more spawn() errors are classified as runtime errors, it's
no longer appropriate to only check UV_ENOENT when determining
if stdio can be setup. This commit reverses the check to look
for EMFILE and ENFILE specifically.

PR-URL: #27696
Fixes: #26852
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
child_process Issues and PRs related to the child_process subsystem.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants