svn commit: r366022 - in head/sys: kern sys

Konstantin Belousov kib at FreeBSD.org
Tue Sep 22 22:48:13 UTC 2020


Author: kib
Date: Tue Sep 22 22:48:12 2020
New Revision: 366022
URL: https://svnweb.freebsd.org/changeset/base/366022

Log:
  Add O_RESOLVE_BENEATH and AT_RESOLVE_BENEATH to mimic Linux' RESOLVE_BENEATH.
  
  It is like O_BENEATH, but disables to walk out of the subtree rooted
  in the starting directory. O_BENEATH does not care if path walks out
  if it returned.
  
  Requested by:	Dan Gohman <dev at sunfishcode.online>
  PR:	248335
  Reviewed by:	markj
  Tested by:	pho
  Sponsored by:	The FreeBSD Foundation
  MFC after:	1 week
  Differential revision:	https://reviews.freebsd.org/D25886

Modified:
  head/sys/kern/vfs_lookup.c
  head/sys/kern/vfs_syscalls.c
  head/sys/kern/vfs_vnops.c
  head/sys/sys/fcntl.h
  head/sys/sys/namei.h

Modified: head/sys/kern/vfs_lookup.c
==============================================================================
--- head/sys/kern/vfs_lookup.c	Tue Sep 22 22:43:32 2020	(r366021)
+++ head/sys/kern/vfs_lookup.c	Tue Sep 22 22:48:12 2020	(r366022)
@@ -428,6 +428,16 @@ namei_setup(struct nameidata *ndp, struct vnode **dpp,
 		if (error == 0)
 			ndp->ni_lcf |= NI_LCF_LATCH;
 	}
+	if (error == 0 && (cnp->cn_flags & RBENEATH) != 0) {
+		if (cnp->cn_pnbuf[0] == '/' ||
+		    (ndp->ni_lcf & NI_LCF_BENEATH_ABS) != 0) {
+			error = EINVAL;
+		} else if ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) == 0) {
+			ndp->ni_lcf |= NI_LCF_STRICTRELATIVE |
+			    NI_LCF_CAP_DOTDOT;
+		}
+	}
+
 	/*
 	 * If we are auditing the kernel pathname, save the user pathname.
 	 */

Modified: head/sys/kern/vfs_syscalls.c
==============================================================================
--- head/sys/kern/vfs_syscalls.c	Tue Sep 22 22:43:32 2020	(r366021)
+++ head/sys/kern/vfs_syscalls.c	Tue Sep 22 22:48:12 2020	(r366022)
@@ -124,6 +124,8 @@ at2cnpflags(u_int at_flags, u_int mask)
 	at_flags &= mask;
 	if ((at_flags & AT_BENEATH) != 0)
 		res |= BENEATH;
+	if ((at_flags & AT_RESOLVE_BENEATH) != 0)
+		res |= RBENEATH;
 	if ((at_flags & AT_SYMLINK_FOLLOW) != 0)
 		res |= FOLLOW;
 	/* NOFOLLOW is pseudo flag */
@@ -1503,11 +1505,13 @@ sys_linkat(struct thread *td, struct linkat_args *uap)
 	int flag;
 
 	flag = uap->flag;
-	if ((flag & ~(AT_SYMLINK_FOLLOW | AT_BENEATH)) != 0)
+	if ((flag & ~(AT_SYMLINK_FOLLOW | AT_BENEATH |
+	    AT_RESOLVE_BENEATH)) != 0)
 		return (EINVAL);
 
 	return (kern_linkat(td, uap->fd1, uap->fd2, uap->path1, uap->path2,
-	    UIO_USERSPACE, at2cnpflags(flag, AT_SYMLINK_FOLLOW | AT_BENEATH)));
+	    UIO_USERSPACE, at2cnpflags(flag, AT_SYMLINK_FOLLOW | AT_BENEATH |
+	    AT_RESOLVE_BENEATH)));
 }
 
 int hardlink_check_uid = 0;
@@ -1872,7 +1876,7 @@ kern_funlinkat(struct thread *td, int dfd, const char 
 restart:
 	bwillwrite();
 	NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 |
-	    at2cnpflags(flag, AT_BENEATH),
+	    at2cnpflags(flag, AT_BENEATH | AT_RESOLVE_BENEATH),
 	    pathseg, path, dfd, &cap_unlinkat_rights, td);
 	if ((error = namei(&nd)) != 0) {
 		if (error == EINVAL)
@@ -2075,7 +2079,7 @@ kern_accessat(struct thread *td, int fd, const char *p
 	struct nameidata nd;
 	int error;
 
-	if ((flag & ~(AT_EACCESS | AT_BENEATH)) != 0)
+	if ((flag & ~(AT_EACCESS | AT_BENEATH | AT_RESOLVE_BENEATH)) != 0)
 		return (EINVAL);
 	if (amode != F_OK && (amode & ~(R_OK | W_OK | X_OK)) != 0)
 		return (EINVAL);
@@ -2096,7 +2100,7 @@ kern_accessat(struct thread *td, int fd, const char *p
 		usecred = cred;
 	AUDIT_ARG_VALUE(amode);
 	NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF |
-	    AUDITVNODE1 | at2cnpflags(flag, AT_BENEATH),
+	    AUDITVNODE1 | at2cnpflags(flag, AT_BENEATH | AT_RESOLVE_BENEATH),
 	    pathseg, path, fd, &cap_fstat_rights, td);
 	if ((error = namei(&nd)) != 0)
 		goto out;
@@ -2387,11 +2391,12 @@ kern_statat(struct thread *td, int flag, int fd, const
 	struct nameidata nd;
 	int error;
 
-	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0)
+	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH |
+	    AT_RESOLVE_BENEATH)) != 0)
 		return (EINVAL);
 
 	NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_BENEATH |
-	    AT_SYMLINK_NOFOLLOW) | LOCKSHARED | LOCKLEAF |
+	    AT_RESOLVE_BENEATH | AT_SYMLINK_NOFOLLOW) | LOCKSHARED | LOCKLEAF |
 	    AUDITVNODE1, pathseg, path, fd, &cap_fstat_rights, td);
 
 	if ((error = namei(&nd)) != 0)
@@ -2710,7 +2715,8 @@ int
 sys_chflagsat(struct thread *td, struct chflagsat_args *uap)
 {
 
-	if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0)
+	if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH |
+	    AT_RESOLVE_BENEATH)) != 0)
 		return (EINVAL);
 
 	return (kern_chflagsat(td, uap->fd, uap->path, UIO_USERSPACE,
@@ -2743,7 +2749,7 @@ kern_chflagsat(struct thread *td, int fd, const char *
 
 	AUDIT_ARG_FFLAGS(flags);
 	NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(atflag, AT_SYMLINK_NOFOLLOW |
-	    AT_BENEATH) | AUDITVNODE1, pathseg, path, fd,
+	    AT_BENEATH | AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd,
 	    &cap_fchflags_rights, td);
 	if ((error = namei(&nd)) != 0)
 		return (error);
@@ -2838,7 +2844,8 @@ int
 sys_fchmodat(struct thread *td, struct fchmodat_args *uap)
 {
 
-	if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0)
+	if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH |
+	    AT_RESOLVE_BENEATH)) != 0)
 		return (EINVAL);
 
 	return (kern_fchmodat(td, uap->fd, uap->path, UIO_USERSPACE,
@@ -2871,7 +2878,7 @@ kern_fchmodat(struct thread *td, int fd, const char *p
 
 	AUDIT_ARG_MODE(mode);
 	NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
-	    AT_BENEATH) | AUDITVNODE1, pathseg, path, fd,
+	    AT_BENEATH | AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd,
 	    &cap_fchmod_rights, td);
 	if ((error = namei(&nd)) != 0)
 		return (error);
@@ -2966,7 +2973,8 @@ int
 sys_fchownat(struct thread *td, struct fchownat_args *uap)
 {
 
-	if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0)
+	if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH |
+	    AT_RESOLVE_BENEATH)) != 0)
 		return (EINVAL);
 
 	return (kern_fchownat(td, uap->fd, uap->path, UIO_USERSPACE, uap->uid,
@@ -2982,7 +2990,7 @@ kern_fchownat(struct thread *td, int fd, const char *p
 
 	AUDIT_ARG_OWNER(uid, gid);
 	NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
-	    AT_BENEATH) | AUDITVNODE1, pathseg, path, fd,
+	    AT_BENEATH | AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd,
 	    &cap_fchown_rights, td);
 
 	if ((error = namei(&nd)) != 0)
@@ -3334,13 +3342,14 @@ kern_utimensat(struct thread *td, int fd, const char *
 	struct timespec ts[2];
 	int error, flags;
 
-	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0)
+	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH |
+	    AT_RESOLVE_BENEATH)) != 0)
 		return (EINVAL);
 
 	if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0)
 		return (error);
 	NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
-	    AT_BENEATH) | AUDITVNODE1,
+	    AT_BENEATH | AT_RESOLVE_BENEATH) | AUDITVNODE1,
 	    pathseg, path, fd, &cap_futimes_rights, td);
 	if ((error = namei(&nd)) != 0)
 		return (error);
@@ -3835,7 +3844,7 @@ kern_frmdirat(struct thread *td, int dfd, const char *
 restart:
 	bwillwrite();
 	NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 |
-	    at2cnpflags(flag, AT_BENEATH),
+	    at2cnpflags(flag, AT_BENEATH | AT_RESOLVE_BENEATH),
 	    pathseg, path, dfd, &cap_unlinkat_rights, td);
 	if ((error = namei(&nd)) != 0)
 		goto fdout;
@@ -4320,7 +4329,8 @@ int
 sys_getfhat(struct thread *td, struct getfhat_args *uap)
 {
 
-	if ((uap->flags & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0)
+	if ((uap->flags & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH |
+	    AT_RESOLVE_BENEATH)) != 0)
 		return (EINVAL);
 	return (kern_getfhat(td, uap->flags, uap->fd, uap->path, UIO_USERSPACE,
 	    uap->fhp));
@@ -4339,7 +4349,7 @@ kern_getfhat(struct thread *td, int flags, int fd, con
 	if (error != 0)
 		return (error);
 	NDINIT_AT(&nd, LOOKUP, at2cnpflags(flags, AT_SYMLINK_NOFOLLOW |
-	    AT_BENEATH) | LOCKLEAF | AUDITVNODE1,
+	    AT_BENEATH | AT_RESOLVE_BENEATH) | LOCKLEAF | AUDITVNODE1,
 	    pathseg, path, fd, td);
 	error = namei(&nd);
 	if (error != 0)

Modified: head/sys/kern/vfs_vnops.c
==============================================================================
--- head/sys/kern/vfs_vnops.c	Tue Sep 22 22:43:32 2020	(r366021)
+++ head/sys/kern/vfs_vnops.c	Tue Sep 22 22:48:12 2020	(r366022)
@@ -200,6 +200,8 @@ open2nameif(int fmode, u_int vn_open_flags)
 	res = ISOPEN | LOCKLEAF;
 	if ((fmode & O_BENEATH) != 0)
 		res |= BENEATH;
+	if ((fmode & O_RESOLVE_BENEATH) != 0)
+		res |= RBENEATH;
 	if ((vn_open_flags & VN_OPEN_NOAUDIT) == 0)
 		res |= AUDITVNODE1;
 	if ((vn_open_flags & VN_OPEN_NOCAPCHECK) != 0)

Modified: head/sys/sys/fcntl.h
==============================================================================
--- head/sys/sys/fcntl.h	Tue Sep 22 22:43:32 2020	(r366021)
+++ head/sys/sys/fcntl.h	Tue Sep 22 22:48:12 2020	(r366022)
@@ -136,6 +136,9 @@ typedef	__pid_t		pid_t;
 #if __BSD_VISIBLE
 #define	O_VERIFY	0x00200000	/* open only after verification */
 #define	O_BENEATH	0x00400000	/* Fail if not under cwd */
+#define	O_RESOLVE_BENEATH 0x00800000	/* As O_BENEATH, but do not allow
+					   resolve to walk out of cwd even to
+					   return back */
 #endif
 
 /*
@@ -215,6 +218,9 @@ typedef	__pid_t		pid_t;
 #define	AT_SYMLINK_FOLLOW	0x0400	/* Follow symbolic link */
 #define	AT_REMOVEDIR		0x0800	/* Remove directory instead of file */
 #define	AT_BENEATH		0x1000	/* Fail if not under dirfd */
+#define	AT_RESOLVE_BENEATH	0x2000	/* As AT_BENEATH, but do not allow
+					   resolve to walk out of dirfd even
+					   to return back */
 #endif
 
 /*

Modified: head/sys/sys/namei.h
==============================================================================
--- head/sys/sys/namei.h	Tue Sep 22 22:43:32 2020	(r366021)
+++ head/sys/sys/namei.h	Tue Sep 22 22:48:12 2020	(r366022)
@@ -133,7 +133,8 @@ int	cache_fplookup(struct nameidata *ndp, enum cache_f
 #define	BENEATH		0x0080	/* No escape from the start dir */
 #define	LOCKSHARED	0x0100	/* Shared lock leaf */
 #define	NOFOLLOW	0x0000	/* do not follow symbolic links (pseudo) */
-#define	MODMASK		0x01fc	/* mask of operational modifiers */
+#define	RBENEATH	0x100000000ULL /* No escape, even tmp, from start dir */
+#define	MODMASK		0xf000001fcULL	/* mask of operational modifiers */
 /*
  * Namei parameter descriptors.
  *


More information about the svn-src-all mailing list