Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zfs_mkdir() should require the caller to specify the directory name length #15705

Open
jharmening opened this issue Dec 23, 2023 · 2 comments
Open
Labels
Type: Defect Incorrect behavior (e.g. crash, hang)

Comments

@jharmening
Copy link
Contributor

System information

Type Version/Name
Distribution Name FreeBSD
Distribution Version
Kernel Version 15.0-CURRENT
Architecture amd64
OpenZFS Version

Describe the problem you're observing

zfs_mkdir() currently takes the name of the new directory as the 'dirname' parameter and obtains the length via strlen().
This is a potential security issue and is inefficient in cases for which the length is already known, but it also causes a functional issue with unionfs on FreeBSD: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=275871

Since unionfs can create shadow directories as part of its lookup operation, the struct componentname * it provides to VOP_MKDIR() may have come from vfs_lookup(), in which case cnp->cn_nameptr will contain the remaining portion of the entire lookup path, delimited by '/'. cnp->cn_namelen will, however, correctly reflect the length of only the current path component. But zfs_freebsd_mkdir() doesn't respect cnp->cn_namelen because zfs_mkdir() doesn't make that possible. This results in incorrectly-named directories containing "/" which breaks pathname resolution as described in the bug report.

Describe how to reproduce the problem

zfs create zroot/var/repro
mkdir -p /var/repro/z/a
echo > /var/repro/z/a/b
mkdir -p /var/repro/y
mount_unionfs /var/repro/y /var/repro/z
echo "blah" > /var/repro/z/a/b

-> Observe the presence of a non-deletable file named "a/b" in /var/repro/y

While this can be worked around easily enough on the unionfs side of things, changing zfs_mkdir() to accept a length parameter seems like the better long-term fix.

Include any warning/errors/backtraces from the system logs

@jharmening jharmening added the Type: Defect Incorrect behavior (e.g. crash, hang) label Dec 23, 2023
@lundman
Copy link
Contributor

lundman commented Dec 23, 2023

Just so I understand,

zfs_vnop_mkdir() is called with the OS side parameters - whatever they may be, which will include struct componentname and cnp->cn_namelen as part of the wrapper layer (fbsd, macos, window at least, I think zpl_file has something similar).

But then it calls zfs_mkdir() where it drops struct componentname parameter, and you propose we keep it?

@jharmening
Copy link
Contributor Author

Well, I'm really proposing that zfs_mkdir (which appears to be shared between the Linux and FreeBSD implementations) be changed to accept the length of 'dirname' as an explicit parameter instead of obtaining it by calling strlen(). The FreeBSD implementation in zfs_freebsd_mkdir() would pass ap->a_cnp->cn_namelen for this new param.

It does seem as though a fair amount of plumbing work would be needed for this change: For example, zfs_dirent_lock() would need to take the length and use it to set dl->dl_namesize immediately while using some other means besides dl->dl_namesize == 0 to determine whether dl->dl_name has been copied. But (naively at least) it seems as though this could make things better overall, by eliminating the need to call strlen() in so many places and reducing the risk of buffer overflow in case someone fails to properly NUL-terminate.

freebsd-git pushed a commit to freebsd/freebsd-src that referenced this issue Feb 18, 2024
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().

openzfs/zfs#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
MFC after:	2 weeks
Differential Revision: https://reviews.freebsd.org/D43818
freebsd-git pushed a commit to freebsd/freebsd-src that referenced this issue Mar 4, 2024
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().

openzfs/zfs#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 a2ddbe0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Defect Incorrect behavior (e.g. crash, hang)
Projects
None yet
Development

No branches or pull requests

2 participants