29

I was reading the famous Unix Recovery Legend, and it occurred to me to wonder:

If I had a BusyBox shell open, and the BusyBox binary were itself deleted, would I still be able to use all the commands included in the BusyBox binary?

Clearly I wouldn't be able to use the BB version of those commands from another running shell such as bash, since the BusyBox file itself would be unavailable for bash to open and run. But from within the running instance of BusyBox, it appears to me there could be two methods by which BB would run a command:

  1. It could fork and exec a new instance of BusyBox, calling it using the appropriate name—and reading the BusyBox file from disk to do so.
  2. It could fork and perform some internal logic to run the specified command (for example, by running it as a function call).

If (1) is the way BusyBox works, I would expect that certain BusyBox-provided commands would become unavailable from within a running instance of BB after the BB binary were deleted.

If (2) is how it works, BusyBox could be used even for recovery of a system where BB itself had been deleted—provided there were still a running instance of BusyBox accessible.

Is this documented anywhere? If not, is there a way to safely test it?

Wildcard
  • 35,316
  • 26
  • 130
  • 258
  • 2
    `is there a way to safely test it?` Download the generic x86 `openwrt` image and attach the image to a new VirtualBox machine – basin Apr 04 '16 at 20:17
  • 2
    And this raises the question, *how* do Busybox commands continue to work after `PATH` is unset? Does it assume a default value of `PATH`? – muru Apr 05 '16 at 01:05
  • 2
    @muru: From the source code (at least for its ash clone) it looks like it treats an unset PATH the same as it would an empty string, so it searches the current directory, and only that. – hmakholm left over Monica Apr 05 '16 at 15:57
  • @HenningMakholm Well, my comment was answered by Gilles' answer. However, it's good to know that - I'd expected only builtins to work. – muru Apr 05 '16 at 16:40
  • 1
    Apart from some seldom accidents/legends, this is useful in real-life if you want to replace your operating system by a different unpacked root directory. In this case ld.so (ld-linux.so) and /bin/mv are crucial. – u_Ltd. Sep 30 '20 at 23:16

3 Answers3

34

By default, BusyBox doesn't do anything special regarding the applets that it has built in (the commands listed with busybox --help).

However, if the FEATURE_SH_STANDALONE and FEATURE_PREFER_APPLETS options are enabled at compile time, then when BusyBox sh¹ executes a command which is a known applet name, it doesn't do the normal PATH lookup, but instead runs its built-in applets through a shortcut:

  • Applets that are declared as “noexec” in the source code are executed as function calls in a forked process. As of BusyBox 1.22, the following applets are noexec: chgrp, chmod, chown, cksum, cp, cut, dd, dos2unix, env, fold, hd, head, hexdump, ln, ls, md5sum, mkfifo, mknod, sha1sum, sha256sum, sha3sum, sha512sum, sort, tac, unix2dos.
  • Applets that are declared as “nofork” in the source code are executed as function calls in the same process. As of BusyBox 1.22, the following applets are nofork: [[, [, basename, cat, dirname, echo, false, fsync, length, logname, mkdir, printenv, printf, pwd, rm, rmdir, seq, sync, test, true, usleep, whoami, yes.
  • Other applets are really executed (with fork and execve), but instead of doing a PATH lookup, BusyBox executes /proc/self/exe, if available (which is normally the case on Linux), and a path defined at compile time otherwise.

This is documented in a bit more detail in docs/nofork_noexec.txt. The applet declarations are in include/applets.src.h in the source code.

Most default configurations turn these features off, so that BusyBox executes external commands like any other shell. Debian turns these features on in both its busybox and busybox-static packages.

So if you have a BusyBox executable compiled with FEATURE_SH_STANDALONE and FEATURE_PREFER_APPLETS, then you can execute all BusyBox commands from a BusyBox shell even if the executable is deleted (except for the applets that are not listed above, if /proc/self/exe is not available).

¹ There are actually two implementations of "sh" in BusyBox — ash and hush — but they behave the same way in this respect.

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • @Wildcard I included a bit too much when I extracted the list from the source code. Fixed, thanks. – Gilles 'SO- stop being evil' Apr 05 '16 at 01:16
  • This is a great answer, but I'm having a little trouble following the conditionals. It seems there are four values/variables that could have an effect on the behavior of any given command: `FEATURE_SH_STANDALONE`, `FEATURE_PREFER_APPLETS`, `nofork` and `noexec`. Could you please clarify how these four factors interact? I only see two possible behaviors in the final outcome, as specified in my question; why the dual flags "nofork" and "noexec"? – Wildcard Apr 05 '16 at 01:32
  • 1
    @Wildcard `FEATURE_PREFER_APPLETS` and `FEATURE_SH_STANDALONE` are compile-time flags, enabling or disabling features. The applets are marked `nofork` and `noexec` irrespective of which flags were used. Whether or not such markings have any effect depends on `FEATURE_PREFER_APPLETS` being enabled. Hence, three possible behaviours: 1. `FEATURE_PREFER_APPLETS` disabled, 2. `FEATURE_PREFER_APPLETS` enabled and applet is `nofork`, 3. `FEATURE_PREFER_APPLETS` enabled and applet is `noexec`. The third para in the docs explain it nicely. And the last section shows the possible cases. – muru Apr 05 '16 at 02:04
  • Thanks, @muru. In summary then it looks like to get the behavior of "able to run all (or nearly all) BB commands from a running BB instance *even if the binary is deleted*," BB would need to be compiled with `FEATURE_PREFER_APPLETS`, `FEATURE_SH_STANDALONE` *and* `FEATURE_SH_NOFORK`? – Wildcard Apr 05 '16 at 02:23
  • 1
    @Wildcard `FEATURE_SH_STANDALONE` (which requires `FEATURE_PREFER_APPLETS`). `nofork` isn't needed. With `FEATURE_SH_STANDALONE`, `/proc/self/exe` is used where applicable, so it will work [even if BB was deleted](http://unix.stackexchange.com/a/197901/70524). You can test this out with fairly minimal risk on any Debian or Arch Linux systm, run `busybox ash`, `unset PATH`, do basin's commands. It works fine. – muru Apr 05 '16 at 02:38
  • 3
    On an Ubuntu 14.04.1 LTS system, Busybox is configured to prefer applets. Since neither `cat` nor `chmod` require exec-ing a pathname, you can recover the executable thusly: `cat /proc/self/exe > busybox; chmod 755 busybox`. – Barefoot IO Apr 05 '16 at 02:40
  • Why would cat be nofork but tac be noexec? I mean, there shouldn't be any fundamental difference between the two. – forest Apr 05 '16 at 05:57
  • 1
    @forest There's a huge difference: `tac` requires either a seekable input file which is not always available, or reading the entire input into memory. `cat` can read its input from start to finish, discarding what it's already processed. It's much easier to implement and it's also much more commonly used, so it makes more sense to optimise that one. – hvd Apr 05 '16 at 07:24
  • 1
    @Wildcard Nofork and noexec are indications set on each applet. `FEATURE_xxx` is a compile-time option for BusyBox as a whole. The nofork and noexec indications only matter if `FEATURE_PREFER_APPLETS` is active (at least for the purpose of executing a command in the shell, they're also used in some other contexts). – Gilles 'SO- stop being evil' Apr 05 '16 at 10:01
  • @Giles, the bullet points for noexec and nofork currently show the opposite examples than they should - Applets that are declared as “**noexec**” in the source code are executed as function calls in a forked process. As of BusyBox 1.22, the following applets are **nofork** – Keith Hall Apr 05 '16 at 10:48
  • The third bullet point now *fully* answers my question. So, given the availability of `/proc/self/exe` and given a BusyBox binary compiled with `FEATURE_SH_STANDALONE` and `FEATURE_PREFER_APPLETS`, *all* BusyBox commands would indeed be fully available so long as BB itself were running. Thanks very much; accepted! – Wildcard Apr 05 '16 at 23:05
  • busybox show the error `applet not found` when I try to use the `echo` command in a distroless docker image like `gcr.io/distroless/java-debian10:11`. Is there any repository to download a compiled binary of busybox with FEATURE_SH_STANDALONE and FEATURE_PREFER_APPLETS enabled? – august0490 Mar 04 '21 at 06:05
  • @august0490 You could download Debian's `busybox-static` binary. Extract `/bin/busybox-static` from the `.deb` package and it'll work on any system with a non-antique Linux kernel and the matching processor architecture. – Gilles 'SO- stop being evil' Mar 04 '21 at 12:37
9

is there a way to safely test it? With the generic x86 openwrt image:

vbox screenshot

Most commands are not built-in, but some are, like echo and printf. A binary file with arbitrary contents can be created using printf, but chmod +x will be a problem.

basin
  • 1,931
  • 4
  • 20
  • 36
0
is there a way to safely test it?

You can check it with the error message of the command. If it's the same from within busybox and when invoked explicitely, then the "adventure" would fail.

On my system the sash shell would be still available, if I had it running.

user@ulmus-thomasii:~$ echo mv b | busybox sh
mv: Fehlender Zieldatei‐Operand hinter 'b'
„mv --help“ liefert weitere Informationen.
user@ulmus-thomasii:~$ /bin/mv b
/bin/mv: Fehlender Zieldatei‐Operand hinter 'b'
„/bin/mv --help“ liefert weitere Informationen.
user@ulmus-thomasii:~$ echo mv b | sash
mv: Fehlender Zieldatei‐Operand hinter 'b'
„mv --help“ liefert weitere Informationen.
user@ulmus-thomasii:~$ echo -mv b | sash
usage: -mv srcName ... destName
user@ulmus-thomasii:~$ 
u_Ltd.
  • 111
  • 2