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

Child process' execFile and execFileSync handle arguments different when shell is not falsy #43333

Closed
ericcornelissen opened this issue Jun 6, 2022 · 6 comments · Fixed by #43345
Labels
child_process Issues and PRs related to the child_process subsystem. confirmed-bug Issues with confirmed bugs.

Comments

@ericcornelissen
Copy link

ericcornelissen commented Jun 6, 2022

Version

v18.3.0

Platform

Linux *** 5.13.0-44-generic #49~20.04.1-Ubuntu SMP *** x86_64 x86_64 x86_64 GNU/Linux

Subsystem

node:child_process

What steps will reproduce the bug?

  1. Create an ESM file with the following content:
    import { execFile, execFileSync } from "node:child_process";
    
    const stdout = execFileSync(
      "echo",
      ["foo", "bar"],
      { shell: "/bin/bash" },
    );
    console.log(`execFileSync: ${stdout}`); // Outputs: "execFileSync: \n"
    
    execFile(
      "echo",
      ["foo", "bar"],
      { shell: "/bin/bash" },
      (_, stdout) => {
        console.log(`execFile: ${stdout}`); // Output: "execFile: foo bar\n"
      },
    );
  2. Note that the options object includes a non-falsy value for the shell option. (for reference, see the child_process.execFile documentation).
  3. Run the file using the following command to be able to view the args argument to execFile(Sync) after normalization:
    $ env NODE_DEBUG=child_process node t.js 2>&1 | grep -w args
  4. Observe the different behaviour:
    args: [ '/bin/bash', '-c', '/bin/bash -c echo foo bar' ],
    args: [ '/bin/bash', '-c', 'echo foo bar' ],
    

How often does it reproduce? Is there a required condition?

It always reproduces.

What is the expected behavior?

child_process.execFileSync and child_process.execFile invoke commands in the same way given the same arguments. In particular, I believe the behaviour of execFileSync is expected.

What do you see instead?

child_process.execFileSync and child_process.execFile invoke commands in different ways given the same arguments.

Additional information

This bug reports follows from a discussion in #29466 - in particular the discussion starting with this comment of mine. This bug report is based on @bnoordhuis' comment in that thread.

I tested and was able to reproduce this bug on Node v16.15.0 and v18.3.0.

@VoltrexKeyva VoltrexKeyva added the child_process Issues and PRs related to the child_process subsystem. label Jun 6, 2022
@bnoordhuis bnoordhuis added the confirmed-bug Issues with confirmed bugs. label Jun 7, 2022
@bnoordhuis
Copy link
Member

Create an ESM file

For the record: also happens with CJS.

@zhmushan
Copy link
Contributor

zhmushan commented Jun 7, 2022

This is caused by normalizeSpawnArguments being called repeatedly. We should avoid using the normalize function for spawn in execFile, even though it works fine most of the time.

This issue can be assigned to me.

@F3n67u
Copy link
Member

F3n67u commented Jun 7, 2022

Could you help me understand why the behavior of execFileSync is expected?

If echo foo bar is interpreted to bash -c 'echo foo bar' when shell is /bin/bash, then foo bar should be printed, right?

$ bash -c "echo foo bar"
foo bar

@zhmushan
Copy link
Contributor

zhmushan commented Jun 8, 2022

@F3n67u Your understanding is correct, but now execFileSync interprets echo foo bar as /bin/bash -c "/bin/bash -c echo foo bar"

@RaisinTen
Copy link
Contributor

This issue can be assigned to me.

No need to wait for someone to assign you to the issue, feel free to send the PR when your patch is ready. :)

@ericcornelissen
Copy link
Author

Could you help me understand why the behavior of execFileSync is expected?

That's based on @bnoordhuis' comments in #29466.

If echo foo bar is interpreted to bash -c 'echo foo bar' when shell is /bin/bash, then foo bar should be printed, right?

That is actually the topic of the discussion I brought up over at #29466 as well.

ericcornelissen added a commit to ericcornelissen/webmangler that referenced this issue Jun 13, 2022
Due to a bug with `execFileSync` [1], arguments passed to it when used
in combination with the `shell: true` option won't be used on certain
shells. Notably, this includes many Unix shells (such as Bash). Luckily,
for the purposes of this project, `spawnSync` can be used instead.

--
1. nodejs/node#43333
nodejs-github-bot pushed a commit that referenced this issue Jul 18, 2022
PR-URL: #43345
Fixes: #43333
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: LiviaMedeiros <livia@cirno.name>
danielleadams pushed a commit that referenced this issue Jul 26, 2022
PR-URL: #43345
Fixes: #43333
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: LiviaMedeiros <livia@cirno.name>
Fyko pushed a commit to Fyko/node that referenced this issue Sep 15, 2022
PR-URL: nodejs#43345
Fixes: nodejs#43333
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: LiviaMedeiros <livia@cirno.name>
ericcornelissen added a commit to ericcornelissen/shescape that referenced this issue Jan 31, 2023
Since, per [1], it's not actually guaranteed these work the same.

--
1. nodejs/node#43333
ericcornelissen added a commit to ericcornelissen/shescape that referenced this issue Feb 1, 2023
* Get fuzz shell once per fuzz target

* Handle errors explicitly in fuzz targets

* Extract argument escaping/quoting out of invocation

Should make for easier overall reading and aligns all fuzz targets with
the style of `exec.test.cjs`.

* Test both sync and callback versions of explicitly

Since, per [1], it's not actually guaranteed these work the same.

* Remove multiple argument versions of checks

I do not believe these currently provide significant value. They don't
seem to test anything special about multiple arguments. Plus the method
by which these convert the buffer into multiple args leaves a lot to be
desired. An upside is that fuzzing should become more efficient as less
cycles are wasted on testing something with little to no value.

--
1. nodejs/node#43333
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. confirmed-bug Issues with confirmed bugs.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants