$ echo '#!/path/to/busybox echo' > myecho
$ chmod +x myecho
$ ./myecho 123
./myecho 123
Right now this doesn't work properly, because "./myecho" (argv[0]) gets placed into argv[2] of the process. Otherwise, this technique IMHO is better than symlinks:- Each applet uses the same amount of disk space (0 blocks, i.e. the content fits into inode).
- Doesn't read or write to argv[0].
- You could finally rename the applets. This is not that useful if busybox is your only posix userspace implementation, but very useful if you want many implementations to live side-by-side. E.g. on macOS, I'd like to have readlink point to BSD/macOS's readlink, greadlink to GNU coreutil's, bbreadlink to busybox's.
But as I said, this doesn't work for now. The best you can do now is to write shell two-liners https://news.ycombinator.com/item?id=41436012. Some of such two-liners may also fit into the inode inlining limit, so that's a plus. But you will have performance penalty on every call (since sh needs to start up).
Finally, symlinks can be relative, while the solution you proposed is not. This is particularly useful for distributing software, e.g. distributing a tar file with the busybox itself and their symlinks.
In fact, you don't even need symlinks at all: you can even have hard links, that could even save disk space on embedded filesystems, that are readonly images anyway.
There are valid reasons but I think the performance angle is the weakest argument to make.
I highly doubt that. Path traversal is one of the most optimized pieces of code in the Linux kernel, especially for commonly accessed places like /bin where everything is most likely already in the dentry cache. For the script with a shebang on the other hand it first has to read it from disk (or the page cache), then parse the path from it, and then do a path traversal anyway to find the referenced file.
Those that exist today would, but no kernel would have to work like that.
Once you've agreed that monolithic kernels have merits, you've accepted that the kernel can do whatever it wants to make this efficient—including being complicit in this scheme and leapfrogging over most of what you just described.
That's a pretty weird argument. "Yes, what you say is completely correct, but let's imagine a world where you were wrong."
We have what we have, today. We should form conclusions and make decisions based on things that exist, not on things that we might dream up.
Aside from that, it's against the rules here to those kinds of fake quotes. (And even if it weren't against the rules, it's obnoxious and disingenuous on its own, so just, like, you know... don't do it.)
What part of the guidelines are you referring to?
Also, despite the quotation marks, I don't think they mean to quote you. They're just rephrasing you as they understood you.
Coincidentally enough, I've just done that too in another comment:
And I didn't mention the guidelines (i.e. newsguidelines.html). On that note, though:
> the site guidelines[...] aren't a list of proscribed behaviors but a set of values to internalize. I'd say "Please respond to the strongest plausible interpretation of what someone says, not a weaker one that's easier to criticize" covers this case pretty squarely
(from <https://news.ycombinator.com/item?id=15892014#15893789>)
See also:
<https://news.ycombinator.com/item?id=38688831#38690517>
plus lots (and lots) more.
Besides, should one really write something performance critical for embedded in shell in the first place?
Is that really the case? AFAIK, OpenWRT uses SquashFS by default, and a quick web search tells me that "[...] In addition, inode and directory data are highly compacted, and packed on byte boundaries. Each compressed inode is on average 8 bytes in length [...]" (https://www.kernel.org/doc/html/latest/filesystems/squashfs....). That is, even if the content fits into the inode, it will make the inode use more space (they're variable-size, unlike on traditional filesystems with fixed-size inodes).
And using hardlinks (traditionally, we use hardlinks with busybox, not symlinks) goes even further: all commands use a single inode, the only extra space needed is for the directory entry (which you need anyway).
#!/bin/sh
busybox $0 $@
and then every command required could just be a hardlink to the same script, instead of replicating it over and over again for hardcoded command names.Then I realised the whole point is to posit a world where $0 doesn't exist, and we're not allowed to be clever about it.
I don't think that's the gist of the article, but the throwaway suggestion of 'just make lots of copies, who cares about diskspace' is insufficient and thus distracts. It's.. a single line about solutions in an article that isn't _about_ solving problems, it's about highlighting a problem exists and that it's worth solving.
I read the article more as: There is __often__ no good reason to use argv[0], and it should be avoided if at all possible, and if it cannot be avoided, it would behoove the industry to work on ways to make sure in the future it can be avoidable.
For example, why in the blazes does windows taskman.exe list argv[0] in the GUI table view? That's just asking for trouble. Show the actual file path, and always an absolute one - that way you avoid confusion about which executable you're actually running, and it's just as readable if not more readable for every app _except_ those who care about argv[0], e.g. if you ran `/bin/dd` and it's actually busybox, in taskman you'd see `/bin/busybox` instead which'd be worse than seeing 'dd'. That is simple enough to solve (add an API call to update _your own process name_ or at least update your own process 'title' which interfaces like ps/taskman can use accordingly), but, now we're talking about coordinating between OS, glibc, busybox, and so on - lots of parties. I don't mind that the article doesn't delve that deep, as that wasn't the point of it. The point is simply to show the problems the kludge of 'we will show argv[0] instead of the executable name' causes.
This article feels more about explaining that in the distant past, a mistake was made with some history as to why that mistake was made and the deleterious practical effects that this mistake is causing or is likely to cause (most of them security related). It's not really about solving the problem; that presumably comes later and should be sketched out by those who are knowledgable on _that_ subject. That doesn't imply the author is ignorant or that the article is insufficiently defended. Just that it hasn't covered all aspects of what it's writing about.
This was kind of in the middle of your complaint about windows, but then you've got unixy busybox discussion.
On a unix filesystem, a file that's hard linked with multiple names has no single 'actual name'. All of the names are equally valid. You could show the filesystem and inode number, which should uniquely identify the file, but is pretty user unfriendly.
But each of the multiple names points to the same actual data, so it doesn't matter which one is shown. The obvious choice would be to show the absolute path that the OS used to load the executable.
The same is true for hard linked files on Windows. That never stops Windows from showing you a path.
There is almost always an "obviously right" path (the one used when opening the file). And if you lost track of that, deterministically choosing one of the possible paths is almost always more user friendly than just chowing inode numbers.
The path used while opening a file is easy to get confused. If your cwd changed names or was deleted since you entered it, and you open an executable with a relative path, what is the "obviously right" path then?
Really, this discussion just shows the question is the problem; if you're asking "what path was used the launch the process", that's easy to keep track of & always be right. If you're asking "what is the path right now to the file that launched the process", maybe that has no answer.
(And a program could do that itself, if it wants to.)
https://man.freebsd.org/cgi/man.cgi?query=setproctitle&aprop...
Coding bugs into your programs is not a problem it’s a bug. None of the weird arg[0] examples can happen on the shell (without escaping), only when using system calls.
The more I read the article the more I feel this is a reaction to a behavior the author did not expect and fancy them as smart therefore the last 20 years of use age of this feature are obviously wrong.
$ help exec
[...]
Options:
-a name pass NAME as the zeroth argument to COMMAND
Even in shell, you can explicitly specify the argv[0] when running an executable.Bash is a language, so again we are telling the stream to do something silly and calling it out had a security problem.
The issue is not arg[0] but uninformed expectations on how these systems work.
Relying on the program/command name for security and not the executable path is a bug.
Furthermore if a bad actor has enough access to run exec you probably are in a bad way.
The whole post also seems to not understand that both windows and linux have ways to change this display after the executable is running via SetConseTitle and prctl or simply modify arg[0] directly.
We could call it setproctitle, or something. \s
You could argue the mistake was done elsewhere so this feature could be abused.
There are numerous reasons why this is not desirable, for example knowing whether an application was called from one symbolic link or a relative path dictates what that application's working directory is.
If so, then I disagree with the premise of the article, fundamentally. I don't see a problem. If someone is writing security software and doesn't already know about the mutability of argv[0], and doesn't know that (on Linux at least) /proc/$PID/exe is the only correct way to gt the binary backing a process... well, then they have no business writing security software.
There is no problem here. The author is making a big deal about nothing, either because they have a weird axe to grind, or because they're ignorant.
It's the other way round. This is like asking if running RHEL is part of the specification? Obviously not. But RHEL provides an environment that is quite close to the specification.
So is running a busybox part of the specification? Obviously not. But Busybox provides an environment that is close to the speficiation.
#!/bin/sh
exec /bin/busybox cmp
And place it at /usr/bin/cmp #!/bin/sh
exec /bin/busybox cmp "$@" Applications should note that the standard PATH to the shell cannot be assumed to be either /bin/sh or /usr/bin/sh, and should be determined by interrogation of the PATH returned by getconf PATH, ensuring that the returned pathname is an absolute pathname and not a shell built-in.Plus, you have to know the absolute path of the executable busybox, not something you always know in advance.
But realistically, the only people that need to worry about the actual location of the busybox executable are the people who write the install script - it would take that as an argument or variable and spit out all the little scripts as an automated process. The current installer already has to do most of this work as part of setting up the relevant links anyway.
If there really is a need for having one executable that comprises multiple commands, is `busybox whoami` instead of `whoami` so much more effort? To me, that would make more sense in terms of what is going on; aliases could be used if one-word commands are preferred. In most non busybox contexts, argv[0] is just an unnecessary addition that, as the linked article shows, can introduce weirdness.
It's clear from the comments there are still many who think argv[0] is a good thing, which is great - I'm glad the post sparked this debate.
It's not the "more effort" that is the deal breaker here. It is a matter of compliance with specs and user expectations. What you're suggesting would make Busybox very non-POSIXy, very non-Unixy. All scripts written over the last many decades would need to be updated to call `busybox ls` instead of `ls`? How is that a viable solution?
> I'm glad the post sparked this debate.
This is a very strange way to deflect concerns about quality of the article!
(a) the hypothetical non-argv0 busybox being discussed, and
(b) a POSIX shell of the maintainer's choice, with built-in aliases for 'ls=busybox ls'
be sufficient to make the system POSIX complaint?
That's indeed why I personally don't use shell aliases at all, instead opting for actual shell scripts in my $PATH. Those will work no matter what shell I'm using (if any).
I really don’t think it is a debate. The usage of arg[0] is massively understated by the article. Just go look at gcc or any modern day compiler. Its use so much that the conversion of should we has been hashes out by many different groups yet they still chose to implement it.
The security concerns are a non issue. As arg[0] was not the problem. It was the lack of technical knowledge of how systems work and a flaw in the security application.
Bash has an sh compatibility mode that runs when you invoke it as sh.
You appear not to realize that busybox is an essential component of a POSIX like system.
Shell aliases don't solve all problems, even if you do:
alias rm="busybox rm"
alias xargs="busybox xargs"
# etc.
you still have to write `xargs -exec busybox rm`, because xargs won't use the shell alias.But the main problem with this approach is that POSIX and LSB require certain binaries to be available at certain paths. When they're not, most shell scripts will just break.
The minimal standard solution is probably to create shell scripts for all of these, e.g. in /bin/ls:
#!/bin/sh
exec /bin/busybox ls
But this both adds runtime overhead (on every invocation!) and is quite wasteful in terms of disk space. Busybox boasts over 400 tools. At 4 KB per file, that's 1.6 MiB of just shell scripts. Of course that can be less if the file system uses some type of compression which is common on embedded systems where storage space is small, but it still seems to defeat the purpose of using busybox to create a minimal system. #!/bin/busybox sh
exec /bin/busybox ls
?Actually this observation invalidates the whole setup. Because even though you could define /bin/sh itself as:
#!/bin/busybox sh
exec /bin/busybox sh
Then you still cannot use #!/bin/sh in any other shell scripts, because for historical reasons the interpreter of a script is not allowed to be another interpreted script, it must be a binary. So /bin/sh pretty much has to be an actual binary.What you do? Replace every single occurrence of each command by prefixing it `busybox`? Not ideal at all...
One good use for it is to make a guess as to where your executable is installed. Yes, it would be nice if there were a more certain way to get that... but not for security purposes. You don't want to rely on filenames for security anyway, because anybody can make copies and symlinks and rename files at will, and it's really, really hard to catch all the cases of that. Much harder than, for instance, remembering that argv[0] is a hint from your caller, not gospel from the OS.
In the same way, I know that it's fashionable nowadays for incompetent idiots to write security tools, but a security tool that trusts an argv value for anything much was obviously written by an incomptent idiot, because that's not what they're for.
This person is arguing that that's somehow wrong because busybox, or more importantly some other software that's trying to monitor it, might get confused about whether it's busybox.
Busybox is a reimplementation of the standard linux utils (ls, find, dir, etc..) for resource limited machines. To quote from the man page:
> BusyBox is a multi-call binary that combines many common Unix
> utilities into a single executable.
How it works is that it symlinks the binary to each of the commands it implements and then it executes the corresponding functionality based on the value of argv[0].
The hangup here seems to be the definition of "program". I'm using it to mean something roughly like "executable", which I think is fairly close to what the original article meant it to mean. You seem to be using some concept of "program" that makes each of busybox's functions a separate program. As far as I'm concerned, on the other hand, busybox is one big program that does a lot of largely unrelated things, choosing which of them to do based on how it's invoked. There's no right answer. You could say that all of the software running on a whole computer is one giant program, and in fact sometimes I do find it convenient to think of it that way.
I don't know that your definition of "program" is wrong, but I do think it's alien to this context.
Then your previous statement makes no sense in context. At least to me.
Yes busybox knows it's busybox. But busybox doesn't do anything if it is not invoked in a certain way which relies on argv[0] being what it is today. I am not sure what you're arguing for frankly.
The article's lead argument for the "badness" of argv[0] seems to be, roughly paraphrased, that "the program should already know what it is [true], and this could confuse it [Huh? No I don't really know what that means either]". That's followed by a bunch of other stuff about other programs guessing what executable is running in a given process based on its argv[0], which is of course just deeply ignorant misuse of the value.
I mean, "the name" of the file that got loaded isn't even necessarily either well defined, or useful under any definition.
Not that it couldn't be fixed by changing how we handle login shells but still. Worth remembering.
Similarly the busybox situation could be solved by having busybox ship posix shell wrapper scripts which use `#!/bin/busybox sh` as the shebang and simply consist of a line like `exec /bin/busybox ls "$@"`.
- symlink
- hardlink
- shell script wrappers
- executable binary wrappers around libbusyboxI think any reason one will find are based on backwards compatibility.
The real point are the security flaws in a calling program setting argv[0], because it really, really should be set by the operating system. (As a programmer, I shouldn't have to defend against these kinds of attacks. The OS should block it.)
The criticisms of valid programming practices, IMO, hurt the author's credibility and distract from the real point of the article.
argv[0] was designed to be part of the arguments to the program, and it succeeds perfectly at that task. The problem is that it has been abused by external tools as a way to identify the program just because there was no other alternative.
It has to be writable because the entire argv string (in program memory) is writable and declared as
int main(int argc, char **argv)
not int main(int argc, const char **argv)
and needs to preserve back-compat. Classic C code might be calling strtok on the arguments, so that block of memory needs to remain writable.How would that help? After all, even if this info comes from the OS, the decision logic still lives in your process's memory which the parent process still has full access to.
There is no mention of mutable memory attacks.
(If I was on a computer I'd fire up a C IDE to even see what happens when I mutate argv. I suspect the OS keeps its own copy of what the process was started with.)
The problem here isn't argv[0], the problem is security software not understanding what argv[0] is and if you want security software to better be able to identify processes, the solution isnt changing argv[0], it's implementing an actual process ID checking.
There is an alternative, at least on linux: /proc/$pid/exe.
And if your question is "what executable is running" that is a better way to get it. But for a program like busybox, argv[0] is also important.
Both Windows and Linux provide APIs to get the actual path of the executable. Posix, to the best of my knowledge, does not. Regrettably. And the Linux API is, admittedly, a bit weird. But not that difficult really. Nothing that you can't get Claude to spit out for you in under 45 seconds. ;-P
The contents of argv[0] are yours to use and abuse as you see fit. Operating systems don't know or care if you go trampling recklessly through the contents of argv[0].
And the authors contention that "power --shutdown" and "power --reboot" are viable alternatives to "shutdown" and "reboot" seems.... disingenuous. Is the politest word I can come up with.
And, if you haven't asked yourself, "wait a second, what happens if somebody passes me garbage via execve" before you are halfway through writing the substantial amount of code required to portably normalize argv[0] to an executable path, I don't think you can be trusted to write secure code of any form. Just normalizing the various forms of argv[0] that a Linux shell passes you is a non-trivial effort. So don't use it for THAT.
argv[0] should be used by any logging message that purports to report the program name, because argv[0] should be a string the human recognizes as something they invoked. Taking it away would break usability.
This does, of course, imply that the program name is non-constant untrusted data. Which means we shouldn't be making security software that depends on knowing that name.
edit: basically login(1) execes your shell with - prepended, so an example where POSIX expects this
However, I am willing to consider the discussion about whether there could be merit to restricting the ability to write that value; I could imagine a system that populated it only from the actual file name and did not allow it to be written by the parent process or the child process at runtime. The obvious place this still falls apart is that an attacker could just
but there are sometimes security measures that we use even though they're less than 100% effective so it at least conceivable that this might be a trade off worth making.