Bug report for Bash (not completely reading the runned script)
First I would like to say that I'm not sure if this is a bug or a feature of Bash.
If it is a feature, please let me know how to turn it off; or better make it disabled by default...
The problem is that Bash does not read up the whole script which it is currently executing.
As a result of this, if we update the script file with a newer version while it is running, this may lead to unpredicted results.
Here is the proof-of-concept scenario:
1) The script initial version is "old.sh".
2) I run the script and it executes some commands which take very long time to complete (like some "rsync" which transfers a lot of data).
3) Meanwhile, I update the script with a newer version "new.sh" which is no logically different than the initial one. Just a comment is deleted. Here is a diff.
4) The already running "old.sh" script is supposed to finish up, and the next new call to it should execute the new "new.sh" version.
5) However, we end up in a situation where the script, which we replaced and was already running, suddenly totally misbehaves.
6) In our test scenario, the already running "old.sh" script deletes our "/home" directory which must never happen, having the Bash scripts' source code in mind.
The exact steps to reproduce the problem are:
Terminal-one$ cp old.sh running.sh
Terminal-one$ ./running.sh # this runs a long time
Terminal-two$ cp new.sh running.sh # while "running.sh" is running, we replace it with "new.sh"
Terminal-one$ # the output from "running.sh" is
r.m. -rf /home # Normally we should never end up here, deleting /home!
And if you look at the sources of "old.sh" and "new.sh", you will see that we really must never end up deleting /home, because 0 is never equal to 2.
Why does this happen?
Bash always read()'s the file in chunks and executes the commands it just read().
Additionally, when Bash encounters a line which calls an external command, after the fork() for this command is finished, Bash seek()'s back
in the already open()'ed script file which it executes currently, re-read()s a chunk in the file from the seek()'ed position and continues to execute it.
At the very end of the script execution, Bash does another final read() on the opened script file, and if we appended something to the file,
it is executed too.
Read()'ing the script file in chunks and seek()'ing while it is opened, is completely unacceptable as this file may have changed in time,
because we updated it with a newer version.
If a Bash script finishes in a very short time, this is not an issue. However, there are many usages of Bash scripts which take quite a long
time to complete. Thus any file updates to such long-running Bash scripts impose a risk that something may go wrong.
Proposed solution:
* Read up the whole script file in memory, so that we have a consistent copy of it, regardless of whether we update the script file
during the execution. This is how ELF binaries are executed on Linux systems.
or
* If the above is not possible due to performance or other reasons, please introduce an option so that we can force Bash to copy
the script in memory before executing it.