aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/vfs_cache.c
diff options
context:
space:
mode:
authorMateusz Guzik <mjg@FreeBSD.org>2020-08-22 06:56:04 +0000
committerMateusz Guzik <mjg@FreeBSD.org>2020-08-22 06:56:04 +0000
commit760a430bb3c3b0a4637b0a1abe9554c52fc49695 (patch)
treee5b9539857aa80510401b1c4fe494fb60125effe /sys/kern/vfs_cache.c
parent71bba112f6db4a5c7dbc28917554a6667a528eab (diff)
downloadsrc-760a430bb3c3b0a4637b0a1abe9554c52fc49695.tar.gz
src-760a430bb3c3b0a4637b0a1abe9554c52fc49695.zip
vfs: add a work around for vp_crossmp bug to realpath
The actual bug is not yet addressed as it will get much easier after other problems are addressed (most notably rename contract). The only affected in-tree consumer is realpath. Everyone else happens to be performing lookups within a mount point, having a side effect of ni_dvp being set to mount point's root vnode in the worst case. Reported by: pho
Notes
Notes: svn path=/head/; revision=364478
Diffstat (limited to 'sys/kern/vfs_cache.c')
-rw-r--r--sys/kern/vfs_cache.c27
1 files changed, 26 insertions, 1 deletions
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c
index 0610b685267c..5440ee3e1ac1 100644
--- a/sys/kern/vfs_cache.c
+++ b/sys/kern/vfs_cache.c
@@ -2811,6 +2811,7 @@ vn_fullpath_hardlink(struct thread *td, struct nameidata *ndp, char **retbuf,
size_t addend;
int error;
bool slash_prefixed;
+ enum vtype type;
if (*buflen < 2)
return (EINVAL);
@@ -2824,7 +2825,31 @@ vn_fullpath_hardlink(struct thread *td, struct nameidata *ndp, char **retbuf,
addend = 0;
vp = ndp->ni_vp;
- if (vp->v_type != VDIR) {
+ /*
+ * Check for VBAD to work around the vp_crossmp bug in lookup().
+ *
+ * For example consider tmpfs on /tmp and realpath /tmp. ni_vp will be
+ * set to mount point's root vnode while ni_dvp will be vp_crossmp.
+ * If the type is VDIR (like in this very case) we can skip looking
+ * at ni_dvp in the first place. However, since vnodes get passed here
+ * unlocked the target may transition to doomed state (type == VBAD)
+ * before we get to evaluate the condition. If this happens, we will
+ * populate part of the buffer and descend to vn_fullpath_dir with
+ * vp == vp_crossmp. Prevent the problem by checking for VBAD.
+ *
+ * This should be atomic_load(&vp->v_type) but it is ilegal to take
+ * an address of a bit field, even if said field is sized to char.
+ * Work around the problem by reading the value into a full-sized enum
+ * and then re-reading it with atomic_load which will still prevent
+ * the compiler from re-reading down the road.
+ */
+ type = vp->v_type;
+ type = atomic_load_int(&type);
+ if (type == VBAD) {
+ error = ENOENT;
+ goto out_bad;
+ }
+ if (type != VDIR) {
cnp = &ndp->ni_cnd;
addend = cnp->cn_namelen + 2;
if (*buflen < addend) {