Are BusyBox commands truly built in

busyboxsystem-recovery

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?

Best Answer

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.

Related Question