<pre style='margin:0'>
Christopher Nielsen (mascguy) pushed a commit to branch master
in repository macports-legacy-support.

</pre>
<p><a href="https://github.com/macports/macports-legacy-support/commit/8ff11ede715a9bdb55d6f98ef7b2cf325d917e7d">https://github.com/macports/macports-legacy-support/commit/8ff11ede715a9bdb55d6f98ef7b2cf325d917e7d</a></p>
<pre style="white-space: pre; background: #F8F8F8"><span style='display:block; white-space:pre;color:#808000;'>commit 8ff11ede715a9bdb55d6f98ef7b2cf325d917e7d
</span>Author: Fred Wright <fw@fwright.net>
AuthorDate: Wed Jul 2 12:19:41 2025 -0700

<span style='display:block; white-space:pre;color:#404040;'>    statxx: Validate buffer address in 10.4 stat64() calls.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    This arranges to use the caller's stat64 buffer for the stat buffer,
</span><span style='display:block; white-space:pre;color:#404040;'>    thereby leveraging the OS's address validation, though it still needs
</span><span style='display:block; white-space:pre;color:#404040;'>    to validate the extra portion.  In most cases, the latter avoids the
</span><span style='display:block; white-space:pre;color:#404040;'>    full validation by virtue of lying within the same page.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    TESTED:
</span><span style='display:block; white-space:pre;color:#404040;'>    Tests pass on all platforms.
</span>---
 src/statxx.c | 149 ++++++++++++++++++++++++++++++++++++++---------------------
 src/util.h   |   4 +-
 2 files changed, 100 insertions(+), 53 deletions(-)

<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/statxx.c b/src/statxx.c
</span><span style='display:block; white-space:pre;color:#808080;'>index 7077afb..b3d79e0 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/statxx.c
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/statxx.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -246,91 +246,136 @@ fstatx_np(int fildes, struct stat *buf, filesec_t fsec)
</span> /*
  * This provides definitions for some 64-bit-inode function variants on 10.4.
  *
<span style='display:block; white-space:pre;background:#ffe0e0;'>- * For the *stat() functions, this simply involves translating the result of
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- * the 32-bit-inode variant.
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- *
</span>  * Providing a similar capability for directory-related functions would be
  * much more difficult, since the differently-formatted dirent struct is
  * provided via a pointer to an internal buffer, rather than one provided
  * by the caller.  Hence we don't do anything about those, for now.
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * For the *stat() functions, this simply involves translating the result of
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * the 32-bit-inode variant.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Since the caller-supplied stat64 buffer is larger than the stat buffer
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * needed by the syscall, we can use it directly for the syscall, thereby
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * (mostly) leveraging the OS address validation.  But since the fields
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * are in a different order, we can't directly reformat it in place, and
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * instead need to make a temporary copy as an intermediary.  Although this
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * seems like extra overhead, it's far less expensive than performing the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * buffer address validation in userspace.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * The "mostly" comes about because if a page boundary lands between the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * end of the stat and the end of the stat64, we haven't fully validated the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * buffer.  We call the validation function for extra portion, with the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * known valid start address as a hint, which will usually avoid the full
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * validation sequence.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * For some unknown reason, optimized code was sometimes screwing up the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * result until the extra clearing step was inserted, even though there's
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * no good reason why this should be necessary (there's no type punning
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * involved).
</span>  */
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <errno.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "util.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+typedef union stat_buf_u {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  struct stat s;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  struct stat64 s64;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+} stat_buf_t;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> /* Do a field-by-field copy from ino32 stat to ino64 stat. */
 /* Also provide passthrough for return value. */
 static int
<span style='display:block; white-space:pre;background:#ffe0e0;'>-convert_stat(const struct stat *in, struct stat64 *out, int status)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+convert_stat(int result, stat_buf_t *sb)
</span> {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_dev = in->st_dev;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_mode = in->st_mode;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_nlink = in->st_nlink;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_ino = in->st_ino;  /* Possible but unlikely overflow here */
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_uid = in->st_uid;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_gid = in->st_gid;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_rdev = in->st_rdev;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_atimespec = in->st_atimespec;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_mtimespec = in->st_mtimespec;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_ctimespec = in->st_ctimespec;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  struct stat stbuf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  static const struct stat64 s64zero = {0};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (MPLS_SLOWPATH(result)) return result;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (__mpls_check_access(&sb->s + 1, sizeof(sb->s64) - sizeof(sb->s),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      VM_PROT_WRITE, &sb->s)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    errno = EFAULT;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return -1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  stbuf = sb->s;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  /* Start with all-zero result (avoid weird optimizer bug) */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64 = s64zero;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_dev = stbuf.st_dev;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_mode = stbuf.st_mode;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_nlink = stbuf.st_nlink;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_ino = stbuf.st_ino;  /* Possible but unlikely overflow here */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_uid = stbuf.st_uid;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_gid = stbuf.st_gid;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_rdev = stbuf.st_rdev;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_atimespec = stbuf.st_atimespec;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_mtimespec = stbuf.st_mtimespec;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_ctimespec = stbuf.st_ctimespec;
</span>   /* The ino32 stat doesn't have birthtime, so use MIN(ctime, mtime) */
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  if (in->st_ctimespec.tv_sec < in->st_mtimespec.tv_sec
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-      || (in->st_ctimespec.tv_sec == in->st_mtimespec.tv_sec
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-          && in->st_ctimespec.tv_nsec < in->st_mtimespec.tv_nsec)) {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    out->st_birthtimespec = in->st_ctimespec;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (stbuf.st_ctimespec.tv_sec < stbuf.st_mtimespec.tv_sec
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      || (stbuf.st_ctimespec.tv_sec == stbuf.st_mtimespec.tv_sec
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          && stbuf.st_ctimespec.tv_nsec < stbuf.st_mtimespec.tv_nsec)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    sb->s64.st_birthtimespec = stbuf.st_ctimespec;
</span>   } else {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    out->st_birthtimespec = in->st_mtimespec;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    sb->s64.st_birthtimespec = stbuf.st_mtimespec;
</span>   }
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_size = in->st_size;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_blocks = in->st_blocks;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_blksize = in->st_blksize;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_flags = in->st_flags;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_gen = in->st_gen;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_lspare = 0;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_qspare[0] = 0;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  out->st_qspare[1] = 0;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return status;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_size = stbuf.st_size;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_blocks = stbuf.st_blocks;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_blksize = stbuf.st_blksize;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_flags = stbuf.st_flags;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_gen = stbuf.st_gen;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  /* Copy the "do not use" spares verbatim as well */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_lspare = stbuf.st_lspare;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_qspare[0] = stbuf.st_qspare[0];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  sb->s64.st_qspare[1] = stbuf.st_qspare[1];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return result;
</span> }
 
 int
 stat$INODE64(const char *__restrict path, struct stat64 *buf)
 {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  struct stat stbuf;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return convert_stat(&stbuf, buf, stat(path, &stbuf));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  stat_buf_t *sb = (stat_buf_t *) buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return convert_stat(stat(path, &sb->s), sb);
</span> }
 
 int
 lstat$INODE64(const char *__restrict path, struct stat64 *buf)
 {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  struct stat stbuf;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return convert_stat(&stbuf, buf, lstat(path, &stbuf));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  stat_buf_t *sb = (stat_buf_t *) buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return convert_stat(lstat(path, &sb->s), sb);
</span> }
 
 int
 fstat$INODE64(int fildes, struct stat64 *buf)
 {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  struct stat stbuf;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return convert_stat(&stbuf, buf, fstat(fildes, &stbuf));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  stat_buf_t *sb = (stat_buf_t *) buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return convert_stat(fstat(fildes, &sb->s), sb);
</span> }
 
 int
 statx_np$INODE64(const char *__restrict path, struct stat64 *buf,
                  filesec_t fsec)
 {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  struct stat stbuf;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return convert_stat(&stbuf, buf, statx_np(path, &stbuf, fsec));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  stat_buf_t *sb = (stat_buf_t *) buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return convert_stat(statx_np(path, &sb->s, fsec), sb);
</span> }
 
 int
 lstatx_np$INODE64(const char *__restrict path, struct stat64 *buf,
                   filesec_t fsec)
 {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  struct stat stbuf;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return convert_stat(&stbuf, buf, lstatx_np(path, &stbuf, fsec));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  stat_buf_t *sb = (stat_buf_t *) buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return convert_stat(lstatx_np(path, &sb->s, fsec), sb);
</span> }
 
 int
 fstatx_np$INODE64(int fildes, struct stat64 *buf, filesec_t fsec)
 {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  struct stat stbuf;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return convert_stat(&stbuf, buf, fstatx_np(fildes, &stbuf, fsec));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  stat_buf_t *sb = (stat_buf_t *) buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return convert_stat(fstatx_np(fildes, &sb->s, fsec), sb);
</span> }
 
 #if __MPLS_HAVE_STAT64
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -338,43 +383,43 @@ fstatx_np$INODE64(int fildes, struct stat64 *buf, filesec_t fsec)
</span> int
 stat64(const char *__restrict path, struct stat64 *buf)
 {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  struct stat stbuf;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return convert_stat(&stbuf, buf, stat(path, &stbuf));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  stat_buf_t *sb = (stat_buf_t *) buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return convert_stat(stat(path, &sb->s), sb);
</span> }
 
 int
 lstat64(const char *__restrict path, struct stat64 *buf)
 {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  struct stat stbuf;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return convert_stat(&stbuf, buf, lstat(path, &stbuf));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  stat_buf_t *sb = (stat_buf_t *) buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return convert_stat(lstat(path, &sb->s), sb);
</span> }
 
 int
 fstat64(int fildes, struct stat64 *buf)
 {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  struct stat stbuf;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return convert_stat(&stbuf, buf, fstat(fildes, &stbuf));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  stat_buf_t *sb = (stat_buf_t *) buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return convert_stat(fstat(fildes, &sb->s), sb);
</span> }
 
 int
 statx64_np(const char *__restrict path, struct stat64 *buf, filesec_t fsec)
 {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  struct stat stbuf;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return convert_stat(&stbuf, buf, statx_np(path, &stbuf, fsec));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  stat_buf_t *sb = (stat_buf_t *) buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return convert_stat(statx_np(path, &sb->s, fsec), sb);
</span> }
 
 int
 lstatx64_np(const char *__restrict path, struct stat64 *buf, filesec_t fsec)
 {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  struct stat stbuf;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return convert_stat(&stbuf, buf, lstatx_np(path, &stbuf, fsec));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  stat_buf_t *sb = (stat_buf_t *) buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return convert_stat(lstatx_np(path, &sb->s, fsec), sb);
</span> }
 
 int
 fstatx64_np(int fildes, struct stat64 *buf, filesec_t fsec)
 {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-  struct stat stbuf;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-  return convert_stat(&stbuf, buf, fstatx_np(fildes, &stbuf, fsec));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  stat_buf_t *sb = (stat_buf_t *) buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return convert_stat(fstatx_np(fildes, &sb->s, fsec), sb);
</span> }
 
 #endif /* __MPLS_HAVE_STAT64 */
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/util.h b/src/util.h
</span><span style='display:block; white-space:pre;color:#808080;'>index 347e761..1e39ffd 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/util.h
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/util.h
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -19,7 +19,9 @@
</span> /* MP support header */
 #include "MacportsLegacySupport.h"
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-#define __MPLS_NEED_CHECK_ACCESS__ __MPLS_LIB_FIX_TIGER_PPC64__
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define __MPLS_NEED_CHECK_ACCESS__ \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    (__MPLS_LIB_FIX_TIGER_PPC64__ \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     || __MPLS_LIB_SUPPORT_STAT64__)
</span> 
 #if __MPLS_NEED_CHECK_ACCESS__
 
</pre><pre style='margin:0'>

</pre>