Skip to content

Commit 972fb0d

Browse files
committed
Use memfd_create when available
1 parent f26dbf0 commit 972fb0d

File tree

6 files changed

+112
-6
lines changed

6 files changed

+112
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.InteropServices;
5+
using System.Threading;
6+
using Microsoft.Win32.SafeHandles;
7+
8+
internal static partial class Interop
9+
{
10+
internal static partial class Sys
11+
{
12+
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_MemfdCreate", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)]
13+
internal static partial SafeFileHandle MemfdCreate(string name);
14+
15+
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_MemfdSupported", SetLastError = true)]
16+
private static partial int MemfdSupportedImpl();
17+
18+
private static volatile int s_memfdSupported = -1;
19+
20+
internal static bool MemfdSupported
21+
{
22+
get
23+
{
24+
int result = Interlocked.CompareExchange(ref s_memfdSupported, -1, -1);
25+
if (result == -1)
26+
{
27+
result = MemfdSupportedImpl();
28+
Interlocked.Exchange(ref s_memfdSupported, result);
29+
}
30+
return result == 1;
31+
}
32+
}
33+
}
34+
}

src/libraries/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@
9191
Link="Common\Interop\Unix\Interop.Libraries.cs" />
9292
<Compile Include="$(CommonPath)Interop\Unix\Interop.Errors.cs"
9393
Link="Common\Interop\Unix\Interop.Errors.cs" />
94+
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Close.cs"
95+
Link="Common\Interop\Unix\System.Native\Interop.Close.cs" />
9496
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Fcntl.cs"
9597
Link="Common\Interop\Unix\Interop.Fcntl.cs" />
9698
<Compile Include="$(CommonPath)Interop\Unix\Interop.IOErrors.cs"
@@ -119,6 +121,8 @@
119121
Link="Common\Interop\Unix\Interop.MAdvise.cs" />
120122
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.ShmOpen.cs"
121123
Link="Common\Interop\Unix\Interop.ShmOpen.cs" />
124+
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.MemfdCreate.cs"
125+
Link="Common\Interop\Unix\Interop.MemfdCreate.cs" />
122126
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Unlink.cs"
123127
Link="Common\Interop\Unix\Interop.Unlink.cs" />
124128
</ItemGroup>

src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs

+16-6
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,14 @@ private static SafeFileHandle CreateSharedBackingObject(Interop.Sys.MemoryMapped
190190
do
191191
{
192192
mapName = GenerateMapName();
193-
fd = Interop.Sys.ShmOpen(mapName, flags, (int)perms); // Create the shared memory object.
193+
if (Interop.Sys.MemfdSupported)
194+
{
195+
fd = Interop.Sys.MemfdCreate(mapName);
196+
}
197+
else
198+
{
199+
fd = Interop.Sys.ShmOpen(mapName, flags, (int)perms); // Create the shared memory object.
200+
}
194201

195202
if (fd.IsInvalid)
196203
{
@@ -204,7 +211,7 @@ private static SafeFileHandle CreateSharedBackingObject(Interop.Sys.MemoryMapped
204211
// the result of native shm_open does not work well with our subsequent call to mmap.
205212
return null;
206213
}
207-
else if (errorInfo.Error == Interop.Error.ENAMETOOLONG)
214+
else if (!Interop.Sys.MemfdSupported && errorInfo.Error == Interop.Error.ENAMETOOLONG)
208215
{
209216
Debug.Fail($"shm_open failed with ENAMETOOLONG for {Encoding.UTF8.GetByteCount(mapName)} byte long name.");
210217
// in theory it should not happen anymore, but just to be extra safe we use the fallback
@@ -219,10 +226,13 @@ private static SafeFileHandle CreateSharedBackingObject(Interop.Sys.MemoryMapped
219226

220227
try
221228
{
222-
// Unlink the shared memory object immediately so that it'll go away once all handles
223-
// to it are closed (as with opened then unlinked files, it'll remain usable via
224-
// the open handles even though it's unlinked and can't be opened anew via its name).
225-
Interop.CheckIo(Interop.Sys.ShmUnlink(mapName));
229+
if (!Interop.Sys.MemfdSupported)
230+
{
231+
// Unlink the shared memory object immediately so that it'll go away once all handles
232+
// to it are closed (as with opened then unlinked files, it'll remain usable via
233+
// the open handles even though it's unlinked and can't be opened anew via its name).
234+
Interop.CheckIo(Interop.Sys.ShmUnlink(mapName));
235+
}
226236

227237
// Give it the right capacity. We do this directly with ftruncate rather
228238
// than via FileStream.SetLength after the FileStream is created because, on some systems,

src/native/libs/System.Native/entrypoints.c

+2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ static const Entry s_sysNative[] =
6262
DllImportEntry(SystemNative_Close)
6363
DllImportEntry(SystemNative_Dup)
6464
DllImportEntry(SystemNative_Unlink)
65+
DllImportEntry(SystemNative_MemfdSupported)
66+
DllImportEntry(SystemNative_MemfdCreate)
6567
DllImportEntry(SystemNative_ShmOpen)
6668
DllImportEntry(SystemNative_ShmUnlink)
6769
DllImportEntry(SystemNative_GetReadDirRBufferSize)

src/native/libs/System.Native/pal_io.c

+42
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,48 @@ int32_t SystemNative_Unlink(const char* path)
369369
return result;
370370
}
371371

372+
int32_t SystemNative_MemfdSupported(void)
373+
{
374+
#ifdef MFD_ALLOW_SEALING
375+
#ifdef TARGET_LINUX
376+
struct utsname uts;
377+
int32_t major, minor;
378+
379+
// memfd_create is only known to work properly on kernel version > 3.17 and throws SIGSEGV instead of ENOTSUP
380+
if (sscanf(uts.release, "%d.%d", &major, &minor) == 2 && (major < 3 || (major == 3 && minor < 17)))
381+
{
382+
return 0;
383+
}
384+
#endif
385+
386+
int32_t fd = memfd_create("test", MFD_CLOEXEC | MFD_ALLOW_SEALING);
387+
if (fd < 0) return 0;
388+
389+
close(fd);
390+
return 1;
391+
#else
392+
errno = ENOTSUP;
393+
return 0;
394+
#endif
395+
}
396+
397+
intptr_t SystemNative_MemfdCreate(const char* name)
398+
{
399+
#ifdef MFD_ALLOW_SEALING
400+
#if defined(SHM_NAME_MAX) // macOS
401+
assert(strlen(name) <= SHM_NAME_MAX);
402+
#elif defined(PATH_MAX) // other Unixes
403+
assert(strlen(name) <= PATH_MAX);
404+
#endif
405+
406+
return memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING);
407+
#else
408+
(void)name;
409+
errno = ENOTSUP;
410+
return -1;
411+
#endif
412+
}
413+
372414
intptr_t SystemNative_ShmOpen(const char* name, int32_t flags, int32_t mode)
373415
{
374416
#if defined(SHM_NAME_MAX) // macOS

src/native/libs/System.Native/pal_io.h

+14
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,20 @@ PALEXPORT intptr_t SystemNative_Dup(intptr_t oldfd);
369369
*/
370370
PALEXPORT int32_t SystemNative_Unlink(const char* path);
371371

372+
/**
373+
* Check if the system supports memfd_create(2).
374+
*
375+
* Returns 1 if memfd_create is supported, 0 if not supported, or -1 on failure. Sets errno on failure.
376+
*/
377+
PALEXPORT int32_t SystemNative_MemfdSupported(void);
378+
379+
/**
380+
* Create an anonymous file descriptor. Implemented as shim to memfd_create(2).
381+
*
382+
* Returns file descriptor or -1 on failure. Sets errno on failure.
383+
*/
384+
PALEXPORT intptr_t SystemNative_MemfdCreate(const char* name);
385+
372386
/**
373387
* Open or create a shared memory object. Implemented as shim to shm_open(3).
374388
*

0 commit comments

Comments
 (0)