The Case of an Empty Executable
I recently came across a short article written about a decade ago. It was a curious thing already as it was hosted in a user's home directory off a web server with the standard ~<username>
showing up in the URL. The important part that caught my eye was this:
The "true" program does nothing; it merely exits with a zero exit status. This can be done with an empty file that's marked executable, and that's what it was in the earliest unix system libraries.
Being a curious sort, and presented with an old mystery, I had to try out this little tidbit of information:
Sure enough, an empty file can be successfully run. It obviously doesn't have a known binary header, so it won't be interpreted as a valid executable on it's own. Even scripts rely on the shebang header ("#!") to be considered valid by the kernel, so something else has to be executing this. We can confirm the kernel isn't recognizing this by abusing "strace" into calling an explicit execve on the file:
|
|
This is exactly what I was expecting and the Kernel source reflects exactly what I'd expect. I'll leave it up to you test the results on an empty but otherwise valid shell script. With the kernel cleared of any odd behavior I was left with only one suspect. A little shell by the name of bash.
Bash is a coy devil with several different mechanisms built-in to execute a program. Likely one of these culprits are being used behind the scenes when we run a program. A quick trip the man page and a late night cup of coffee narrowed down my search to the following functions:
- command
- eval
- exec
I decided it was time to talk to each of them one by one and see what was up. Putting them under the bright light I was surprised that they all were telling the same story:
How sinister! Someone had gotten to them first, I have to go higher into their organization. This calls for... The Source. I quickly traverse into the builtin directory and identify the commonality "parse_and_execute". This is where it gets a little fuzzy as bash is a rather complicated code base and I didn't want to spend to much time on this in the middle of the night.
After parsing the file, it does seem to treat it as a script (as expected). There are two possibilities here and I didn't trace down which was true. Either "parse_and_execute" is simply returning with a success or it is sending the contents to "execute_command_internal", which in turn defaults to a successfully return value.
The motive remains unclear, but no harm seems to be getting done so I'm going to call this one case closed. It'd be interesting to see how other shells behave with empty files.