<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>