Open milahu opened 2 years ago
Interesting find! So, if I understand correctly, both uutils and GNU have the same problem here?
For GNU, there is at least a workaround: -S
. This works on my machine:
#! /usr/bin/env -S A=1 python
# test.py
import os; print(os.environ["A"])
But uutils env
does not support this yet. The reason this is necessary is that A=1 python
are passed to env
as one argument (although it is not clear to me why that leads to infinite recursion).
Sidenote: If you wanted to post an upstream bug report to GNU coreutils, you are in the wrong place. This repo is a reimplementation of the GNU coreutils. GNU bug reports are created from their mailing list and posted to https://debbugs.gnu.org/cgi/pkgreport.cgi?pkg=coreutils. See https://www.gnu.org/software/coreutils/coreutils.html for more info.
For GNU, there is at least a workaround:
-S
weird ...
But
uutils
env does not support this yet.
both uutils and GNU have the same problem here?
yes. let's hope that no one depends on this bug ; )
The reason this is necessary is that
A=1 python
are passed toenv
as one argument
no. A=1
is correctly parsed as "set env A to 1"
see my example
$ ./coreutils/target/debug/env A=1 python
>>> import os; print(os.environ["A"])
1
so it's the combination of shebang and env that triggers the bug
This repo is a reimplementation of the GNU coreutils.
yes. i feel it's easier to fix the bug here first, and then send a patch to upstream
The reason this is necessary is that A=1 python are passed to env as one argument
no. A=1 is correctly parsed as "set env A to 1"
Yeah, sorry, I meant "are passed as one argument in a shebang" :). It is indeed correct when not used as a shebang. See section 23.2.2 here: https://www.gnu.org/software/coreutils/manual/html_node/env-invocation.html
aaah, so it's a limitation of shebang https://en.wikipedia.org/wiki/Shebang_(Unix)#Character_interpretation
#! /usr/bin/printf A=1 '(%s) ' 1 2 3 4
prints
A=1 '(./test.sh) ' 1 2 3 4
because A=1 '(%s) ' 1 2 3 4
is parsed as one string
which makes the env -S
feature necessary to pass arguments
#! /usr/bin/env -S printf '(%s) ' 1 2 3 4
prints
(1) (2) (3) (4) (./test.sh)
so it's surprising that argv[1] == "A=1 python"
leads to infinite recursion
while this fails correctly
#! /usr/bin/env hello world
with
/usr/bin/env: ‘hello world’: No such file or directory
/usr/bin/env: use -[v]S to pass options in shebang lines
I don't think this is actually a bug and env
is working as intended. I know that what I just said sounds wrong, but hear me out first.
test.sh
consists of:
#! /path/to/env A=1 echo
When running ./test.sh
, argv
will become the following:
["/path/to/env", "A=1 echo", "./test.sh"]
Alright, everything is working as intended until now. Now is where it all falls apart.
When env
parses argv[1]
it notices the =
sign and splits it into 2 parts, the first being A
and the second being 1 echo
. Then it sets A
to 1 echo
. Since there are no longer any arguments with an =
, argv[2]
(./test.sh
) is the command that is going to be run.
So basically env
has correctly set A
to 1 echo
and then runs ./test.sh
again, starting the fork bomb. This is all intended behavior. Weird but intended. If it were modified, there would be no way to set a variable with spaces in it using env
.
If it were modified, there would be no way to set a variable with spaces in it using
env
.
spaces can be quoted ...
$ env '-S printf "a %q z\n" 1 2 3 4'
a 1 z
a 2 z
a 3 z
a 4 z
$ ./target/debug/env '-S printf "a %q z\n" 1 2 3 4'
error: Found argument '-S' which wasn't expected, or isn't valid in this context
... but not escaped
$ env '-S printf a\ %q\ z\n 1 2 3 4'
env: invalid sequence '\ ' in -S
$ env '-S printf a\\ %q\\ z\\n 1 2 3 4'
a\printf: warning: ignoring excess arguments, starting with ‘%q\\’
So basically
env
has correctly setA
to1 echo
and then runs./test.sh
again, starting the fork bomb. This is all intended behavior. Weird but intended.
another difference:
gnu env
is using "tail call recursion" which runs forever
uutils env
is using "stack recursion" which runs until OOM
compare these:
#! /usr/bin/env A=1 python
# test.py
import os; print(os.environ["A"])
#! ./coreutils/target/debug/env A=1 python
# test.py
import os; print(os.environ["A"])
systemd-run --scope -p TasksMax=10 --user strace -f --trace execve ./test.py 2>&1 | grep -F 'execve("./test.py"'
/usr/bin/env
keeps running, because numTasks is always below 10
./coreutils/target/debug/env
stops after 10 forks
upstream issue from 2015 https://bugs.launchpad.net/ubuntu/+source/coreutils/+bug/1421760
these work as expected ...
but when i combine shebang with
env A=1
, i get infinite recursionwarning: this is a fork bomb → use
systemd-run
to limit the number of forks