From: Vibhav Pant vibhavp@gmail.com
On Linux, try creating a memfd first to implement create_temp_file. If a large page mapping is requested (SEC_LARGE_PAGES), create a memfd with the MFD_HUGETLB flags. If SEC_COMMIT is passed, allocate sufficient space to back the mapping with posix_fallocate.
Additionally, ensure when SEC_LARGE_PAGES is requested, ensure that SEC_COMMIT is passed as well. --- configure | 12 +++++ configure.ac | 2 + include/config.h.in | 6 +++ server/mapping.c | 124 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 140 insertions(+), 4 deletions(-)
diff --git a/configure b/configure index 4f4c37756c1..2aebac79845 100755 --- a/configure +++ b/configure @@ -7994,6 +7994,12 @@ if test "x$ac_cv_header_linux_major_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_MAJOR_H 1" >>confdefs.h
+fi +ac_fn_c_check_header_compile "$LINENO" "linux/memfd.h" "ac_cv_header_linux_memfd_h" "$ac_includes_default" +if test "x$ac_cv_header_linux_memfd_h" = xyes +then : + printf "%s\n" "#define HAVE_LINUX_MEMFD_H 1" >>confdefs.h + fi ac_fn_c_check_header_compile "$LINENO" "linux/param.h" "ac_cv_header_linux_param_h" "$ac_includes_default" if test "x$ac_cv_header_linux_param_h" = xyes @@ -20786,6 +20792,12 @@ if test "x$ac_cv_func_mach_continuous_time" = xyes then : printf "%s\n" "#define HAVE_MACH_CONTINUOUS_TIME 1" >>confdefs.h
+fi +ac_fn_c_check_func "$LINENO" "memfd_create" "ac_cv_func_memfd_create" +if test "x$ac_cv_func_memfd_create" = xyes +then : + printf "%s\n" "#define HAVE_MEMFD_CREATE 1" >>confdefs.h + fi ac_fn_c_check_func "$LINENO" "pipe2" "ac_cv_func_pipe2" if test "x$ac_cv_func_pipe2" = xyes diff --git a/configure.ac b/configure.ac index 3f54230b46b..9cc7df4e10a 100644 --- a/configure.ac +++ b/configure.ac @@ -384,6 +384,7 @@ AC_CHECK_HEADERS(\ linux/input.h \ linux/ioctl.h \ linux/major.h \ + linux/memfd.h \ linux/param.h \ linux/serial.h \ linux/types.h \ @@ -2079,6 +2080,7 @@ AC_CHECK_FUNCS(\ getrandom \ kqueue \ mach_continuous_time \ + memfd_create \ pipe2 \ port_create \ posix_fadvise \ diff --git a/include/config.h.in b/include/config.h.in index ef783c75a42..923d943de01 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -180,6 +180,9 @@ /* Define to 1 if you have the <linux/major.h> header file. */ #undef HAVE_LINUX_MAJOR_H
+/* Define to 1 if you have the <linux/memfd.h> header file. */ +#undef HAVE_LINUX_MEMFD_H + /* Define to 1 if you have the <linux/param.h> header file. */ #undef HAVE_LINUX_PARAM_H
@@ -219,6 +222,9 @@ /* Define to 1 if you have the <mach-o/loader.h> header file. */ #undef HAVE_MACH_O_LOADER_H
+/* Define to 1 if you have the 'memfd_create' function. */ +#undef HAVE_MEMFD_CREATE + /* Define to 1 if you have the <mntent.h> header file. */ #undef HAVE_MNTENT_H
diff --git a/server/mapping.c b/server/mapping.c index 857472204d3..7ec0e837949 100644 --- a/server/mapping.c +++ b/server/mapping.c @@ -21,6 +21,7 @@ #include "config.h"
#include <assert.h> +#include <errno.h> #include <fcntl.h> #include <stdarg.h> #include <stdio.h> @@ -29,6 +30,11 @@ #include <sys/stat.h> #include <sys/mman.h> #include <unistd.h> +#ifdef HAVE_MEMFD_CREATE +#include <linux/memfd.h> +#include <dirent.h> +#include <limits.h> +#endif
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -317,13 +323,117 @@ static int check_current_dir_for_exec(void) return (ret != MAP_FAILED); }
+#ifdef HAVE_MEMFD_CREATE +static void make_memfd_name( const char *prefix, ULONG sec_flags, char *dest ) +{ + /* memfd names are restricted to 250 bytes including \0, but are only meant for debugging, with + * duplicate names not causing any side-effects. */ + + if (prefix != NULL) + { + snprintf( dest, 250, "map-%s-%x", prefix, (int)sec_flags ); + } + else + { + snprintf( dest, 250, "map-%x", (int)sec_flags ); + } +} + +static int create_memfd( ULONG file_access, ULONG sec_flags, file_pos_t new_size, int *err ) +{ + int fd; + off_t size = new_size; + char memfd_name[256]; + unsigned int memfd_flags = MFD_ALLOW_SEALING; + unsigned int seal_flags = F_SEAL_SEAL | F_SEAL_WRITE; + +#ifdef HAVE_POSIX_FALLOCATE + if (sizeof(file_pos_t) > sizeof(off_t) && size != new_size) + { + *err = EINVAL; + return -1; + } +#endif +#ifdef MFD_EXEC + memfd_flags |= MFD_EXEC; +#endif + + make_memfd_name( NULL, sec_flags, memfd_name ); + + if (sec_flags & SEC_LARGE_PAGES) + { + memfd_flags |= MFD_HUGETLB; + } + fd = memfd_create( memfd_name, memfd_flags ); + if (fd == -1) + { + if (errno == EINVAL && (sec_flags & SEC_LARGE_PAGES)) + { + /* MFD_HUGETLB & MFD_ALLOW_SEALING is only available in Linux >= 4.16. Lets try creating + * one without HUGETLB */ + fd = memfd_create( memfd_name, memfd_flags & ~MFD_HUGETLB ); + if (fd == -1) + { + *err = errno; + return -1; + } + } + else + { + *err = errno; + return -1; + } + } +#ifdef HAVE_POSIX_FALLOCATE + if (sec_flags & SEC_COMMIT) + { + *err = posix_fallocate( fd, 0, size ); + if (*err != 0) + { + close( fd ); + return -1; + } + } +#endif + if (ftruncate( fd, size ) == -1) + { + *err = errno; + close( fd ); + return -1; + } + if (file_access & FILE_WRITE_DATA) + { + seal_flags &= ~F_SEAL_WRITE; + } + + if (fcntl( fd, F_ADD_SEALS, F_SEAL_SEAL, seal_flags ) == -1) + { + *err = errno; + close( fd ); + return -1; + } + return fd; +} +#endif /* defined(HAVE_MEMFD_CREATE) */ + /* create a temp file for anonymous mappings */ -static int create_temp_file( file_pos_t size ) +static int create_temp_file( ULONG file_access, ULONG sec_flags, file_pos_t size ) { static int temp_dir_fd = -1; char tmpfn[16]; int fd;
+#ifdef HAVE_MEMFD_CREATE + int err = 0; + + fd = create_memfd( file_access, sec_flags, size, &err); + if (fd != -1) + { + return fd; + } + +#endif + if (temp_dir_fd == -1) { temp_dir_fd = server_dir_fd; @@ -608,7 +718,7 @@ static int build_shared_mapping( struct mapping *mapping, int fd,
/* create a temp file for the mapping */
- if ((shared_fd = create_temp_file( total_size )) == -1) return 0; + if ((shared_fd = create_temp_file( FILE_WRITE_DATA, 0, total_size )) == -1) return 0; if (!(file = create_file_for_fd( shared_fd, FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0 ))) return 0;
if (!(buffer = malloc( max_size ))) goto error; @@ -940,7 +1050,12 @@ static struct ranges *create_ranges(void)
static unsigned int get_mapping_flags( obj_handle_t handle, unsigned int flags ) { - switch (flags & (SEC_IMAGE | SEC_RESERVE | SEC_COMMIT | SEC_FILE)) + if (flags & SEC_LARGE_PAGES) + { + if (!(flags & SEC_COMMIT)) + goto error; + } + switch (flags & (SEC_IMAGE | SEC_RESERVE | SEC_COMMIT | SEC_FILE )) { case SEC_IMAGE: if (flags & (SEC_WRITECOMBINE | SEC_LARGE_PAGES)) break; @@ -955,6 +1070,7 @@ static unsigned int get_mapping_flags( obj_handle_t handle, unsigned int flags ) if (handle) return SEC_FILE | (flags & (SEC_NOCACHE | SEC_WRITECOMBINE)); return flags; } +error: set_error( STATUS_INVALID_PARAMETER ); return 0; } @@ -1044,7 +1160,7 @@ static struct mapping *create_mapping( struct object *root, const struct unicode } if ((flags & SEC_RESERVE) && !(mapping->committed = create_ranges())) goto error; mapping->size = (mapping->size + page_mask) & ~((mem_size_t)page_mask); - if ((unix_fd = create_temp_file( mapping->size )) == -1) goto error; + if ((unix_fd = create_temp_file( file_access, flags, mapping->size )) == -1) goto error; if (!(mapping->fd = create_anonymous_fd( &mapping_fd_ops, unix_fd, &mapping->obj, FILE_SYNCHRONOUS_IO_NONALERT ))) goto error; allow_fd_caching( mapping->fd );