All of lore.kernel.org
 help / color / mirror / Atom feed
* [pseudo][PATCH 0/4] Implement openat2 wrapper
@ 2026-01-15  1:09 Mark Hatle
  2026-01-15  1:10 ` [pseudo][PATCH 1/4] test-syscall: Remove build warning Mark Hatle
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Mark Hatle @ 2026-01-15  1:09 UTC (permalink / raw)
  To: yocto-patches; +Cc: seebs, richard.purdie

Since openat2 isn't a regular function on most systems, the implementation
is a bit strange.  We implement the openat2 function based on the existing
openat (in the future, we may be able to get rid of openat redirecting it
to this new openat2.)  Then instead of calling real_openat2, we call
real_syscall instead.

This appears to work where I've run it, but I don't have a very extensive
test case at this time.  Any review of this serious would be appreciated.

Mark Hatle (1):
  openat2: Implement openat2 wrapper

mark.hatle (3):
  test-syscall: Remove build warning
  ports/linux/pseudo_wrappers.c: Reorder the syscall operations
  ports/linux/pseudo_wrappers.c: Call the wrappers where possible

 ports/linux/openat2/guts/openat2.c | 186 +++++++++++++++++++++++++++++++++++--
 ports/linux/pseudo_wrappers.c      |  51 ++++++----
 test/test-syscall.c                |  12 ++-
 3 files changed, 215 insertions(+), 34 deletions(-)

-- 
1.8.3.1



^ permalink raw reply	[flat|nested] 5+ messages in thread

* [pseudo][PATCH 1/4] test-syscall: Remove build warning
  2026-01-15  1:09 [pseudo][PATCH 0/4] Implement openat2 wrapper Mark Hatle
@ 2026-01-15  1:10 ` Mark Hatle
  2026-01-15  1:10 ` [pseudo][PATCH 2/4] ports/linux/pseudo_wrappers.c: Reorder the syscall operations Mark Hatle
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Mark Hatle @ 2026-01-15  1:10 UTC (permalink / raw)
  To: yocto-patches; +Cc: seebs, richard.purdie

From: "mark.hatle" <mark.hatle@kernel.crashing.org>

Compiler is warning about argc and argv not being used.

Signed-off-by: mark.hatle <mark.hatle@kernel.crashing.org>
---
 test/test-syscall.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/test-syscall.c b/test/test-syscall.c
index 1e21525..9031766 100644
--- a/test/test-syscall.c
+++ b/test/test-syscall.c
@@ -11,7 +11,7 @@
 #include <string.h>
 #include <errno.h>
 
-int main(int argc, char *argv[]) {
+int main() {
     long rc = 0;
 
 #ifdef SYS_renameat2
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [pseudo][PATCH 2/4] ports/linux/pseudo_wrappers.c: Reorder the syscall operations
  2026-01-15  1:09 [pseudo][PATCH 0/4] Implement openat2 wrapper Mark Hatle
  2026-01-15  1:10 ` [pseudo][PATCH 1/4] test-syscall: Remove build warning Mark Hatle
@ 2026-01-15  1:10 ` Mark Hatle
  2026-01-15  1:10 ` [pseudo][PATCH 3/4] ports/linux/pseudo_wrappers.c: Call the wrappers where possible Mark Hatle
  2026-01-15  1:10 ` [pseudo][PATCH 4/4] openat2: Implement openat2 wrapper Mark Hatle
  3 siblings, 0 replies; 5+ messages in thread
From: Mark Hatle @ 2026-01-15  1:10 UTC (permalink / raw)
  To: yocto-patches; +Cc: seebs, richard.purdie

From: "mark.hatle" <mark.hatle@kernel.crashing.org>

The seccomp wrap always takes effect when pseudo is running, this will
prevent various behavior, even if pseudo is generally considered to be
disabled, but in memory.

The openat2 and renameat2 however should only run if pseudo is enabled.

Signed-off-by: mark.hatle <mark.hatle@kernel.crashing.org>
---
 ports/linux/pseudo_wrappers.c | 29 ++++++++++++++++-------------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/ports/linux/pseudo_wrappers.c b/ports/linux/pseudo_wrappers.c
index 6b54083..b486c34 100644
--- a/ports/linux/pseudo_wrappers.c
+++ b/ports/linux/pseudo_wrappers.c
@@ -65,19 +65,6 @@ syscall(long number, ...) {
 		return rc;
 	}
 
-#ifdef SYS_renameat2
-	/* concerns exist about trying to parse arguments because syscall(2)
-	 * specifies strange ABI behaviors. If we can get better clarity on
-	 * that, it could make sense to redirect to wrap_renameat2().
-	 */
-	if (number == SYS_renameat2) {
-		errno = ENOSYS;
-		return -1;
-	}
-#else
-	(void) number;
-#endif
-
 #ifdef SYS_seccomp
 	/* pseudo and seccomp are incompatible as pseudo uses different syscalls
 	 * so pretend to enable seccomp but really do nothing */
@@ -92,6 +79,10 @@ syscall(long number, ...) {
 	}
 #endif
 
+        if (pseudo_disabled) {
+                goto call_syscall;
+        }
+
 #ifdef SYS_openat2
 	/* concerns exist about trying to parse arguments because syscall(2)
 	 * specifies strange ABI behaviors. If we can get better clarity on
@@ -105,6 +96,18 @@ syscall(long number, ...) {
 	}
 #endif
 
+#ifdef SYS_renameat2
+	/* concerns exist about trying to parse arguments because syscall(2)
+	 * specifies strange ABI behaviors. If we can get better clarity on
+	 * that, it could make sense to redirect to wrap_renameat2().
+	 */
+	if (number == SYS_renameat2) {
+		errno = ENOSYS;
+		return -1;
+	}
+#endif
+
+call_syscall:
 	/* gcc magic to attempt to just pass these args to syscall. we have to
 	 * guess about the number of args; the docs discuss calling conventions
 	 * up to 7, so let's try that?
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [pseudo][PATCH 3/4] ports/linux/pseudo_wrappers.c: Call the wrappers where possible
  2026-01-15  1:09 [pseudo][PATCH 0/4] Implement openat2 wrapper Mark Hatle
  2026-01-15  1:10 ` [pseudo][PATCH 1/4] test-syscall: Remove build warning Mark Hatle
  2026-01-15  1:10 ` [pseudo][PATCH 2/4] ports/linux/pseudo_wrappers.c: Reorder the syscall operations Mark Hatle
@ 2026-01-15  1:10 ` Mark Hatle
  2026-01-15  1:10 ` [pseudo][PATCH 4/4] openat2: Implement openat2 wrapper Mark Hatle
  3 siblings, 0 replies; 5+ messages in thread
From: Mark Hatle @ 2026-01-15  1:10 UTC (permalink / raw)
  To: yocto-patches; +Cc: seebs, richard.purdie

From: "mark.hatle" <mark.hatle@kernel.crashing.org>

Using va args, call the wrapper when the syscall is invoked.  This will
allow us to implement the renameat2 and openat2 functions in the future
while keeping the syscall work unchanged.

The syscall -> wrappers should continue to be sparse into things we know
are outliers, as this could result in some maintenance work keeping the
argument processing sane.

Signed-off-by: mark.hatle <mark.hatle@kernel.crashing.org>
---
 ports/linux/pseudo_wrappers.c | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/ports/linux/pseudo_wrappers.c b/ports/linux/pseudo_wrappers.c
index b486c34..7c025ae 100644
--- a/ports/linux/pseudo_wrappers.c
+++ b/ports/linux/pseudo_wrappers.c
@@ -84,26 +84,31 @@ syscall(long number, ...) {
         }
 
 #ifdef SYS_openat2
-	/* concerns exist about trying to parse arguments because syscall(2)
-	 * specifies strange ABI behaviors. If we can get better clarity on
-	 * that, it could make sense to redirect to wrap_openat2().
-	 * There is a CVE patch (CVE-2025-45582) to tar 1.34 in Centos Stream which
+	/* There is a CVE patch (CVE-2025-45582) to tar 1.34 in Centos Stream which
 	 * uses syscall to access openat2() and breaks builds if we don't redirect.
 	 */
 	if (number == SYS_openat2) {
-		errno = ENOSYS;
-		return -1;
+		va_start(ap, number);
+		int dirfd = va_arg(ap, int);
+		const char * path = va_arg(ap, const char *);
+		void *how = va_arg(ap, void *);
+		size_t size = va_arg(ap, size_t);
+
+		return wrap_openat2(dirfd, path, how, size);
 	}
 #endif
 
 #ifdef SYS_renameat2
-	/* concerns exist about trying to parse arguments because syscall(2)
-	 * specifies strange ABI behaviors. If we can get better clarity on
-	 * that, it could make sense to redirect to wrap_renameat2().
-	 */
+        /* Call out wrapper, expanding the variable arguments first */
 	if (number == SYS_renameat2) {
-		errno = ENOSYS;
-		return -1;
+		va_start(ap, number);
+		int olddirfd = va_arg(ap, int);
+		const char * oldpath = va_arg(ap, const char *);
+		int newdirfd = va_arg(ap, int);
+		const char * newpath = va_arg(ap, const char *);
+		unsigned int flags = va_arg(ap, unsigned int);
+
+		return wrap_renameat2(olddirfd, oldpath, newdirfd, newpath, flags);
 	}
 #endif
 
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [pseudo][PATCH 4/4] openat2: Implement openat2 wrapper
  2026-01-15  1:09 [pseudo][PATCH 0/4] Implement openat2 wrapper Mark Hatle
                   ` (2 preceding siblings ...)
  2026-01-15  1:10 ` [pseudo][PATCH 3/4] ports/linux/pseudo_wrappers.c: Call the wrappers where possible Mark Hatle
@ 2026-01-15  1:10 ` Mark Hatle
  3 siblings, 0 replies; 5+ messages in thread
From: Mark Hatle @ 2026-01-15  1:10 UTC (permalink / raw)
  To: yocto-patches; +Cc: seebs, richard.purdie

From: Mark Hatle <mark.hatle@amd.com>

This wrapper is based on ports/linux/guts/openat.c wrapper.

The flag and mode semantics have been replaced with 'open_how'.
The new resolve items should work, but are not processed by pseudo.
The input value is simply passed to the openat2 syscall.

Signed-off-by: Mark Hatle <mark.hatle@amd.com>
---
 ports/linux/openat2/guts/openat2.c | 186 +++++++++++++++++++++++++++++++++++--
 ports/linux/pseudo_wrappers.c      |   5 +
 test/test-syscall.c                |  10 +-
 3 files changed, 187 insertions(+), 14 deletions(-)

diff --git a/ports/linux/openat2/guts/openat2.c b/ports/linux/openat2/guts/openat2.c
index da01b31..673d486 100644
--- a/ports/linux/openat2/guts/openat2.c
+++ b/ports/linux/openat2/guts/openat2.c
@@ -1,22 +1,188 @@
 /*
- * Copyright (c) 2026 Mark Hatle <mark.hatle@amd.com>; see
- * guts/COPYRIGHT for information.
+ * Copyright (c) 2008-2010, 2013 Wind River Systems
+ * Copyright (c) 2026 Yocto Project
+ * see guts/COPYRIGHT for information.
  *
  * SPDX-License-Identifier: LGPL-2.1-only
  *
+ * Note this file is based on ./ports/linux/guts/openat.c
+ *
  * int openat2(int dirfd, const char *path, struct open_how *how, size_t size)
  *	int rc = -1;
  */
+	struct stat64 buf;
+	int overly_magic_nonblocking = 0;
+	int existed = 1;
+	int save_errno;
+	sigset_t local_saved_sigmask;
+	struct open_how my_how;
+
+	/* Validate parameters */
+	if (!how || size < sizeof(struct open_how)) {
+		errno = EINVAL;
+		return -1;
+	}
+	if (size > sizeof(struct open_how)) {
+		errno = E2BIG;
+		return -1;
+	}
+
+	memcpy(&my_how, how, size);
+
+	/* mask out mode bits appropriately */
+	my_how.mode = my_how.mode & ~pseudo_umask;
+
+#if defined(PSEUDO_NO_REAL_AT_FUNCTIONS) || ! defined(SYS_openat2)
+	if (dirfd != AT_FDCWD) {
+		errno = ENOSYS;
+		return -1;
+	}
+#endif
+
+#ifdef PSEUDO_FORCE_ASYNC
+	/* Yes, I'm aware that every Linux system I've seen has
+	 * DSYNC and RSYNC being the same value as SYNC.
+	 */
 
-	(void) dirfd;
-	(void) path;
-	(void) how;
-	(void) size;
-	/* for now, let's try just failing out hard, and hope things retry with a
-	 * different syscall.
+	my_how.flags &= ~(O_SYNC
+#ifdef O_DIRECT
+		| O_DIRECT
+#endif
+#ifdef O_DSYNC
+		| O_DSYNC
+#endif
+#ifdef O_RSYNC
+		| O_RSYNC
+#endif
+	);
+#endif
+
+#ifdef O_TMPFILE
+	/* don't handle O_CREAT the same way if O_TMPFILE exists
+	 * and is set.
+	 */
+	if ((my_how.flags & O_TMPFILE) == O_TMPFILE) {
+		existed = 0;
+	} else
+#endif
+	/* if a creation has been requested, check whether file exists */
+	/* note "else" in #ifdef O_TMPFILE above */
+	if (my_how.flags & O_CREAT) {
+		save_errno = errno;
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+		if (my_how.flags & O_NOFOLLOW) {
+			rc = real___lxstat64(_STAT_VER, path, &buf);
+		} else {
+			rc = real___xstat64(_STAT_VER, path, &buf);
+		}
+#else
+		rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, (my_how.flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0);
+#endif
+		existed = (rc != -1);
+		if (!existed)
+			pseudo_debug(PDBGF_FILE, "openat2_creat: %s -> 0%lld\n", path, my_how.mode);
+		errno = save_errno;
+	}
+
+	/* if a pipe is opened without O_NONBLOCK, for only reading or
+	 * only writing, it can block forever. We need to do extra magic
+	 * in that case...
 	 */
-	errno = ENOSYS;
-	rc = -1;
+	if (!(my_how.flags & O_NONBLOCK) && ((my_how.flags & (O_WRONLY | O_RDONLY | O_RDWR)) != O_RDWR)) {
+		save_errno = errno;
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+		if (my_how.flags & O_NOFOLLOW) {
+			rc = real___lxstat64(_STAT_VER, path, &buf);
+		} else {
+			rc = real___xstat64(_STAT_VER, path, &buf);
+		}
+#else
+		rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, (my_how.flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0);
+#endif
+		if (rc != -1 && S_ISFIFO(buf.st_mode)) {
+			overly_magic_nonblocking = 1;
+		}
+	}
+
+	/* this is a horrible special case and i do not know whether it will work */
+	if (overly_magic_nonblocking) {
+		pseudo_droplock();
+		sigprocmask(SIG_SETMASK, &pseudo_saved_sigmask, &local_saved_sigmask);
+	}
+	/* because we are not actually root, secretly mask in 0600 to the
+	 * underlying mode.  The ", 0" is because the only time mode matters
+	 * is if a file is going to be created, in which case it's
+	 * not a directory.
+	 */
+#if defined(PSEUDO_NO_REAL_AT_FUNCTIONS) || ! defined(SYS_openat2)
+	pseudo_debug(PDBGF_SYSCALL, "openat2, calling open.\n");
+	rc = real_open(path, my_how.flags, PSEUDO_FS_MODE(my_how.mode, 0));
+#else
+	/* openat2 in glibc is still rare, so directly call the syscall for now */
+# if 1
+	pseudo_debug(PDBGF_SYSCALL, "openat2, calling syscall.\n");
+	rc = real_syscall(SYS_openat2, dirfd, path, how, size);
+# else
+	pseudo_debug(PDBGF_SYSCALL, "openat2, calling openat2.\n");
+	rc = real_openat2(dirfd, path, how, size);
+# endif
+#endif
+	if (overly_magic_nonblocking) {
+		save_errno = errno;
+		sigprocmask(SIG_SETMASK, &local_saved_sigmask, NULL);
+		/* well this is a problem. we can't NOT proceed; we may have
+		 * already opened the file! we can't even return up the call
+		 * stack to stuff that's going to try to drop the lock.
+		 */
+		if (pseudo_getlock()) {
+			pseudo_diag("PANIC: after opening a readonly/writeonly FIFO (path '%s', fd %d, errno %d, saved errno %d), could not regain lock. unrecoverable. sorry. bye.\n",
+				path, rc, errno, save_errno);
+			abort();
+		}
+		errno = save_errno;
+	}
+
+	if (rc != -1) {
+		save_errno = errno;
+		int stat_rc;
+#ifdef O_TMPFILE
+		/* in O_TMPFILE case, nothing gets put in the
+		 * database, because there's no directory entries for
+		 * the file yet.
+		 */
+		if ((my_how.flags & O_TMPFILE) == O_TMPFILE) {
+			real_fchmod(rc, PSEUDO_FS_MODE(my_how.mode, 0));
+			errno = save_errno;
+			return rc;
+		}
+#endif
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+		if (my_how.flags & O_NOFOLLOW) {
+			stat_rc = real___lxstat64(_STAT_VER, path, &buf);
+		} else {
+			stat_rc = real___xstat64(_STAT_VER, path, &buf);
+		}
+#else
+		stat_rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, (my_how.flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0);
+#endif
+
+		pseudo_debug(PDBGF_FILE, "openat2(path %s), flags %lld, stat rc %d, stat mode %o\n",
+			path, my_how.flags, stat_rc, buf.st_mode);
+		if (stat_rc != -1) {
+			buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, my_how.mode);
+			if (!existed) {
+				real_fchmod(rc, PSEUDO_FS_MODE(my_how.mode, 0));
+				// file has no path, but has been created
+				pseudo_client_op(OP_CREAT, 0, -1, dirfd, path, &buf);
+			}
+				pseudo_client_op(OP_OPEN, PSEUDO_ACCESS(my_how.flags), rc, dirfd, path, &buf);
+		} else {
+			pseudo_debug(PDBGF_FILE, "openat2 (fd %d, path %d/%s, flags %lld) succeeded, but stat failed (%s).\n",
+				rc, dirfd, path, my_how.flags, strerror(errno));
+			pseudo_client_op(OP_OPEN, PSEUDO_ACCESS(my_how.flags), rc, dirfd, path, 0);
+		}
+		errno = save_errno;
+	}
 
 /*	return rc;
  * }
diff --git a/ports/linux/pseudo_wrappers.c b/ports/linux/pseudo_wrappers.c
index 7c025ae..82cfab4 100644
--- a/ports/linux/pseudo_wrappers.c
+++ b/ports/linux/pseudo_wrappers.c
@@ -69,6 +69,7 @@ syscall(long number, ...) {
 	/* pseudo and seccomp are incompatible as pseudo uses different syscalls
 	 * so pretend to enable seccomp but really do nothing */
 	if (number == SYS_seccomp) {
+		pseudo_debug(PDBGF_SYSCALL, "syscall, faking seccomp.\n");
 		unsigned long cmd;
 		va_start(ap, number);
 		cmd = va_arg(ap, unsigned long);
@@ -88,6 +89,8 @@ syscall(long number, ...) {
 	 * uses syscall to access openat2() and breaks builds if we don't redirect.
 	 */
 	if (number == SYS_openat2) {
+		pseudo_debug(PDBGF_SYSCALL, "syscall, faking openat2.\n");
+
 		va_start(ap, number);
 		int dirfd = va_arg(ap, int);
 		const char * path = va_arg(ap, const char *);
@@ -101,6 +104,8 @@ syscall(long number, ...) {
 #ifdef SYS_renameat2
         /* Call out wrapper, expanding the variable arguments first */
 	if (number == SYS_renameat2) {
+		pseudo_debug(PDBGF_SYSCALL, "syscall, faking renameat2.\n");
+
 		va_start(ap, number);
 		int olddirfd = va_arg(ap, int);
 		const char * oldpath = va_arg(ap, const char *);
diff --git a/test/test-syscall.c b/test/test-syscall.c
index 9031766..58329dd 100644
--- a/test/test-syscall.c
+++ b/test/test-syscall.c
@@ -67,17 +67,19 @@ int main() {
 
     int fd;
     fd = syscall(SYS_openat2, AT_FDCWD, ".", &how, sizeof(how));
+    printf("diag: openat2: %d (%s)\n", fd, strerror(errno));
     if (fd == -1) {
         if (errno != ENOSYS) {
             printf("openat2: fail: function implemented: %s\n", strerror(errno));
             rc++;
         }
-        else
-            printf("openat2: pass\n");
+        else {
+            printf("openat2: fail: %s", strerror(errno));
+            rc++;
+	}
     }
     else {
-        printf("openat2: fail: function implemented\n");
-        rc++;
+        printf("openat2: pass\n");
     }
 
     close(fd);
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-01-15  1:10 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-15  1:09 [pseudo][PATCH 0/4] Implement openat2 wrapper Mark Hatle
2026-01-15  1:10 ` [pseudo][PATCH 1/4] test-syscall: Remove build warning Mark Hatle
2026-01-15  1:10 ` [pseudo][PATCH 2/4] ports/linux/pseudo_wrappers.c: Reorder the syscall operations Mark Hatle
2026-01-15  1:10 ` [pseudo][PATCH 3/4] ports/linux/pseudo_wrappers.c: Call the wrappers where possible Mark Hatle
2026-01-15  1:10 ` [pseudo][PATCH 4/4] openat2: Implement openat2 wrapper Mark Hatle

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.