18 ms·
Why bother with argv[0]?
- nottorp 2y ago<Cough> Busybox. There is life outside the enterprise security theater.
- linsomniac 2y ago"Security" software that trusts /proc/cmdline (and the like), and in particular if it doesn't complain about /proc/cmdline having a mismatch with /proc/exe, doesn't seem like very useful security software to me. Particularly if it's security software that is making some security decisions based on argv[0]. Seems like this security software is broken, not argv[0]
- suprjami 2y agoIt's not often a self-promotion blog post has the entirety of HN telling you you're wrong. Better luck next time lol
- kelnos 2y ago> From a 2020s standpoint, this seems highly undesirable, as it makes software less predictable and goes against modern design principles. Says who? I'm not aware of any modern design principles that say anything about this sort of thing. > argv[0] is ignored (mostly) Pretty much any program I've written that has a --help option uses argv[0] to print out the usage string, i.e.: printf("%s [--some-arg] FILENAME\n", argv[0]); > First off, argv[0] can be used to fool security software Then that security software is poorly written. On Linux, the correct way to find the binary of a running process is by calling readlink(2) on /proc/$PID/exe. Assuming security software like this is going to have a lot of OS-specific code, it seems fine to me to expect they use it (and then have to do other things on other OSes). > Another argument against this design is that if you have two programs that are so similar that it pays off to consolidate them into a single file, is there really a need for two separate programs/program names? The author is talking about shutdown and restart being symlinks to systemctl on systemd-based systems. But what about something like busybox? busybox contains hundreds of programs, all conveniently in a single, statically-linked binary. On my system it's about 800kB. While I agree that even 250MB is not a big deal for most systems these days, it certainly is a problem for, say, a WiFi router that only has 8MB of flash. > Ultimately, nobody wants to be bothered by argv[0]. False. I find it useful, and am not "bothered" by it at all. And I suspect security folks aren't really bothered either: the ones that actually know what they're doing look at /proc/$PID/exe when they want to find the binary backing a PID. This article is kinda lame, and it seems like the author's objections are mostly based on ignorance.
- anacrolix 2y agoI think the Unix philosophy wins here. It might not be a clean interface but let the implementations decide what to do with it. If you remove it you are more likely to cause issues and have to grow new interfaces elsewhere.
- deleted 2y ago[deleted]
- _xiaz 2y agoL Take
- johnisgood 2y agoSo wait, I should not use `argv` in C's main() or what? Is it only speaking against `argv[0]` or `argv` in general? What is this proposed solution if any? What about `__progname`? The only issue here is that if `argv[0]` is a path, then `__progname` is only the filename. What if I want the path?
- JoyfulPanda 2y agoHoly moly, the article addresses argv[0] as the problem, while the real problem is that the snake oil industry has no clue what they are doing
- sph 2y agoWhat a silly post. I use argv[0] in my host-spawn tool (https://github.com/1player/host-spawn https://github.com/1player/host-spawn) so one can symlink it to a name inside a container and when you run it, it's executed on the host. # Inside your container: $ flatpak --version zsh: command not found: flatpak # Have host-spawn handle any flatpak command $ ln -s /usr/local/bin/host-spawn /usr/local/bin/flatpak # Now flatpak will always be executed on the host $ flatpak --version Flatpak 1.12.7 I am able to tell the symlink name by reading argv[0] to know which command to run. It is such a powerful and neat UNIX trick that has no simple alternative (in this example one would have to write ad-hoc shell scripts for each command they want to run)
- 4star3star 2y agoThe post is silly because you wrote software that makes use of argv[0]? On the contrary, it opens a discussion about unintended security implications that might be avoided in the future if command line implementation can be reconsidered.
- thayne 2y ago> and (especially a few decades ago) can offer cross-platform/backwards syntax compatibility using a shared code base. This is still very much an issue. For the shutdown and reboot case, the main reason those symlinks is exist is for backwards compatibility for existing programs and scripts (and muscle memory) that assume there is a shutdown or reboot command, and compatibility with systems that don't use systemd. Another way to do that could be to use a shell script that execs systemctl, but that requires a separate intermediate shell process, which may have its own compatibility issues. Another use of argv[0] that isn't discussed at all is putting a hyphen at the beginning of argv[0] for login shells. For example if bash is invoked as the login shell argv[0] is "-bash". That probably wasn't a great design decision, but changing it now would probably cause a lot of breakage.
- account42 2y ago> This seems like a questionable design decision. Nope. > Should a program be allowed to behave differently based on its name? Yes. The program can also inspect any other part of its environment, including the parent process. What makes sense to inspect here depends on the particular program in question. The symlink example is still useful today. > From a 2020s standpoint, this seems highly undesirable Nope. > it makes software less predictable It doesn't. It makes it more predictable if programs can easily provide compatibility interfaces. Yes, you could do the same with a wrapper but removing friction matters. > and goes against modern design principles. Then modern design priciples can take a hike. > Today however, disk space is no longer considered an issue It should be considered an issue though. I buy better hardware to get more use out of it, not for lazy developers to needlessly piss it all away. This is just yet nother example of "securit" people trying to make their lifes easier by making other's lifes harder. And as usual it's only theater since almost all of the "exploits" apply to arguments as well which for many programs provide plenty opportunity to include arbitrary strings. Fix your tools instead of expecting the world to work around their limitations.
- nmz 2y agoReally strange that argv[0] has a basically unlimited character size while #! has a hardcodede 256 byte limit.
- guappa 2y agoWait until he finds out about busybox! Also claiming that the windows API to call a new process is good… wow… I guess he's never had to pass a filename with quotes and spaces in its name. The API expects you to do the escaping yourself. Yes it needs to be escaped, because it's all one single string.
- pjc50 2y agoThere are a number of good things about CreateProcess, but argument passing is not one of them. It's a very longstanding misfeature in the design of CMD.EXE and almost certainly dates from MSDOS and therefore CP/M. A side effect of that is that programs do their own unescaping. Unix users who are used to quotes being stripped for them may be surprised by this.
- timrobinson333 2y agoMany windows programmers fail to appreciate this. If you're using a language that provides argv-style functionality, the quoting and escaping mechanism is entirely at the mercy of that language, so you can't reliably make any general assumptions about how to quote parameters to a command line
- account42 2y agoAnd specifically, Microsoft themselves can't even agree on the rules so Win32 API CommandLineToArgv and and the MSVCRT have slightly different quoting/escaping rules.
- deleted 2y ago[deleted]
- avidiax 2y agoIt is sometimes used to allow one binary to be the symlink target of hundreds of commands. Android does this for most common shell commands. Toybox and busybox are examples of such implementations. https://github.com/landley/toybox https://github.com/landley/toybox https://en.m.wikipedia.org/wiki/BusyBox https://en.m.wikipedia.org/wiki/BusyBox
- mistercow 2y agoAlso if you want a program to call itself, which is sometimes useful, this way lets you actually call the same program, rather than assuming the name and path.
- akira2501 2y agoBeware TOC TOU problems when doing this.
- fallingsquirrel 2y agoYou can do this without assuming the name by execing /proc/$PID/exe. Then you're not vulnerable to the argv[0] spoofing described in the article. (But of course since argv[0] does exist, you should set it properly and pass through your own argv[0] unchanged.)
- duped 2y agoDon't do this - if you (reliably) want the path to the current executable there is no portable way to do it, but on Linux you need to readlink /proc/self/exe and on MacOS you call _NSGetExecutablePath. I forget the API on Windows.
- kelsey98765431 2y agoThis is how busybox works in 'shim' mode. I am not however concerned with the security argument here, if you have the ability to run code you have the ability to do n to the power of x insidious things, and arg[0] abuse is just one of dozens, (hundreds?) of vectors or useful building blocks in an attack. if we are suddenly giving a shit about security on nixens, we should be looking at deeper SELinux rollouts (ease of use for sysadmins and maintainers so we never see permissive mode instead of just applying the difficult to remember command that will patch your policy settings. We need root capabilities to continue to be separated in the kernel access control scheme and probably we need to start using namespaces much more liberally like projects like silverblue/bluefin which reimplement entire os stack as a series of containers. Stronger container foundations and ease of use for existing security mechanisms will take us much further than worrying about ANYTHING else in the ABI which by the way will never change as long as linus is alive, and he will live on forever as an LLM most likely with the amount of mailing list posts he has made over the years.
- josefx 2y agoMicrosoft defender using broken by design detection rules? One could almost think it is an anti virus program.
- JohnFen 2y ago> Today however, disk space is no longer considered an issue On desktop machines, perhaps, but this is certainly not true on all platforms Linux runs on.
- Suppafly 2y agoPlus the whole "space is not an issue" thing along with "you can just add more ram" is the reason everything is so bloated and slow even on well provisioned machines.
- Sohcahtoa82 2y agoThese days, Windows Calculator takes up more memory than mIRC. Tell me why a simple calculator app needs more memory than a complete multi-server implementation of the IRC protocol (including SSL/TLS), not to mention a full scripting engine.
- Suppafly 2y ago>These days, Windows Calculator takes up more memory than mIRC. I'd be somewhat surprised if that's actually true, but I haven't used mirc for years (started using hexchat once I was honest about the fact that I wasn't going to pay for mirc) but I think a lot of that is an inherent part of windows development now, basic c# projects with graphics end up being pretty big. Interestingly enough, the new windows calculator is mit licensed and on github. But also it has a lot more features than most people think, it's not a 'simple calculator app', it's a full featured graphing calculator even if most people don't use those features.
- Sohcahtoa82 2y ago> I'd be somewhat surprised if that's actually true It 100% is. I launched Calculator and according to the Processes tab in Task Manager, "Calculator" is using 31.2 MB of memory, and mIRC is taking 17.2 MB. That's with Calculator being freshly launched and no input given, compared to mIRC being connected to 1 server and in 8 channels. If I go to the Details tab, then the story it tells is even worse. I include several metrics here: Working Set: - Calculator: 91 MB - mIRC: 40 MB Memory (private working set): - Calculator: 30 MB - mIRC: 18 MB Memory (shared working set): - Calculator: 61 MB - mIRC: 23 MB Commit size: - Calculator: 67 MB - mIRC: 49 MB By basically every metric, mIRC uses less memory than Calculator. > it's not a 'simple calculator app', it's a full featured graphing calculator even if most people don't use those features. The only feature that should significantly impact the memory usage is the graphing. All its little measurement conversion options shouldn't take more than a few kilobytes. But even with the graphing, it's absurd that it takes more memory than the total memory I would have had in a 486 machine that could easily have run an app with the same features. > but I think a lot of that is an inherent part of windows development now, basic c# projects with graphics end up being pretty big. I suppose the price you pay for almost guaranteed memory safety and ease of development through abstractions means your base executable memory footprint includes an entire language runtime.
- keepamovin 2y agoThis is why we can't have nice things. Security footguns everywhere! I'm fascinated by the intersection of argv[0], and the execve behavior of replacing the calling program with the called one. Aside from that, I quite like argv[0], for a much more limited set of reasons than considered in this interesting and comprehensive article. I like the ability to "retitle" a process to put a useful, descriptive, or branded name in there to be seen by ps, et al. NodeJS also exposes this feature, but not quite as you might expect. Whereas in C, setting argv[0] from within the program's execution context will alter what is observed by ps, in NodeJS process.argv is just a descriptive getter. Setting its slots has no effect outside of its context. But this is where process.title steps in. Setting process.title allows you to (in an OS-dependent way) change the name reported in ps and similar tools. Read more here: https://nodejs.org/api/process.html#processtitle https://nodejs.org/api/process.html#processtitle Please don't kill argv[0], its lease hath all too short a date
- kelsey98765431 2y agoYour fascination is rewarded by reading the other man sections such as section three: https://linux.die.net/man/3/execve https://linux.die.net/man/3/execve If you already know about the additional man pages beyond user space, i cannot more strongly recommend diving into them. Additionally the gnu 'info coreutils' is a good place to start, as well as the glibc manual.
- andrewmcwatters 2y agoI wish amateurs would stop propagating the false idea that disk space and memory are cheap and not a problem.
- dotancohen 2y agoI also use argv[0] for the -h help text, to show examples how to use the command.
- anonymousiam 2y agoI've done this too, but you should remove the path elements from the argv[0] string before you include it in your error/help messages.
- jmholla 2y agoYou don't need to. Keeping them shows the user exactly how to call the program based on how they called it.
- Brian_K_White 2y agoSometimes you want it, sometimes you don't, so it needs to be in there, and sometimes you remove it yourself if your context of the moment doesn't want it. And neither the want-it nor the don't-want-it case is such an outlier that you can disregard and not serve that case. Sometimes you're talking to the user about general usage and the full path is a distracting detail and not the important part of the message. Sometimes the full path and truthful invoked filename are an unnecessary security disclosure like telling a web viewer details about the server. Sometimes the full path and truthful invoked filename is a necessary fact in debugging, or in errors, or even ordinary non-error logs that aren't public.
- st_goliath 2y agoThere is also a neat little BSD extension, also supported on a number of other Unix-like systems and GNU userspace (i.e. glibc, but also other libcs like Musl): extern char *__progname; which holds the program name without the (optional) invocation path in front of it. Basically the last path component of argv[0].
- dotancohen 2y agoNice, thank you.
- dcminter 2y agoThis lost me at "goes against modern design principles" without citing what principle(s) the author had in mind that would proscribe it.
- rpcope1 2y agoAlmost any time someone uses the words "legacy" or "modern" in the context of computers, it's a giveaway to me almost always someone has an axe to grind with few or no real deep substantive reasons. I typically read these as: "legacy" -> anything that has existed for more than a day that I don't understand and don't like that stops me from poorly reinventing the wheel "modern" -> anything that I dreamt up or heard some other hipster talk about recently that I got hyped about
- st_goliath 2y agoGiven the tone and assumptions the article makes, and the things that are explicitly explained, this seems to be one of those articles where a novice learnt something new and then decided to write an article about it, despite not having fully grasped the concept yet. As a result, the author has such strange, absolute positions, calling it a legacy that should be abolished (only tangentially knowing some actual use cases), or that strange quote about design principles. Despite all the talk about security, the whole debacle that argc can be 0 (and argv[0] can be NULL), is completely left aside. This has caused actual security issues quite recently[1]. [1] https://lwn.net/Articles/882799/ https://lwn.net/Articles/882799/
- gwbas1c 2y agoThe security issues the author points out later in the article do have merit. Unfortunately, the author shot their credibility in the foot by perseverating on use of argv[0]; instead of glossing over it and getting to the point.
- hiccuphippo 2y agoI would guess the modern principle of disregard for disk space or memory usage :(
- 2y ago
- skobes 2y ago"Windows’ own API calls for creating new processes (such as CreateProcess [6], ShellExecute [7]) do not allow you to set argv[0]: it sets it for you, based on how the path to the executable was provided." Isn't this contradicted by the docs? CreateProcess receives lpApplicationName and lpCommandLine, and they can be different.
- magicalhippo 2y agoNot the way I understand it. In the execv documentation[1], you pass the program name twice: int execv(const char *path, char *const argv[]); The argument path points to a pathname that identifies the new process image file. The argument argv is an array of character pointers to null-terminated strings. [..] The value in argv[0] should point to a filename string that is associated with the process being started by one of the exec functions. Windows does not allow you to do that, AFAIK. [1]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/execve.html https://pubs.opengroup.org/onlinepubs/9699919799/functions/e...
- skobes 2y ago> Windows does not allow you to do that, AFAIK. It does though, using the lpCommandLine parameter to CreateProcess as I said. CreateProcess("main.exe", "foobar", ...) argv[0] is "foobar"
- magicalhippo 2y agoI stand corrected. Been ages since I used Win32 API a lot, and I realized I can't recall using both of those arguments when calling CreateProcess.
- deleted 2y ago[deleted]
- DSMan195276 2y agoYeah they have this incorrect. if you provide `lpApplicationName` and `lpCommandLine` then the application name is not automatically added to the command line string, you have to add it yourself to the string provided as `lpCommandLine`. I checked and the docs for `CreateProcess` briefly mention this issue: > If both lpApplicationName and lpCommandLine are non-NULL, the null-terminated string pointed to by lpApplicationName specifies the module to execute, and the null-terminated string pointed to by lpCommandLine specifies the command line. The new process can use GetCommandLine to retrieve the entire command line. Console processes written in C can use the argc and argv arguments to parse the command line. _Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line._
- mannyv 2y ago"Remember, the safest computer is one that's turned off and unplugged."
- lanstin 2y agoThis article seems to be an example of how some common security practices are kind of surface level. If you want to limit what a box can access on the network, do it in the network. Why is security looking for bad urls in the argv; if you know they are bad just block them? Or better yet if they aren't good, don't allow them. And if you want to know what a process is doing, ask the kernel to log its syscalls. If you take away argv 0 you will lose some valuable stuff (cute little busybox links, error logs that have argv[0] in them, and attackers will just name payload.exe ls.exe. And if your network is allow all, they will still reach CNC or collector end point.
- dividuum 2y agoSeriously: Their reason is basically "argv[0] is bad because security snake oil software is garbage": 1) Oh no, the only protection is looking at argv[0]. What kind of clown software is that? Software that notably runs on an already compromised system.. 2) No need for argv[0] to fool software that concats argv values with spaces: just run 'curl -o "test.txt |grep" 1.1.1.1' 3) A long argument messes up telemetry? Let's hope that bucket doesn't have more holes.
- shermantanktop 2y agoThese are all very realistic examples. Should they happen? No, but reality is messy and imperfect. The crappy software you describe would not exist if there were great solutions in this space.
- Hizonner 2y agoThere are better solutions than that. Off the top of my head, on Linux, you could get what the article is asking for by doing a readlink on /proc/self/exe. The crappy software exists because the people who write it don't have any idea what they're doing. And the reason for that is that the people who found companies in the security space have discovered that nobody can tell whether their products really work or not, so they can save money on talent and training.
- 2y ago
- tantalor 2y agoThe name of something is not an intrinsic property.
- barelyauser 2y agoCan something posses extrinsic properties? Or are them a intrinsic property of external things?
- account42 2y agoAre you asking if a property being intrinsic or not is an intrinsic property of that property?
- samatman 2y agoYes. Not only is there a Wikipedia article on it, there's more than one. Here's the one covering science and engineering, which is the appropriate version for this discussion. https://en.wikipedia.org/wiki/Intrinsic_and_extrinsic_properties https://en.wikipedia.org/wiki/Intrinsic_and_extrinsic_proper...
- travisgriggs 2y ago> “Should a program be allowed to behave differently based on its name?” I don’t see why not. It’s allowed to behave differently based on the arguments that follow it. I personally think the genericity of including the program name itself as one of its own calling arguments is really meta cool.
- strawhatguy 2y agoYes, this is useful for backwards compat too, like bash with an 'sh' mode.
- Too 2y agoIf i download a new version of foo and rename my old version to foo_old_backup_2, should foo_old_backup_2 start behaving differently, just because it has a different name? NO THANKS! A program should be sandboxed from its environment, including how the user started it. How a user names and organizes his files is a matter between the user and the operating system, not something individual program should care about.
- account42 2y agoA lot of programs actually do need support files at specific locations (either full paths or relative to the executable) so you already don't get to abitrarily organize your program binaries any way you want (without adjusting the programs).
- alkonaut 2y agoReally the weirdness isn't that main is invoked with the program name as argv[0]. The weirdness comes not in main() but in execv. Shouldn't execv have just taken the user provided arguments, prepended the program name (as provided by the OS) and then invoke the main function of the program with that array? The busybox argument or shutdown/reboot explains why the name of a symlinked binary is helpful as argv[0]. But does the busybox/shutdown case explain why the execv lets the user set the argv[0] value to anything other than what the path says?
- MPSimmons 2y ago
- omphaloskeptic 2y agoAlso, on POSIX systems, exec-ing a program with argv[0] starting with ‘-‘ will have it start as a login shell, which is a whole rabbit hole of its own. I’m sure it’s within the security model (and the linked article doesn’t really discuss the concept of OS security models), but it’s still a pretty big shift in behaviour just from adding a character to the argv[0] value
- fanf2 2y agoNo, that’s a property of how shells interpret argv[0], not a property of exec()
- theamk 2y agoThat's a weird take against argv[0] - all arguments are: "goes against modern design principles" and "can confuse programs which use argv[0] when they wanted "exec" instead" For the former, I don't see how this goes against modern principles - in presence of symlinks, it is pretty reasonable to want to know both "how was this program called", as well as "what's the actual executable we ended up with". And this does more than just giving multiple names to same program - for example python uses argv[0] to tell if it's inside virtualenv and adjust search paths accordingly. This makes it appear like there are multiple python installs on system, with no extra disk space taken. For the latter, yes, programs can have bugs and OSes can have non-obvious semantics, and if you are security software, it's very important to be aware about them. I would not mark "argv[0]" as something especially bad from security perspective. All the author's examples would still be possible in hypothetical world where argv[0] is set by system - as nothing stops user from creating a symlink in temporary dir with deceiving name (spaces and quotes are OK in filenames!) and exec'ing it directly. Instead, fix your security software so it quotes argv values?
- cedilla 2y ago> all arguments are: "goes against modern design principles" And the key witness is systemd, which is too young to buy a beer - even in Germany.
- wietze 2y agoFrom a living-of-the-land perspective, having to symlink/hardlink/alias a command is much noisier - and thus easier to detect. So although you are right in saying it wouldn't completely solve the problem, making it a system responsibility would still significantly reduce the scope for abuse.
- KingOfCoders 2y agoI use argv[0] to monitor the binary by itself and restart when it has changed.
- actionfromafar 2y agoHow? Checking and storing a checksum, or just file change metadata?
- marcosdumay 2y agoWell, I'm not the GP , but probably with OS file change monitoring API, that changes for each OS but the maintream ones all have some.
- yjftsjthsd-h 2y agoSo obviously claiming that there's no good reason for process to read argv[0] is either demonstrating the author's ignorance or needs a much stronger defense; I'd be fascinated to hear how they think busybox should work on an OpenWrt box with a 16MB root filesystem. 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 ln /bin/curl ./some\ other\ name 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.
- kazinator 2y agoIf hard linking (no symbolic) is used to install the BusyBox commands, then instead of argv[0], BusyBox could use the platform-specific means of obtaining the executable name, and take the basename of that path. On Linux this means /proc/self/exe; _NSGetExecutablePath on Drawin; getexecname on Solaris; GetModuleFilename on Windows; ...
- mzs 2y agohttps://github.com/util-linux/util-linux/blob/master/login-utils/login.c#L1565 https://github.com/util-linux/util-linux/blob/master/login-u... edit: basically login(1) execes your shell with - prepended, so an example where POSIX expects this
- pie_flavor 2y agoAnother example program that reads argv[0] is Rustup, the version manager for Rust. Rust versions can be set per directory either as a machine-specific override or via a file. Rustup is symlinked to all the Rust commands like rustc and cargo, and when invoked as one of those commands, it checks what version it is supposed to be using, and then forwards to that version. I don't see how you'd do this without argv[0] (or a dozen slightly different pointlessly recompiled binaries).
- red_admiral 2y ago
- blenderob 2y ago> argv[0] is a relic of the past Busybox says hello. Seriously though, how is this on the front page? Both the premise and conclusions contradict the reality of how argv[0] is used with symbolic links and hard links.
- hinkley 2y ago> Today however, disk space is no longer considered an issue; Tell me you don’t use Docker without telling me you don’t use Docker. I’d argue the certutil problem the author mentions is a flaw in certutil, not argv’s fault. Doesn’t that mean it falls to symlinks as well? If you look at sudo, it’s generally deny by default. Rename a program all you want, you won’t get to use it unless you can overwrite a program that is in the sudoer file. So I don’t know what nonsense certutil is playing at if it’s using argv to do its job. That’s appalling.
- Arch-TK 2y agoFor a command line utility, argv[0] is nice to see in error messages (e.g. `./tool: fatal: Could not open './file' for reading`). When the shell combines stdout and stderr, it's easier to spot exactly what you just typed as argv[0] from all the other output. For most other things, definitely unnecessary.
- azlev 2y agoI don't think the argv was made with security in mind. If we want something to be used in security field, the design since day 0 should consider it. Trying to retrofit something will break a lot of things.
- js2 2y ago> Today however, disk space is no longer considered an issue; this is evidenced by macOS Sonoma, where shutdown and reboot are two separate executables. Try running `ls -li /usr/bin` on macOS and you might be surprised to learn that all of these are a single executable: DeRez, GetFileInfo, Rez, SetFile, SplitForks, ar, as, asa, ... yacc. There's 77 different entries in `/usr/bin` (including `git` and `python3`) that are all links to the same binary (`com.apple.dt.xcode_select.tool-shim`). It's a wrapper that implements the `xcode-select` concept to locate and run the real executable provided by either the Command Line Tools package or a particular Xcode version you may have installed. And that's not the only one. There's another 68 links starting with `binhex.pl` and ending with `zipdetails` that are a single 811 byte wrapper-script around perl. Altogether, I see that there are 26 different names that are multiply linked: ls -li /usr/bin | awk '{print $1}' | sort | uniq -c | sort -n | grep -v "\s*1\s" | wc -l Some of the other examples: less & more, bc & dc, atrm & batch, stat & readlink. Having a program behave dynamically based on argv[0] is a useful tool in the Unix toolbox. The alternative would be compiling 77 different versions of `tool-shim,` creating 68 different versions of that perl wrapper, etc. The `git` binary uses this concept too. You can create an executable named `git-foo`, put it anywhere in your PATH, and then call it as `git foo`. In the end, argv[0] is just an argument that can be used to improve CLI ergonomics and reduce code duplication. It's not solely about disk space. I think that makes it a more common and useful concept than you give it credit for. As to the rest of the post: I'm not really sure how argv[0] being in the caller's control is any different than the rest of the execution context being in the caller's control: the remaining arguments, the environment, limits on file descriptors, which file descriptors are open, the program's real and effective uid and gid, signals it might receive and so on. These all amount to untrusted input any executable has to be cognizant of, more or less so depending upon what privileges the executable has and what its goals are.
- cryptonector 2y agoBesides, disk space is not an issue, but container image size still can be an issue because those have to be copied around the network, and it's easy to have thousands of 10GB images consume more disk space than you might have thought you'd need.
- t43562 2y agoarg0 also contains the path from where the invoker invoked the binary so for me this enables all sorts of binaries that work out where their dependencies are relative to their original binary. That's extremely convenient because you can combine it with $PWD to find out the absolute path to the binary. One can then guess what the PYTHONPATH and LD_LIBRARY_PATH should be most of the time and save someone from having to set them. Obviously this is of most use when you're running something you've installed into /opt (e.g. /opt/myprog/bin, /opt/myprog/lib etc) or are running it from the source tree.
- account42 2y ago> arg0 also contains the path from where the invoker invoked the binary Not in general it doesn't. Convention for shells is to pass the string the user used to invoke the program which may be an absolute path, a relative path or just a filename resolved against $PATH. > this enables all sorts of binaries that work out where their dependencies are relative to their original binary You should use the OS-specific functions to retrieve the current executable path for that - GetModuleFileName(NULL, ...) on Windows and readlink("/proc/self/exe", ...) on Linux. For script look into your interpreter documentation - e.g. Bash has ${BASH_SOURCE[0]}. Unfortunately POSIX shell scripts are SOL and have to rely on $0 plus some $PATH searching.
- layer8 2y agoIf nothing else, argv[0] is useful for producing error messages that indicate the name of the executable that is outputting the message. It's probably a good idea to not have it settable to other values by the invoking process, as is generally the case on Windows (ignoring its Posix subsystem here).
- account42 2y agoYou can set the full command line on windows independently from the program path using the standard Win32 CreateProcess(Ex) functions. This includes the part that ends up in argv[0] with your usual C runtime (Windows itself only provides a string and leaves it up to the program to split into arguments which may or may not use the standard CommandLineToArgv* functions - the standard C runtime doesn't and has slightly different escaping rules).
- alerighi 2y ago> It's probably a good idea to not have it settable to other values by the invoking process, as is generally the case on Windows (ignoring its Posix subsystem here). Well there is an use case that I sometime use for setting argv[0]. Consider you want to run yourself as a subprocess. Why you want to do that? There are plenty of reasons, but in general the thing is that doing things after a fork() is not safe under some circumstances and thus sometimes you also want to exec yourself. A technique is to then call yourself using another name in argv[0] for then in the main take a different flow from the normal command line parting, without adding an argument that the user can specify if it know that it exists. Yes, I know that there are a ton of other methods to do the same thing (perhaps an environment variable, for example), but I find the method of argv[0] quite nice and simple to be fair.
- cryptonector 2y agoPlease no. If you want to know what a process is running, look carefully in `/proc` or use `lsof` or whatever, but no, please, `argv[0]` is super useful. I use it, lots of people use it. And it's well known that pstrings can be abused to hide things from `ps`, but so what, it's been that way for 4+ decades and it's a well-known "problem" (it's not a problem).
- tqwhite 2y agoargv[0] is a parameter. Like any user input, it should be treated skeptically. There is absolutely nothing wrong with allowing more than one way to invoke the same program. This article is simply silly. Fortunately, it will be ignored completely since acting on it would break the universe.
- CamJN 2y agoThis is near and dear to my heart. I wanted to make a utility to get the arguments of other processes, and found after looking that every single use of the KERN_PROCARGS2 sysctl (used on macOS) on the internet is wrong (they assume argv[0] is not an empty string), including Apple's and Google's. So after making my utility I also made a library out of it, both are bsd-3, but non-gratis: https://getargv.narzt.cam/ https://getargv.narzt.cam/
- Dwedit 2y agoHow about the part about knowing what the directory the executable was launched from? It could be different than the working directory.
- gorjusborg 2y agoWhy bother asking?
- hi-v-rocknroll 2y agoArguing against legacy quirks is arguing against compatibility and arguing for throwing away decades of code portability guarantees through 20/20 hindsight perfectionism failing to consider the costs and burdens of reimagining the world with bikeshedding rants.
- deleted 2y ago[deleted]
- PaulHoule 2y agoIt’s part of the shambolic world of Unix and C. But “worse is better!” A good language spec is laid out in a way that reads from front to back with minimized circularity. See Common Lisp, Java, Python, etc. As a kid in high school checking out Unix manuals and implementing many Unix tools in https://subethasoftware.com/2022/09/27/exploring-1984-os-9-on-a-64k-trs-80-color-computer-part-2/ https://subethasoftware.com/2022/09/27/exploring-1984-os-9-o... I struggled with K&R because of the circularity of the book, which was really an anomaly built into C, the culture of C, or both because C++ books still read this way. C had so many half-baked things, such as an otherwise clean parser that required access to the symbol table. And of course a general fast and looseness which lead to the buffer overflow problem. There were other languages which failed to solve the systems programming problem like PL/I and Ada, not to mention ISO Pascal which could have tried but didn’t. (Turbo Pascal proved it could have been done.) People took until 1990 or so to be able to write good language specs consistently, so we can forgive Unix but boy is it awful if you look closely at it. On the other hand, IBM never did make a universal OS for the “universal” 360, yet Unix proved to be adaptable for almost everything.
- zabzonk 2y agoi may have missed it, but where does the C Standard say anything about access to a symbol table? or even if such a thing exists. and as for IBM i managed to use all sorts of OSs in VMs on IBM hardware back in the 1980s. Which did you have problems with?
- PaulHoule 2y agoThe parser in C has to keep track of the symbol table to handle cases like typdef int myint; myint x; which is unusual among programming languages. Sure I used VM on IBM hardware in the 1980s and it was great. I also used timesharing systems on the PDP-8 (what atrocious hardware!), the PDP-11 and the PDP-10/20 in the 1970s. Although the 360 was superior in so many respects (except for the slow interrupt handling) it failed to break into the huge market for general-purpose timesharing to support software development and such (learning BASIC) until the time microcomputers came along and crushed the timesharing market. (PDP-10 was famously used to develop microcomputer software such as the original Microsoft BASIC and Infocom's z-machine games) Fred Brooks' project to develop an OS for the 360 was notoriously troubled and IBM belatedly turned to VM as a dark horse. Today it looks ahead of its time (as virtualization became mainstream on x86 in the 00's) but back then IBM was flailing and they wound up with a good software story by accident. It was not really their fault, people just didn't know how to make an OS and the most advanced thinking back then was monstrosities like MULTICS. It was Unix and VAX/VMS that pointed to what a general purpose OS would look like a few years later and there has been relatively little innovation since then because nobody can afford to rearchitect the user space. (e.g. no way you can take out the "bloat" because you'll have to put it back in to run the software you want) IBM's z-architecture (the other z) has a great software story today (even runs Linux) but it was not the Plan A or even the Plan B.
- jrockway 2y agoI think argv[0] is fine. It sounds like there is a lot of bad security scanning software that doesn't understand how the `exec` syscall works. That sounds like their problem and not a fundamental problem with argv[0]. Most people use argv[0] so they can do something like: $ mycommand help Type `mycommand foo bar` to foo bars. $ mycommand1.2.3 help Type `mycommand1.2.3 foo bar` to foo bars. This is admittedly less fun when mycommand is /home/jrockway/.cache/bazel/_bazel_jrockway/7f95bd5e6dcc2e75a861133ddc7aee82/execroot/_main/bazel-out/k8-fastbuild/mycommand/mycommand_/mycommand` however.
- denysvitali 2y agoI don't think argv[0] includes the full path (or at least some programming language strip the whole path and keep only the last part)
- denysvitali 2y agoI stand corrected - C, Go and Python are all consistent here and show the full path. I seem to recall there was a language that only provided the stripped part - but I guess my memory is failing me here. Sorry for the wrong information above.
- jrockway 2y agoLanguages might remove the dirname part, but argv[0] is not necessarily a path, it's just a string passed to the exec system call that is also passed to main(int argc, char *argv). While many languges don't call their main function that, somewhere in the runtime that's what it gets.
- account42 2y agoIt depends on the caller not on the language of the program being called. If you execute something via $PATH then most shells will only pass the command you typed and not the full path. Similarly, when you use a relative path like ./command then usually your argv0 will be that relative path and not the full path to the executable. So in practice argv may or may not be a full path and if it does not contain a slash then it generally isn't even a relative path (well, not relative to the current directory anyway). For the help case in gp I think it makes sens for programs to always strip away anything up to including the last slash from argv0.
- jujube3 2y agoProblem: virus scanning software on Windows is broken. Solution: we should not use argv[0]?
- gwbas1c 2y agoThe author's extensive criticisms of using argv[0] are a distraction from the main point of the article: Summary: By manipulating argv[0], a malicious program can hide what its doing in security logs. For example, a malicious program can make "curl -T secret.txt 123.45.67.89" look like "curl localhost | grep -T secret.txt 123.45.67.89" in security logs. A mallicious program can also use very large argv[0] values as a DOS attack on system logging; or to truncate malicious arguments. IMO, operating systems should block this practice. Unfortunately, the author's extensive criticism of programs reading argv[0] hurt the author's credibility before most people get to the real point of the article.
- account42 2y ago> IMO, operating systems should block this practice. The "look like" is not a problem with the OS but a problem with displaying an array as a space-separated string without sufficient quoting or escaping, making things ambiguous.
- halayli 2y ago> This seems like a questionable design decision. Should a program be allowed to behave differently based on its name? From a 2020s standpoint, this seems highly undesirable, as it makes software less predictable and goes against modern design principles. No it doesn't make software less predictable nor does it goes against modern design principles. argv has very handy use cases and can be used to provide better user experience. Unless you have evidence to back up your claims, you're just turning a subjective opinion to an objective one without any merit. Either way, it's software developer choice and irrelevant to the user as much as it is irrelevant to the user whether the developer prefers for(;;) over while(1).
- mzs 2y ago"A login shell is one whose first character of argument zero is a -"
- deleted 2y ago[deleted]
- kazinator 2y agoThe author doesn't seem to understand that argv[0] can be different due to, for instance, one executable implementing many programs, such as BusyBox and similar projects. While argv[0] is old, if you had to design it from scratch to day, it would still be a good idea to have the program invocation name as an argument. The idea that anything old must is historic quirk that we can today eliminate is flawed. Now argv[0] should not be relied upon for obtaining the executable name, except as a last resort if the program is built for platforms that don't have anything else. But if one executable has multiple program names via symlinks, only argv[0] will distinguish them.
- Brian_K_White 2y agoThis is stupid. argv0 is just some data like any other data. It's ridiculously useful aside from the obvious busybox style usage. It's huge to be able to have a pointer to the directory where the executable resides, so you can package other assets along side it and have it all work for free without a seperate configuration file or env variables etc. Or for debugging or even non-error logging. You might call a binary from more than one place by other means than symlinks or hard links. You might be running from different mounted filsystems, chroot or container environments etc. A symlink might be in the middle of the path and not the executable name itself. Similarly a mount point. It's just a random small useful tool like all others. Calling it some kind of security problem is like saying that screwdrivers are a security problem because aside from turning screws, some people can use screwdrivers to stab people, and we have nut drivers which can almost serve almost all the same needs for only a little extra work. If your context of the moment means you have a security concern where you shouldn't trust this bit of data as gospel for some reason, then don't. Treat it like user input and take whatever precautions and fallback measures and sanity checks make sense for you in whatever particular situation you are in. F-ing dumb.
- iainmerrick 2y agoIt's huge to be able to have a pointer to the directory where the executable resides, so you can package other assets along side it and have it all work for free without a seperate configuration file or env variables etc. Yes! I was surprised how far down I had to scroll to find somebody mentioning that one. How else can you write a reasonably robust script that actually, you know, does something? You almost always need to grab some known files by their paths relative to the script.
- account42 2y agoargv[0] isn't actually a great solution for that since it isn't required to contain any path and in practice won't depending how the program was called. Some languages don't provide a better solution but they should. For Bash there is ${BASH_SOURCE[0]}. For compiled executables you use OS-provided functions like GetModuleFileName(NULL, ...) on Windows and readlink("/proc/self/exe", ...) on Linux.
- remram 2y ago> From a 2020s standpoint, this seems highly undesirable, as it makes software less predictable and goes against modern design principles. This is not an argument at all, this is a statement that arguments exist. What are they? It's like saying we shouldn't do something because it's "against best practices". I'm asking why are other practices preferred...