git: c18e6a5a5c63 - stable/14 - unionfs: work around underlying FS failing to respect cn_namelen

From: Jason A. Harmening <jah_at_FreeBSD.org>
Date: Mon, 04 Mar 2024 18:51:46 UTC
The branch stable/14 has been updated by jah:

URL: https://cgit.FreeBSD.org/src/commit/?id=c18e6a5a5c63d85d664d60dc3bf0232ea21bf8d2

commit c18e6a5a5c63d85d664d60dc3bf0232ea21bf8d2
Author:     Jason A. Harmening <jah@FreeBSD.org>
AuthorDate: 2023-12-24 04:48:19 +0000
Commit:     Jason A. Harmening <jah@FreeBSD.org>
CommitDate: 2024-03-04 18:31:25 +0000

    unionfs: work around underlying FS failing to respect cn_namelen
    
    unionfs_mkshadowdir() may be invoked on a non-leaf pathname component
    during lookup, in which case the NUL terminator of the pathname buffer
    will be well beyond the end of the current component.  cn_namelen in
    this case will still (correctly) indicate the length of only the
    current component, but ZFS in particular does not currently respect
    cn_namelen, leading to the creation on inacessible files with slashes
    in their names.  Work around this behavior by temporarily NUL-
    terminating the current pathname component for the call to VOP_MKDIR().
    
    https://github.com/openzfs/zfs/issues/15705 has been filed to track
    a proper upstream fix for the issue at hand.
    
    PR:             275871
    Reported by:    Karlo Miličević <karlo98.m@gmail.com>
    Tested by:      Karlo Miličević <karlo98.m@gmail.com>
    Reviewed by:    kib, olce
    Differential Revision: https://reviews.freebsd.org/D43818
    
    (cherry picked from commit a2ddbe019d51b35f9da2cb5ddca8c69f0ee422da)
---
 sys/fs/unionfs/union_subr.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/sys/fs/unionfs/union_subr.c b/sys/fs/unionfs/union_subr.c
index c841136e42d5..469ba0775e7c 100644
--- a/sys/fs/unionfs/union_subr.c
+++ b/sys/fs/unionfs/union_subr.c
@@ -918,7 +918,24 @@ unionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *udvp,
 		goto unionfs_mkshadowdir_abort;
 	unionfs_create_uppervattr_core(ump, &lva, &va, td);
 
+	/*
+	 * Temporarily NUL-terminate the current pathname component.
+	 * This function may be called during lookup operations in which
+	 * the current pathname component is not the leaf, meaning that
+	 * the NUL terminator is some distance beyond the end of the current
+	 * component.  This *should* be fine, as cn_namelen will still
+	 * correctly indicate the length of only the current component,
+	 * but ZFS in particular does not respect cn_namelen in its VOP_MKDIR
+	 * implementation
+	 * Note that this assumes nd.ni_cnd.cn_pnbuf was allocated by
+	 * something like a local namei() operation and the temporary
+	 * NUL-termination will not have an effect on other threads.
+	 */
+	char *pathend = &nd.ni_cnd.cn_nameptr[nd.ni_cnd.cn_namelen];
+	char pathterm = *pathend;
+	*pathend = '\0';
 	error = VOP_MKDIR(udvp, &uvp, &nd.ni_cnd, &va);
+	*pathend = pathterm;
 
 	if (!error) {
 		unionfs_node_update(unp, uvp, td);