Skip to content

Commit

Permalink
Use memfd_create when available
Browse files Browse the repository at this point in the history
  • Loading branch information
am11 committed Jul 20, 2024
1 parent f26dbf0 commit 972fb0d
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -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);

[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_MemfdSupported", SetLastError = true)]
private static partial int MemfdSupportedImpl();

private static volatile int s_memfdSupported = -1;

internal static bool MemfdSupported
{
get
{
int result = Interlocked.CompareExchange(ref s_memfdSupported, -1, -1);
if (result == -1)
{
result = MemfdSupportedImpl();
Interlocked.Exchange(ref s_memfdSupported, result);
}
return result == 1;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@
Link="Common\Interop\Unix\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)Interop\Unix\Interop.Errors.cs"
Link="Common\Interop\Unix\Interop.Errors.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Close.cs"
Link="Common\Interop\Unix\System.Native\Interop.Close.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Fcntl.cs"
Link="Common\Interop\Unix\Interop.Fcntl.cs" />
<Compile Include="$(CommonPath)Interop\Unix\Interop.IOErrors.cs"
Expand Down Expand Up @@ -119,6 +121,8 @@
Link="Common\Interop\Unix\Interop.MAdvise.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.ShmOpen.cs"
Link="Common\Interop\Unix\Interop.ShmOpen.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.MemfdCreate.cs"
Link="Common\Interop\Unix\Interop.MemfdCreate.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Unlink.cs"
Link="Common\Interop\Unix\Interop.Unlink.cs" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,14 @@ private static SafeFileHandle CreateSharedBackingObject(Interop.Sys.MemoryMapped
do
{
mapName = GenerateMapName();
fd = Interop.Sys.ShmOpen(mapName, flags, (int)perms); // Create the shared memory object.
if (Interop.Sys.MemfdSupported)
{
fd = Interop.Sys.MemfdCreate(mapName);
}
else
{
fd = Interop.Sys.ShmOpen(mapName, flags, (int)perms); // Create the shared memory object.
}

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

try
{
// Unlink the shared memory object immediately so that it'll go away once all handles
// to it are closed (as with opened then unlinked files, it'll remain usable via
// the open handles even though it's unlinked and can't be opened anew via its name).
Interop.CheckIo(Interop.Sys.ShmUnlink(mapName));
if (!Interop.Sys.MemfdSupported)
{
// Unlink the shared memory object immediately so that it'll go away once all handles
// to it are closed (as with opened then unlinked files, it'll remain usable via
// the open handles even though it's unlinked and can't be opened anew via its name).
Interop.CheckIo(Interop.Sys.ShmUnlink(mapName));
}

// 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,
Expand Down
2 changes: 2 additions & 0 deletions src/native/libs/System.Native/entrypoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ static const Entry s_sysNative[] =
DllImportEntry(SystemNative_Close)
DllImportEntry(SystemNative_Dup)
DllImportEntry(SystemNative_Unlink)
DllImportEntry(SystemNative_MemfdSupported)
DllImportEntry(SystemNative_MemfdCreate)
DllImportEntry(SystemNative_ShmOpen)
DllImportEntry(SystemNative_ShmUnlink)
DllImportEntry(SystemNative_GetReadDirRBufferSize)
Expand Down
42 changes: 42 additions & 0 deletions src/native/libs/System.Native/pal_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,48 @@ int32_t SystemNative_Unlink(const char* path)
return result;
}

int32_t SystemNative_MemfdSupported(void)
{
#ifdef MFD_ALLOW_SEALING
#ifdef TARGET_LINUX
struct utsname uts;
int32_t major, minor;

// memfd_create is only known to work properly on kernel version > 3.17 and throws SIGSEGV instead of ENOTSUP
if (sscanf(uts.release, "%d.%d", &major, &minor) == 2 && (major < 3 || (major == 3 && minor < 17)))
{
return 0;
}
#endif

int32_t fd = memfd_create("test", MFD_CLOEXEC | MFD_ALLOW_SEALING);

Check failure on line 386 in src/native/libs/System.Native/pal_io.c

View check run for this annotation

Azure Pipelines / runtime (Build android-arm64 Release AllSubsets_Mono)

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

src/native/libs/System.Native/pal_io.c(386,18): error G2B346720: (NETCORE_ENGINEERING_TELEMETRY=Build) implicit declaration of function 'memfd_create' is invalid in C99 [-Werror,-Wimplicit-function-declaration]

Check failure on line 386 in src/native/libs/System.Native/pal_io.c

View check run for this annotation

Azure Pipelines / runtime (Build android-arm64 Release AllSubsets_Mono)

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

src/native/libs/System.Native/pal_io.c(386,18): error G2B346720: (NETCORE_ENGINEERING_TELEMETRY=Build) implicit declaration of function 'memfd_create' is invalid in C99 [-Werror,-Wimplicit-function-declaration]

Check failure on line 386 in src/native/libs/System.Native/pal_io.c

View check run for this annotation

Azure Pipelines / runtime (Build android-arm Release AllSubsets_Mono)

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

src/native/libs/System.Native/pal_io.c(386,18): error G9198F18E: (NETCORE_ENGINEERING_TELEMETRY=Build) implicit declaration of function 'memfd_create' is invalid in C99 [-Werror,-Wimplicit-function-declaration]

Check failure on line 386 in src/native/libs/System.Native/pal_io.c

View check run for this annotation

Azure Pipelines / runtime (Build android-arm Release AllSubsets_Mono)

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

src/native/libs/System.Native/pal_io.c(386,18): error G9198F18E: (NETCORE_ENGINEERING_TELEMETRY=Build) implicit declaration of function 'memfd_create' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
if (fd < 0) return 0;

close(fd);
return 1;
#else
errno = ENOTSUP;
return 0;
#endif
}

intptr_t SystemNative_MemfdCreate(const char* name)
{
#ifdef MFD_ALLOW_SEALING
#if defined(SHM_NAME_MAX) // macOS
assert(strlen(name) <= SHM_NAME_MAX);
#elif defined(PATH_MAX) // other Unixes
assert(strlen(name) <= PATH_MAX);
#endif

return memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING);

Check failure on line 406 in src/native/libs/System.Native/pal_io.c

View check run for this annotation

Azure Pipelines / runtime (Build android-arm64 Release AllSubsets_Mono)

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

src/native/libs/System.Native/pal_io.c(406,12): error G2B346720: (NETCORE_ENGINEERING_TELEMETRY=Build) implicit declaration of function 'memfd_create' is invalid in C99 [-Werror,-Wimplicit-function-declaration]

Check failure on line 406 in src/native/libs/System.Native/pal_io.c

View check run for this annotation

Azure Pipelines / runtime (Build android-arm64 Release AllSubsets_Mono)

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

src/native/libs/System.Native/pal_io.c(406,12): error G2B346720: (NETCORE_ENGINEERING_TELEMETRY=Build) implicit declaration of function 'memfd_create' is invalid in C99 [-Werror,-Wimplicit-function-declaration]

Check failure on line 406 in src/native/libs/System.Native/pal_io.c

View check run for this annotation

Azure Pipelines / runtime (Build android-arm Release AllSubsets_Mono)

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

src/native/libs/System.Native/pal_io.c(406,12): error G9198F18E: (NETCORE_ENGINEERING_TELEMETRY=Build) implicit declaration of function 'memfd_create' is invalid in C99 [-Werror,-Wimplicit-function-declaration]

Check failure on line 406 in src/native/libs/System.Native/pal_io.c

View check run for this annotation

Azure Pipelines / runtime (Build android-arm Release AllSubsets_Mono)

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

src/native/libs/System.Native/pal_io.c(406,12): error G9198F18E: (NETCORE_ENGINEERING_TELEMETRY=Build) implicit declaration of function 'memfd_create' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
#else
(void)name;
errno = ENOTSUP;
return -1;
#endif
}

intptr_t SystemNative_ShmOpen(const char* name, int32_t flags, int32_t mode)
{
#if defined(SHM_NAME_MAX) // macOS
Expand Down
14 changes: 14 additions & 0 deletions src/native/libs/System.Native/pal_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_MemfdSupported(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);

/**
* Open or create a shared memory object. Implemented as shim to shm_open(3).
*
Expand Down

0 comments on commit 972fb0d

Please sign in to comment.