diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemfdCreate.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemfdCreate.cs
new file mode 100644
index 00000000000000..5fdca42fdb4aae
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemfdCreate.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+using System.Threading;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_MemfdCreate", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)]
+ internal static partial SafeFileHandle MemfdCreate(string name, int isReadonly);
+
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_IsMemfdSupported", SetLastError = true)]
+ private static partial int MemfdSupportedImpl();
+
+ private static volatile sbyte s_memfdSupported;
+
+ internal static bool IsMemfdSupported
+ {
+ get
+ {
+ sbyte memfdSupported = s_memfdSupported;
+ if (memfdSupported == 0)
+ {
+ Interlocked.CompareExchange(ref s_memfdSupported, (sbyte)(MemfdSupportedImpl() == 1 ? 1 : -1), 0);
+ memfdSupported = s_memfdSupported;
+ }
+ return memfdSupported > 0;
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj b/src/libraries/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj
index 8c7cd9cfdda07b..d024cdd7d73110 100644
--- a/src/libraries/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj
+++ b/src/libraries/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj
@@ -91,6 +91,8 @@
Link="Common\Interop\Unix\Interop.Libraries.cs" />
+
+
diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs
index 2c49129705d1f9..ae9e24c5722973 100644
--- a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs
+++ b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs
@@ -162,11 +162,13 @@ private static FileAccess TranslateProtectionsToFileAccess(Interop.Sys.MemoryMap
private static SafeFileHandle CreateSharedBackingObject(Interop.Sys.MemoryMappedProtections protections, long capacity, HandleInheritability inheritability)
{
- return CreateSharedBackingObjectUsingMemory(protections, capacity, inheritability)
- ?? CreateSharedBackingObjectUsingFile(protections, capacity, inheritability);
+ return Interop.Sys.IsMemfdSupported ?
+ CreateSharedBackingObjectUsingMemoryMemfdCreate(protections, capacity, inheritability) :
+ CreateSharedBackingObjectUsingMemoryShmOpen(protections, capacity, inheritability)
+ ?? CreateSharedBackingObjectUsingFile(protections, capacity, inheritability);
}
- private static SafeFileHandle? CreateSharedBackingObjectUsingMemory(
+ private static SafeFileHandle? CreateSharedBackingObjectUsingMemoryShmOpen(
Interop.Sys.MemoryMappedProtections protections, long capacity, HandleInheritability inheritability)
{
// Determine the flags to use when creating the shared memory object
@@ -244,27 +246,66 @@ private static SafeFileHandle CreateSharedBackingObject(Interop.Sys.MemoryMapped
fd.Dispose();
throw;
}
+ }
+
+ private static string GenerateMapName()
+ {
+ // macOS shm_open documentation says that the sys-call can fail with ENAMETOOLONG if the name exceeds SHM_NAME_MAX characters.
+ // The problem is that SHM_NAME_MAX is not defined anywhere and is not consistent amongst macOS versions (arm64 vs x64 for example).
+ // It was reported in 2008 (https://lists.apple.com/archives/xcode-users/2008/Apr/msg00523.html),
+ // but considered to be by design (http://web.archive.org/web/20140109200632/http://lists.apple.com/archives/darwin-development/2003/Mar/msg00244.html).
+ // According to https://github.com/qt/qtbase/blob/1ed449e168af133184633d174fd7339a13d1d595/src/corelib/kernel/qsharedmemory.cpp#L53-L56 the actual value is 30.
+ // Some other OSS libs use 32 (we did as well, but it was not enough) or 31, but we prefer 30 just to be extra safe.
+ const int MaxNameLength = 30;
+ // The POSIX shared memory object name must begin with '/'. After that we just want something short (30) and unique.
+ const string NamePrefix = "/dotnet_";
+ return string.Create(MaxNameLength, 0, (span, state) =>
+ {
+ Span guid = stackalloc char[32];
+ Guid.NewGuid().TryFormat(guid, out int charsWritten, "N");
+ Debug.Assert(charsWritten == 32);
+ NamePrefix.CopyTo(span);
+ guid.Slice(0, MaxNameLength - NamePrefix.Length).CopyTo(span.Slice(NamePrefix.Length));
+ Debug.Assert(Encoding.UTF8.GetByteCount(span) <= MaxNameLength); // the standard uses Utf8
+ });
+ }
+
+ private static SafeFileHandle CreateSharedBackingObjectUsingMemoryMemfdCreate(
+ Interop.Sys.MemoryMappedProtections protections, long capacity, HandleInheritability inheritability)
+ {
+ int isReadonly = ((protections & Interop.Sys.MemoryMappedProtections.PROT_READ) != 0 &&
+ (protections & Interop.Sys.MemoryMappedProtections.PROT_WRITE) == 0) ? 1 : 0;
- static string GenerateMapName()
+ SafeFileHandle fd = Interop.Sys.MemfdCreate(GenerateMapName(), isReadonly);
+ if (fd.IsInvalid)
{
- // macOS shm_open documentation says that the sys-call can fail with ENAMETOOLONG if the name exceeds SHM_NAME_MAX characters.
- // The problem is that SHM_NAME_MAX is not defined anywhere and is not consistent amongst macOS versions (arm64 vs x64 for example).
- // It was reported in 2008 (https://lists.apple.com/archives/xcode-users/2008/Apr/msg00523.html),
- // but considered to be by design (http://web.archive.org/web/20140109200632/http://lists.apple.com/archives/darwin-development/2003/Mar/msg00244.html).
- // According to https://github.com/qt/qtbase/blob/1ed449e168af133184633d174fd7339a13d1d595/src/corelib/kernel/qsharedmemory.cpp#L53-L56 the actual value is 30.
- // Some other OSS libs use 32 (we did as well, but it was not enough) or 31, but we prefer 30 just to be extra safe.
- const int MaxNameLength = 30;
- // The POSIX shared memory object name must begin with '/'. After that we just want something short (30) and unique.
- const string NamePrefix = "/dotnet_";
- return string.Create(MaxNameLength, 0, (span, state) =>
+ Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
+ fd.Dispose();
+
+ throw Interop.GetExceptionForIoErrno(errorInfo);
+ }
+
+ try
+ {
+ // Give it the right capacity. We do this directly with ftruncate rather
+ // than via FileStream.SetLength after the FileStream is created because, on some systems,
+ // lseek fails on shared memory objects, causing the FileStream to think it's unseekable,
+ // causing it to preemptively throw from SetLength.
+ Interop.CheckIo(Interop.Sys.FTruncate(fd, capacity));
+
+ // SystemNative_MemfdCreate sets CLOEXEC implicitly. If the inheritability requested is Inheritable, remove CLOEXEC.
+ if (inheritability == HandleInheritability.Inheritable &&
+ Interop.Sys.Fcntl.SetFD(fd, 0) == -1)
{
- Span guid = stackalloc char[32];
- Guid.NewGuid().TryFormat(guid, out int charsWritten, "N");
- Debug.Assert(charsWritten == 32);
- NamePrefix.CopyTo(span);
- guid.Slice(0, MaxNameLength - NamePrefix.Length).CopyTo(span.Slice(NamePrefix.Length));
- Debug.Assert(Encoding.UTF8.GetByteCount(span) <= MaxNameLength); // the standard uses Utf8
- });
+ throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo());
+ }
+
+ return fd;
+ }
+ catch
+ {
+ fd.Dispose();
+ throw;
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.SunOS.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.SunOS.cs
index 84199a3d306d28..fb7ff75cb2e6c6 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Environment.SunOS.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Environment.SunOS.cs
@@ -8,6 +8,6 @@ namespace System
public static partial class Environment
{
public static long WorkingSet =>
- (long)(Interop.procfs.TryReadProcessStatusInfo(Interop.procfs.ProcPid.Self, out Interop.procfs.ProcessStatusInfo status) ? status.ResidentSetSize : 0);
+ (long)(Interop.procfs.TryReadProcessStatusInfo(ProcessId, out Interop.procfs.ProcessStatusInfo status) ? status.ResidentSetSize : 0);
}
}
diff --git a/src/native/libs/System.Native/entrypoints.c b/src/native/libs/System.Native/entrypoints.c
index f413159430ed6f..2165430d65f56a 100644
--- a/src/native/libs/System.Native/entrypoints.c
+++ b/src/native/libs/System.Native/entrypoints.c
@@ -61,6 +61,8 @@ static const Entry s_sysNative[] =
DllImportEntry(SystemNative_Close)
DllImportEntry(SystemNative_Dup)
DllImportEntry(SystemNative_Unlink)
+ DllImportEntry(SystemNative_IsMemfdSupported)
+ DllImportEntry(SystemNative_MemfdCreate)
DllImportEntry(SystemNative_ShmOpen)
DllImportEntry(SystemNative_ShmUnlink)
DllImportEntry(SystemNative_GetReadDirRBufferSize)
diff --git a/src/native/libs/System.Native/pal_io.c b/src/native/libs/System.Native/pal_io.c
index 0b6eab7e8c22bd..17e5f77d2d8427 100644
--- a/src/native/libs/System.Native/pal_io.c
+++ b/src/native/libs/System.Native/pal_io.c
@@ -10,8 +10,8 @@
#include "pal_types.h"
#include
-#include
#include
+#include
#include
#include
#include
@@ -369,6 +369,72 @@ int32_t SystemNative_Unlink(const char* path)
return result;
}
+#ifdef __NR_memfd_create
+#ifndef MFD_CLOEXEC
+#define MFD_CLOEXEC 0x0001U
+#endif
+#ifndef MFD_ALLOW_SEALING
+#define MFD_ALLOW_SEALING 0x0002U
+#endif
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (1024 + 9)
+#endif
+#ifndef F_SEAL_WRITE
+#define F_SEAL_WRITE 0x0008
+#endif
+#endif
+
+int32_t SystemNative_IsMemfdSupported(void)
+{
+#ifdef __NR_memfd_create
+#ifdef TARGET_LINUX
+ struct utsname uts;
+ int32_t major, minor;
+
+ // memfd_create is known to only work properly on kernel version > 3.17.
+ // On earlier versions, it may raise SIGSEGV instead of returning ENOTSUP.
+ if (uname(&uts) == 0 && sscanf(uts.release, "%d.%d", &major, &minor) == 2 && (major < 3 || (major == 3 && minor < 17)))
+ {
+ return 0;
+ }
+#endif
+
+ // Note that the name has no affect on file descriptor behavior. From linux manpage:
+ // Names do not affect the behavior of the file descriptor, and as such multiple files can have the same name without any side effects.
+ int32_t fd = (int32_t)syscall(__NR_memfd_create, "test", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ if (fd < 0) return 0;
+
+ close(fd);
+ return 1;
+#else
+ errno = ENOTSUP;
+ return 0;
+#endif
+}
+
+intptr_t SystemNative_MemfdCreate(const char* name, int32_t isReadonly)
+{
+#ifdef __NR_memfd_create
+#if defined(SHM_NAME_MAX) // macOS
+ assert(strlen(name) <= SHM_NAME_MAX);
+#elif defined(PATH_MAX) // other Unixes
+ assert(strlen(name) <= PATH_MAX);
+#endif
+
+ int32_t fd = (int32_t)syscall(__NR_memfd_create, name, MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ if (!isReadonly || fd < 0) return fd;
+
+ // Add a write seal when readonly protection requested
+ while (fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) < 0 && errno == EINTR);
+ return fd;
+#else
+ (void)name;
+ (void)isReadonly;
+ errno = ENOTSUP;
+ return -1;
+#endif
+}
+
intptr_t SystemNative_ShmOpen(const char* name, int32_t flags, int32_t mode)
{
#if defined(SHM_NAME_MAX) // macOS
diff --git a/src/native/libs/System.Native/pal_io.h b/src/native/libs/System.Native/pal_io.h
index 03fd94cea25417..a07aa7c170219a 100644
--- a/src/native/libs/System.Native/pal_io.h
+++ b/src/native/libs/System.Native/pal_io.h
@@ -369,6 +369,20 @@ PALEXPORT intptr_t SystemNative_Dup(intptr_t oldfd);
*/
PALEXPORT int32_t SystemNative_Unlink(const char* path);
+/**
+ * Check if the system supports memfd_create(2).
+ *
+ * Returns 1 if memfd_create is supported, 0 if not supported, or -1 on failure. Sets errno on failure.
+ */
+PALEXPORT int32_t SystemNative_IsMemfdSupported(void);
+
+/**
+ * Create an anonymous file descriptor. Implemented as shim to memfd_create(2).
+ *
+ * Returns file descriptor or -1 on failure. Sets errno on failure.
+ */
+PALEXPORT intptr_t SystemNative_MemfdCreate(const char* name, int32_t isReadonly);
+
/**
* Open or create a shared memory object. Implemented as shim to shm_open(3).
*