mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
Mitigate PWD race condition in non-forking subshells
Virtual/non-forking subshells that change the present working directory (PWD) with 'cd' suffer from a serious race condition. The PWD is changed within the same process. This means it may not be possible to change back to the original PWD when exiting the subshell, as some other process may destroy the PWD or modify its permissions in the meantime. ksh did not handle this error condition at all, so, after exiting a subshell that invoked 'cd', it could silently end up running the script's following command(s) in the wrong directory. Which might be 'rm -rf *'. So, ouch. The proper and obvious fix is never to allow a virtual subshell to change the PWD, as it can never be guaranteed you can return to a previous directory. If the PWD is changed in a child process, there is no need to restore it in the parent process, and this whole problem is avoided. So subshells really should always fork on encountering a 'cd' command. But forking is slow. It is not uncommon for scripts to 'cd' in a subshell that is run repeatedly in a loop. There is also the issue of custom builtins that can be added to ksh via shared libraries. In the standard shell language, 'cd' is the only command that changes the PWD, so we could just make that command fork the subshell it is run from. But there's no telling what a custom builtin might do. So this commit implements a compromise that will not affect performance unless there is the pathological condition of a PWD that has been rendered inaccessible in some way: 1. When entering a virtual subshell, if the parent shell's PWD proves inaccessible upon saving it, the subshell will now fork into a separate process, avoiding the unrestorable PWD problem. 2. If some attack renders the parent shell's PWD unrestorable *after* ksh enters a virtual subshell, ksh will now error out when exiting it. There is nothing else left to do then. Continuing would mean running arbitrary commands in the wrong PWD. src/cmd/ksh93/sh/subshell.c: - Put all the code/variables only needed for fchdir() behind '#if _lib_fchdir'. This makes it clearer what's what. (I don't know if there is still any system out there without fchdir(3); I haven't found any. The chdir(3) fallback version may be removed later as there is no way to make it remotely secure.) - Fix the attempt to use the O_PATH mode for open(2) as a fallback for nonexistent O_SEARCH on Linux. Define _GNU_SOURCE on Linux, or <fcntl.h> (which is included indirectly) won't define O_PATH. - Fix use of O_SEARCH. The code was simply wrong, repeating an open(".",O_RDONLY) instead. Since a nonexistent O_SEARCH is now redefined as either O_PATH or O_RDONLY, we can simply open(".",O_SEARCH) and be done with it. - Fix fatal error handling. Introduce fatal error condition for failure to fchdir(3) back to the parent's PWD; rename 'duped' to 'fatalerror' and use it for error numbers; save and restore errno on fatal error so the message will report the cause. (We must call errormsg() near the end of sh_subshell() to avoid crashes.) - If open(".",O_SEARCH) was not able get a file descriptor to our PWD on entry, then call sh_subfork() immediately before running the subshell commands. (Forking earlier causes a crash.) - When restoring the PWD, if fchdir(3) fails, do *not* fall back to chdir(3). We already know the PWD is inaccessible, so if chdir(3) "succeeds" then, it's very likely to be a substitute injected by an attacker. src/cmd/ksh93/bltins/cd_pwd.c: - If we don't have fchdir(3), then sh_subshell() must fall back to chdir(2) to restore the PWD. That is highly vulnerable, as a well-timed rename would allow an attacker to usurp the PWD. We can't do anything about that if some custom builtin changes the PWD, but we can at least make 'cd' always fork a subshell, which slows down ksh but removes the need for the parent shell ever to restore the PWD. (There is certainly no popular system where this is relevant and there might not be any such current system.) This commit adds no regression test because a portable regression test is not really doable. Different kernels, external /bin/pwd utilities, etc. all have quite different behaviour under the pathological condition of an inaccessible PWD, so both the before-fix and the after-fix behaviour differs. See link below. Resolves: https://github.com/ksh93/ksh/issues/141 Thanks to Stéphane Chazelas for the bug report.
This commit is contained in:
parent
4ae962aba6
commit
dd9bc22928
4 changed files with 70 additions and 18 deletions
|
@ -17,4 +17,4 @@
|
|||
* David Korn <dgk@research.att.com> *
|
||||
* *
|
||||
***********************************************************************/
|
||||
#define SH_RELEASE "93u+m 2020-09-30"
|
||||
#define SH_RELEASE "93u+m 2020-10-06"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue