From: Vibhav Pant vibhavp@gmail.com
--- server/mapping.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-)
diff --git a/server/mapping.c b/server/mapping.c index ff99b45ce51..2280360f6cd 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> @@ -317,13 +318,90 @@ static int check_current_dir_for_exec(void) return (ret != MAP_FAILED); }
+#if defined( __linux__ ) && defined( _GNU_SOURCE ) +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-%lx-%lld", prefix, sec_flags, current_time); + } + else + { + snprintf( dest, 250, "map-%lx-%lld", sec_flags, current_time ); + } +} + +static int create_memfd( ULONG file_access, ULONG sec_flags, file_pos_t size ) +{ + int fd; + char memfd_name[256]; + unsigned int memfd_flags = MFD_ALLOW_SEALING; + unsigned int seal_flags = F_SEAL_SEAL | F_SEAL_WRITE; + + 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) + { + file_set_error(); + return -1; + } + } + else + { + return -1; + } + } + if (ftruncate(fd, size) == -1) + { + file_set_error(); + 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) + { + file_set_error(); + close( fd ); + return -1; + } + return fd; +} +#endif /* defined( __linux__ ) && defined( _GNU_SOURCE ) */ + /* 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;
+#if defined( __linux__ ) && defined( _GNU_SOURCE ) + fd = create_memfd( file_access, sec_flags, size ); + if (fd != -1) + { + return fd; + } +#endif + if (temp_dir_fd == -1) { temp_dir_fd = server_dir_fd; @@ -608,7 +686,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; @@ -1044,7 +1122,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( mapping->size, file_access, flags )) == -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 );