tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: compat_linux: open: Supporting O_NOATIME



Hi tech-kern@,

I've prepared a patch for this below, BUT, in retrospect, I am not sure how 
consequential this fix is. The privilege check exists only to prevent 
processes from bypassing file access time "atime" update if they don't 
own the file or are not root, but we don't plan to offer that behavior 
anyway, i.e. atime will always be updated regardless of whether a 
process passes or fails the checks.

In other words, the patch is mostly cosmetic for purposes of passing the 
LTP test. With that in mind, I came up with these available 
options based on my research:

1. Do nothing, Linux binaries that pass O_NOATIME will still work. 
Unless a program uses this API to validate ownership of files or root 
privileges (unlikely and weird), program correctness should not be 
affected.

2. Implement a naive check. My patch does that, it checks file ownership 
when O_NOATIME is set, then delegates to the native sys_open(at) 
implementation. This suffers from a potential TOCTOU, but keeps the 
compat_linux layer a thin shim. It has performance implications as it 
will resolve the path twice, which would be even worse for backup software 
than doing nothing.

3. Implement a more robust check: Based on my research, it seems a 
MODULE_HOOK is the right way to add compatibility code inside the 
native sys_open(at) path. This would solve issues in #2 but would turn 
compat_linux into more than a translation layer, which does not seem to 
be worth it in this case.

4. Maybe there are other options that don't suffer from issues in #2 and 
still get us to pass the LTP test, would be happy to look into those 
based on any feedback.

In closing, I did not actually submit a patch to GNATS as my personal 
assessment is that option #1 (doing nothing) is probably better, but 
would appreciate your comments otherwise, regardless, it has been a very 
useful learning exercise, I appreciated the well-written kernel 
developer manual pages :)


diff --git a/sys/compat/linux/common/linux_file.c b/sys/compat/linux/common/linux_file.c
index 66b95278e..42a79491a 100644
--- a/sys/compat/linux/common/linux_file.c
+++ b/sys/compat/linux/common/linux_file.c
@@ -233,10 +233,38 @@ linux_sys_open(struct lwp *l, const struct linux_sys_open_args *uap,
 		syscallarg(int) flags;
 		syscallarg(linux_umode_t) mode;
 	} */
-	int error, fl;
+	int error, fl, lflags, uid;
 	struct sys_open_args boa;
+	struct pathbuf *pb;
+	struct nameidata nd;
+	struct vattr va;
 
 	fl = linux_to_bsd_ioflags(SCARG(uap, flags));
+	lflags = SCARG(uap, flags);
+	uid = kauth_cred_geteuid(l->l_cred);
+
+	if (lflags & LINUX_O_NOATIME) {
+		error = pathbuf_copyin(SCARG(uap, path), &pb);
+		if (error)
+			return error;
+
+		NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, pb);
+		if ((error = namei(&nd)) == 0) {
+			if ((error = VOP_GETATTR(nd.ni_vp, &va, l->l_cred)))
+				goto out;
+
+			if (uid != va.va_uid &&
+			    kauth_authorize_generic(l->l_cred,
+				KAUTH_GENERIC_ISSUSER, NULL) != 0) {
+				error = EPERM;
+			}
+		out:
+			vput(nd.ni_vp);
+		}
+		pathbuf_destroy(pb);
+		if (error)
+			return error;
+	}
 
 	memset(&boa, 0, sizeof(boa));
 	SCARG(&boa, path) = SCARG(uap, path);
@@ -260,10 +288,55 @@ linux_sys_openat(struct lwp *l, const struct linux_sys_openat_args *uap,
 		syscallarg(int) flags;
 		syscallarg(linux_umode_t) mode;
 	} */
-	int error, fl;
+	int error, fl, lflags, uid;
 	struct sys_openat_args boa;
+	struct pathbuf *pb;
+	struct nameidata nd;
+	struct vattr va;
+	struct vnode *dvp = NULL;
+	const char *pathstring = NULL;
+	file_t *dfp = NULL;
 
 	fl = linux_to_bsd_ioflags(SCARG(uap, flags));
+	lflags = SCARG(uap, flags);
+	uid = kauth_cred_geteuid(l->l_cred);
+
+	if (lflags & LINUX_O_NOATIME) {
+		error = pathbuf_copyin(SCARG(uap, path), &pb);
+		if (error)
+			return error;
+
+		pathstring = pathbuf_stringcopy_get(pb);
+		if (SCARG(uap, fd) != AT_FDCWD && pathstring[0] != '/') {
+			if ((error = fd_getvnode(SCARG(uap, fd), &dfp)) != 0)
+				goto out2;
+			dvp = dfp->f_vnode;
+		}
+
+		NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, pb);
+		if (dvp != NULL)
+			NDAT(&nd, dvp);
+
+		if ((error = namei(&nd)) == 0) {
+			if ((error = VOP_GETATTR(nd.ni_vp, &va, l->l_cred)))
+				goto out1;
+
+			if (uid != va.va_uid &&
+			    kauth_authorize_generic(l->l_cred,
+				KAUTH_GENERIC_ISSUSER, NULL) != 0) {
+				error = EPERM;
+			}
+out1:
+			vput(nd.ni_vp);
+		}
+		if (dvp != NULL)
+			fd_putfile(SCARG(uap, fd));
+out2:
+		pathbuf_stringcopy_put(pb, pathstring);
+		pathbuf_destroy(pb);
+		if (error)
+			return error;
+	}
 
 	memset(&boa, 0, sizeof(boa));
 	SCARG(&boa, fd) = SCARG(uap, fd);


Home | Main Index | Thread Index | Old Index