Current behavior
runtime_install_root_is_protected resolves paths lexically, which is correct for every spelling of a path but is not symlink-safe. Because the home-directory exemption is consulted before the protected-root list, a symlink the user owns is enough to escape it:
$ ln -s /etc "$HOME/link"
runtime_install_root_is_protected("$HOME/link/child") -> false (reported removable)
std::fs::canonicalize("$HOME/link") -> Ok("/etc")
The path is lexically inside $HOME, so the guard takes the exemption's early return and never consults the protected-root list, while the kernel lands in /etc.
Expected behavior
A path whose resolved location is inside a protected system root is refused, including when it reaches that location through a symlink.
Steps to reproduce
Create $HOME/link -> /etc, then ask the guard about $HOME/link/child.
Environment
- Observed on Linux against
main. Platform-independent logic.
Additional context
This is pre-existing and was not introduced by #491, which fixed a different hole (respelled paths such as /etc/, //etc, $HOME/../../etc). It is filed separately because #491's first draft carried a comment asserting that a symlink "can only land the kernel somewhere the lexical answer did not already call safe" — which is false, and is exactly this case. That comment was corrected, but the hole it described remains.
Closing it means deciding how much of a path to canonicalize. The reason lexical resolution was chosen is that these paths frequently do not exist yet — they come from a registry entry — so canonicalize has nothing to resolve. A plausible approach is canonicalizing the longest existing ancestor and appending the remainder, but that is a design decision rather than a small patch.
Current behavior
runtime_install_root_is_protectedresolves paths lexically, which is correct for every spelling of a path but is not symlink-safe. Because the home-directory exemption is consulted before the protected-root list, a symlink the user owns is enough to escape it:The path is lexically inside
$HOME, so the guard takes the exemption's early return and never consults the protected-root list, while the kernel lands in/etc.Expected behavior
A path whose resolved location is inside a protected system root is refused, including when it reaches that location through a symlink.
Steps to reproduce
Create
$HOME/link -> /etc, then ask the guard about$HOME/link/child.Environment
main. Platform-independent logic.Additional context
This is pre-existing and was not introduced by #491, which fixed a different hole (respelled paths such as
/etc/,//etc,$HOME/../../etc). It is filed separately because #491's first draft carried a comment asserting that a symlink "can only land the kernel somewhere the lexical answer did not already call safe" — which is false, and is exactly this case. That comment was corrected, but the hole it described remains.Closing it means deciding how much of a path to canonicalize. The reason lexical resolution was chosen is that these paths frequently do not exist yet — they come from a registry entry — so
canonicalizehas nothing to resolve. A plausible approach is canonicalizing the longest existing ancestor and appending the remainder, but that is a design decision rather than a small patch.