From 42c54ff9d5e8733ec76f53b52ce2a7cbd6870763 Mon Sep 17 00:00:00 2001
From: Fred Wright <fw@fwright.net>
Date: Thu, 7 Nov 2024 17:27:22 -0800
Subject: [PATCH] test_realpath: Add nonexistent path case.

Although more thorough testing of various path cases would be useful,
this one, which just tests a simple nonexistent relative path, is
sufficient to demonstrate a bug in the 10.6 non-POSIX realpath(),
which returns "success" via an unsafe pointer to an internal buffer.

TESTED:
Fails in known bad case without the fix; passes in all cases with the
fix.
---
 test/test_realpath.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/test/test_realpath.c b/test/test_realpath.c
index ec890dc..cf14745 100644
--- a/test/test_realpath.c
+++ b/test/test_realpath.c
@@ -37,6 +37,7 @@ typedef struct { strfunc_t realpath; } rpf_t;
 
 #include <assert.h>
 #include <libgen.h>
+#include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -44,6 +45,17 @@ typedef struct { strfunc_t realpath; } rpf_t;
 
 #include <sys/param.h>
 
+/*
+ * Beginning in the 15.x SDK, including malloc.h doesn't work when
+ * _POSIX_C_SOURCE is defined, which we need for the "nonext" version.
+ * So we avoid the official include and declare malloc_size() directly.
+ *
+ * #include <malloc/malloc.h>
+ */
+extern size_t malloc_size(const void *ptr);
+
+#define NONEX_PATH "4981a2d5a4c7bea88154c434b4708045"
+
 /*
  * Allow testing the legacy compatibility entry.
  * We use a simple argless macro and disable the fancier tests.
@@ -85,6 +97,20 @@ main(int argc, char *argv[])
   if (verbose) printf("realpath(path, NULL) supported.\n");
   free((void*)q);
 
+  /*
+   * Test nonexistent path with no supplied buffer.
+   * In some cases (10.6 non-POSIX) this may "succeed" with a bad
+   * returned buffer address.  We accept either failure or success
+   * with a valid buffer.
+   */
+  q = realpath(NONEX_PATH, NULL);
+  assert((!q || malloc_size(q)) \
+         && "realpath(_nonexpath, NULL) returned bad address");
+  if (verbose) {
+    printf("realpath(nonex_path, NULL) %s.\n", q ? "succeeds" : "fails");
+  }
+  if (q && malloc_size(q)) free((void*)q);
+
 #ifndef TEST_MACPORTS_LEGACY_REALPATH
   /* Test with name (reference) only */
   f = realpath;
-- 
GitLab