From 8cdc9d1e23586ae639c241bf8c91830cdef33290 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 11:13:22 +0000 Subject: [PATCH 1/8] Initial plan From 3290bc93d01e730fabad32bee222d01ecf3657fc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 11:46:30 +0000 Subject: [PATCH 2/8] Fix thread-safety issue in GetAllMountPoints by using getfsstat instead of getmntinfo Replace getmntinfo with getfsstat on macOS/BSD to fix AccessViolationException. getmntinfo uses internal static buffers which are not thread-safe, causing corruption when called concurrently from multiple threads. getfsstat requires user-allocated buffers, making it thread-safe. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- src/native/libs/System.Native/pal_mount.c | 31 ++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/native/libs/System.Native/pal_mount.c b/src/native/libs/System.Native/pal_mount.c index 386eba5ede1162..c3c1db27b80a76 100644 --- a/src/native/libs/System.Native/pal_mount.c +++ b/src/native/libs/System.Native/pal_mount.c @@ -8,8 +8,9 @@ #include #include #include +#include -// Check if we should use getmntinfo or /proc/mounts +// Check if we should use getfsstat or /proc/mounts #if HAVE_MNTINFO #include #else @@ -32,18 +33,42 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) { #if HAVE_MNTINFO - // getmntinfo returns pointers to OS-internal structs, so we don't need to worry about free'ing the object + // Use getfsstat which is thread-safe (unlike getmntinfo which uses internal static buffers) #if HAVE_STATFS struct statfs* mounts = NULL; #else struct statvfs* mounts = NULL; #endif - int count = getmntinfo(&mounts, MNT_WAIT); + // First call to get the number of mount points + int count = getfsstat(NULL, 0, MNT_NOWAIT); + if (count <= 0) + { + return count < 0 ? -1 : 0; + } + + // Allocate buffer for mount points (with extra capacity in case new mounts appear) + size_t bufferSize = (size_t)(count + 4) * sizeof(*mounts); + mounts = (typeof(mounts))malloc(bufferSize); + if (mounts == NULL) + { + errno = ENOMEM; + return -1; + } + + // Second call to get actual mount point information + count = getfsstat(mounts, (int)bufferSize, MNT_NOWAIT); + if (count < 0) + { + free(mounts); + return -1; + } + for (int32_t i = 0; i < count; i++) { onFound(context, mounts[i].f_mntonname); } + free(mounts); return 0; } From 6d2c6a5b86d28d9250413fbd070de18a5f27b14a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 11:50:47 +0000 Subject: [PATCH 3/8] Remove typeof() GCC extension for better portability In C, malloc returns void* which implicitly converts to any pointer type, so no explicit cast is needed. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- src/native/libs/System.Native/pal_mount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/libs/System.Native/pal_mount.c b/src/native/libs/System.Native/pal_mount.c index c3c1db27b80a76..22edb8a94512fd 100644 --- a/src/native/libs/System.Native/pal_mount.c +++ b/src/native/libs/System.Native/pal_mount.c @@ -48,7 +48,7 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) // Allocate buffer for mount points (with extra capacity in case new mounts appear) size_t bufferSize = (size_t)(count + 4) * sizeof(*mounts); - mounts = (typeof(mounts))malloc(bufferSize); + mounts = malloc(bufferSize); if (mounts == NULL) { errno = ENOMEM; From 69685895523171c47357c7625bf2485a0f89a80c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 13:31:46 +0000 Subject: [PATCH 4/8] Use retry loop for robustness when mount count changes Instead of using a fixed +4 extra buffer slots, implement a retry loop that reallocates and retries if the number of mounts increases between the count query and the data fetch. This handles the edge case where many mounts are added concurrently. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- src/native/libs/System.Native/pal_mount.c | 63 ++++++++++++++++------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/src/native/libs/System.Native/pal_mount.c b/src/native/libs/System.Native/pal_mount.c index 22edb8a94512fd..950ce321f3ff63 100644 --- a/src/native/libs/System.Native/pal_mount.c +++ b/src/native/libs/System.Native/pal_mount.c @@ -39,28 +39,53 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) #else struct statvfs* mounts = NULL; #endif - // First call to get the number of mount points - int count = getfsstat(NULL, 0, MNT_NOWAIT); - if (count <= 0) - { - return count < 0 ? -1 : 0; - } - // Allocate buffer for mount points (with extra capacity in case new mounts appear) - size_t bufferSize = (size_t)(count + 4) * sizeof(*mounts); - mounts = malloc(bufferSize); - if (mounts == NULL) - { - errno = ENOMEM; - return -1; - } + int count; + int capacity = 0; - // Second call to get actual mount point information - count = getfsstat(mounts, (int)bufferSize, MNT_NOWAIT); - if (count < 0) + // Loop to handle the case where mount points are added between calls + while (1) { - free(mounts); - return -1; + // Get the current number of mount points + count = getfsstat(NULL, 0, MNT_NOWAIT); + if (count < 0) + { + free(mounts); + return -1; + } + if (count == 0) + { + free(mounts); + return 0; + } + + // Reallocate buffer if needed + if (count > capacity) + { + free(mounts); + capacity = count; + mounts = malloc((size_t)capacity * sizeof(*mounts)); + if (mounts == NULL) + { + errno = ENOMEM; + return -1; + } + } + + // Get actual mount point information + count = getfsstat(mounts, capacity * (int)sizeof(*mounts), MNT_NOWAIT); + if (count < 0) + { + free(mounts); + return -1; + } + + // If count fits in capacity, we got all mount points + if (count <= capacity) + { + break; + } + // Otherwise, more mounts were added - loop again with larger buffer } for (int32_t i = 0; i < count; i++) From 87f2d598b9acf05ab37505fad4969bd3d9ec815c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 14:58:03 +0000 Subject: [PATCH 5/8] Simplify count==0 handling by breaking to post-loop code Instead of having a special early return case for count==0, just break out of the loop and let the post-loop code handle it. The for loop will iterate 0 times and free(mounts) is safe for NULL pointers. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- src/native/libs/System.Native/pal_mount.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/native/libs/System.Native/pal_mount.c b/src/native/libs/System.Native/pal_mount.c index 950ce321f3ff63..12b44dea6e645d 100644 --- a/src/native/libs/System.Native/pal_mount.c +++ b/src/native/libs/System.Native/pal_mount.c @@ -53,11 +53,6 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) free(mounts); return -1; } - if (count == 0) - { - free(mounts); - return 0; - } // Reallocate buffer if needed if (count > capacity) @@ -72,6 +67,12 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) } } + // If count is 0, break - post-loop code handles empty case + if (count == 0) + { + break; + } + // Get actual mount point information count = getfsstat(mounts, capacity * (int)sizeof(*mounts), MNT_NOWAIT); if (count < 0) From c15da8c17132921856da6f60df67fdf51c63967d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 16:07:29 +0000 Subject: [PATCH 6/8] Fix build break and improve getfsstat buffer handling - Add explicit cast for malloc to fix C++ build error - Allocate capacity = count + 1 to detect when more mounts are added - Change condition to count < capacity (since getfsstat returns filled count) - Use (size_t) cast consistently for buffer size parameter Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com> --- src/native/libs/System.Native/pal_mount.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/native/libs/System.Native/pal_mount.c b/src/native/libs/System.Native/pal_mount.c index 12b44dea6e645d..947f7b9532b7c3 100644 --- a/src/native/libs/System.Native/pal_mount.c +++ b/src/native/libs/System.Native/pal_mount.c @@ -54,12 +54,16 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) return -1; } - // Reallocate buffer if needed - if (count > capacity) + // Reallocate buffer if needed - allocate one extra to detect if more mounts were added + if (count >= capacity) { free(mounts); - capacity = count; - mounts = malloc((size_t)capacity * sizeof(*mounts)); + capacity = count + 1; +#if HAVE_STATFS + mounts = (struct statfs*)malloc((size_t)capacity * sizeof(*mounts)); +#else + mounts = (struct statvfs*)malloc((size_t)capacity * sizeof(*mounts)); +#endif if (mounts == NULL) { errno = ENOMEM; @@ -74,15 +78,15 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) } // Get actual mount point information - count = getfsstat(mounts, capacity * (int)sizeof(*mounts), MNT_NOWAIT); + count = getfsstat(mounts, (size_t)capacity * sizeof(*mounts), MNT_NOWAIT); if (count < 0) { free(mounts); return -1; } - // If count fits in capacity, we got all mount points - if (count <= capacity) + // If count is less than capacity, we got all mount points + if (count < capacity) { break; } From a97b87bd2eb4cf28acdeb16ff407e79320ccc279 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:28:38 +0000 Subject: [PATCH 7/8] Use multiply_s for integer-overflow safe multiplication - Include pal_safecrt.h for multiply_s - Use multiply_s to compute buffer size with overflow check - Return error on integer overflow - Reuse bufferSize variable for getfsstat call Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com> --- src/native/libs/System.Native/pal_mount.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/native/libs/System.Native/pal_mount.c b/src/native/libs/System.Native/pal_mount.c index 947f7b9532b7c3..3124943c7ca95e 100644 --- a/src/native/libs/System.Native/pal_mount.c +++ b/src/native/libs/System.Native/pal_mount.c @@ -4,6 +4,7 @@ #include "pal_config.h" #include "pal_mount.h" #include "pal_utilities.h" +#include "pal_safecrt.h" #include #include #include @@ -42,6 +43,7 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) int count; int capacity = 0; + size_t bufferSize = 0; // Loop to handle the case where mount points are added between calls while (1) @@ -59,10 +61,15 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) { free(mounts); capacity = count + 1; + if (!multiply_s((size_t)capacity, sizeof(*mounts), &bufferSize)) + { + errno = ENOMEM; + return -1; + } #if HAVE_STATFS - mounts = (struct statfs*)malloc((size_t)capacity * sizeof(*mounts)); + mounts = (struct statfs*)malloc(bufferSize); #else - mounts = (struct statvfs*)malloc((size_t)capacity * sizeof(*mounts)); + mounts = (struct statvfs*)malloc(bufferSize); #endif if (mounts == NULL) { @@ -78,7 +85,7 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) } // Get actual mount point information - count = getfsstat(mounts, (size_t)capacity * sizeof(*mounts), MNT_NOWAIT); + count = getfsstat(mounts, bufferSize, MNT_NOWAIT); if (count < 0) { free(mounts); From 6ca0911283fa8e1b59ef8e1ff54cd60093eded80 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:52:53 +0000 Subject: [PATCH 8/8] Cast buffer size to int for getfsstat and check for overflow - Add check that bufferSize fits in INT_MAX before using - Cast bufferSize to int when calling getfsstat to fix build break Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com> --- src/native/libs/System.Native/pal_mount.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/native/libs/System.Native/pal_mount.c b/src/native/libs/System.Native/pal_mount.c index 3124943c7ca95e..11403005df2dff 100644 --- a/src/native/libs/System.Native/pal_mount.c +++ b/src/native/libs/System.Native/pal_mount.c @@ -66,6 +66,11 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) errno = ENOMEM; return -1; } + if (bufferSize > INT_MAX) + { + errno = ENOMEM; + return -1; + } #if HAVE_STATFS mounts = (struct statfs*)malloc(bufferSize); #else @@ -85,7 +90,7 @@ int32_t SystemNative_GetAllMountPoints(MountPointFound onFound, void* context) } // Get actual mount point information - count = getfsstat(mounts, bufferSize, MNT_NOWAIT); + count = getfsstat(mounts, (int)bufferSize, MNT_NOWAIT); if (count < 0) { free(mounts);