1 /* -----------------------------------------------------------------------
   2    closures.c - Copyright (c) 2019 Anthony Green
   3                 Copyright (c) 2007, 2009, 2010 Red Hat, Inc.
   4                 Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc
   5                 Copyright (c) 2011 Plausible Labs Cooperative, Inc.
   6 
   7    Code to allocate and deallocate memory for closures.
   8 
   9    Permission is hereby granted, free of charge, to any person obtaining
  10    a copy of this software and associated documentation files (the
  11    ``Software''), to deal in the Software without restriction, including
  12    without limitation the rights to use, copy, modify, merge, publish,
  13    distribute, sublicense, and/or sell copies of the Software, and to
  14    permit persons to whom the Software is furnished to do so, subject to
  15    the following conditions:
  16 
  17    The above copyright notice and this permission notice shall be included
  18    in all copies or substantial portions of the Software.
  19 
  20    THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
  21    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23    NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  24    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  25    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  27    DEALINGS IN THE SOFTWARE.
  28    ----------------------------------------------------------------------- */
  29 
  30 #if defined __linux__ && !defined _GNU_SOURCE
  31 #define _GNU_SOURCE 1
  32 #endif
  33 
  34 #include <fficonfig.h>
  35 #include <ffi.h>
  36 #include <ffi_common.h>
  37 
  38 #ifdef __NetBSD__
  39 #include <sys/param.h>
  40 #endif
  41 
  42 #if __NetBSD_Version__ - 0 >= 799007200
  43 /* NetBSD with PROT_MPROTECT */
  44 #include <sys/mman.h>
  45 
  46 #include <stddef.h>
  47 #include <unistd.h>
  48 
  49 static const size_t overhead =
  50   (sizeof(max_align_t) > sizeof(void *) + sizeof(size_t)) ?
  51     sizeof(max_align_t)
  52     : sizeof(void *) + sizeof(size_t);
  53 
  54 #define ADD_TO_POINTER(p, d) ((void *)((uintptr_t)(p) + (d)))
  55 
  56 void *
  57 ffi_closure_alloc (size_t size, void **code)
  58 {
  59   static size_t page_size;
  60   size_t rounded_size;
  61   void *codeseg, *dataseg;
  62   int prot;
  63 
  64   /* Expect that PAX mprotect is active and a separate code mapping is necessary. */
  65   if (!code)
  66     return NULL;
  67 
  68   /* Obtain system page size. */
  69   if (!page_size)
  70     page_size = sysconf(_SC_PAGESIZE);
  71 
  72   /* Round allocation size up to the next page, keeping in mind the size field and pointer to code map. */
  73   rounded_size = (size + overhead + page_size - 1) & ~(page_size - 1);
  74 
  75   /* Primary mapping is RW, but request permission to switch to PROT_EXEC later. */
  76   prot = PROT_READ | PROT_WRITE | PROT_MPROTECT(PROT_EXEC);
  77   dataseg = mmap(NULL, rounded_size, prot, MAP_ANON | MAP_PRIVATE, -1, 0);
  78   if (dataseg == MAP_FAILED)
  79     return NULL;
  80 
  81   /* Create secondary mapping and switch it to RX. */
  82   codeseg = mremap(dataseg, rounded_size, NULL, rounded_size, MAP_REMAPDUP);
  83   if (codeseg == MAP_FAILED) {
  84     munmap(dataseg, rounded_size);
  85     return NULL;
  86   }
  87   if (mprotect(codeseg, rounded_size, PROT_READ | PROT_EXEC) == -1) {
  88     munmap(codeseg, rounded_size);
  89     munmap(dataseg, rounded_size);
  90     return NULL;
  91   }
  92 
  93   /* Remember allocation size and location of the secondary mapping for ffi_closure_free. */
  94   memcpy(dataseg, &rounded_size, sizeof(rounded_size));
  95   memcpy(ADD_TO_POINTER(dataseg, sizeof(size_t)), &codeseg, sizeof(void *));
  96   *code = ADD_TO_POINTER(codeseg, overhead);
  97   return ADD_TO_POINTER(dataseg, overhead);
  98 }
  99 
 100 void
 101 ffi_closure_free (void *ptr)
 102 {
 103   void *codeseg, *dataseg;
 104   size_t rounded_size;
 105 
 106   dataseg = ADD_TO_POINTER(ptr, -overhead);
 107   memcpy(&rounded_size, dataseg, sizeof(rounded_size));
 108   memcpy(&codeseg, ADD_TO_POINTER(dataseg, sizeof(size_t)), sizeof(void *));
 109   munmap(dataseg, rounded_size);
 110   munmap(codeseg, rounded_size);
 111 }
 112 #else /* !NetBSD with PROT_MPROTECT */
 113 
 114 #if !FFI_MMAP_EXEC_WRIT && !FFI_EXEC_TRAMPOLINE_TABLE
 115 # if __linux__ && !defined(__ANDROID__)
 116 /* This macro indicates it may be forbidden to map anonymous memory
 117    with both write and execute permission.  Code compiled when this
 118    option is defined will attempt to map such pages once, but if it
 119    fails, it falls back to creating a temporary file in a writable and
 120    executable filesystem and mapping pages from it into separate
 121    locations in the virtual memory space, one location writable and
 122    another executable.  */
 123 #  define FFI_MMAP_EXEC_WRIT 1
 124 #  define HAVE_MNTENT 1
 125 # endif
 126 # if defined(X86_WIN32) || defined(X86_WIN64) || defined(_M_ARM64) || defined(__OS2__)
 127 /* Windows systems may have Data Execution Protection (DEP) enabled,
 128    which requires the use of VirtualMalloc/VirtualFree to alloc/free
 129    executable memory. */
 130 #  define FFI_MMAP_EXEC_WRIT 1
 131 # endif
 132 #endif
 133 
 134 #if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
 135 # if defined(__linux__) && !defined(__ANDROID__)
 136 /* When defined to 1 check for SELinux and if SELinux is active,
 137    don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
 138    might cause audit messages.  */
 139 #  define FFI_MMAP_EXEC_SELINUX 1
 140 # endif
 141 #endif
 142 
 143 #if FFI_CLOSURES
 144 
 145 #if FFI_EXEC_TRAMPOLINE_TABLE
 146 
 147 #ifdef __MACH__
 148 
 149 #include <mach/mach.h>
 150 #include <pthread.h>
 151 #include <stdio.h>
 152 #include <stdlib.h>
 153 
 154 extern void *ffi_closure_trampoline_table_page;
 155 
 156 typedef struct ffi_trampoline_table ffi_trampoline_table;
 157 typedef struct ffi_trampoline_table_entry ffi_trampoline_table_entry;
 158 
 159 struct ffi_trampoline_table
 160 {
 161   /* contiguous writable and executable pages */
 162   vm_address_t config_page;
 163   vm_address_t trampoline_page;
 164 
 165   /* free list tracking */
 166   uint16_t free_count;
 167   ffi_trampoline_table_entry *free_list;
 168   ffi_trampoline_table_entry *free_list_pool;
 169 
 170   ffi_trampoline_table *prev;
 171   ffi_trampoline_table *next;
 172 };
 173 
 174 struct ffi_trampoline_table_entry
 175 {
 176   void *(*trampoline) (void);
 177   ffi_trampoline_table_entry *next;
 178 };
 179 
 180 /* Total number of trampolines that fit in one trampoline table */
 181 #define FFI_TRAMPOLINE_COUNT (PAGE_MAX_SIZE / FFI_TRAMPOLINE_SIZE)
 182 
 183 static pthread_mutex_t ffi_trampoline_lock = PTHREAD_MUTEX_INITIALIZER;
 184 static ffi_trampoline_table *ffi_trampoline_tables = NULL;
 185 
 186 static ffi_trampoline_table *
 187 ffi_trampoline_table_alloc (void)
 188 {
 189   ffi_trampoline_table *table;
 190   vm_address_t config_page;
 191   vm_address_t trampoline_page;
 192   vm_address_t trampoline_page_template;
 193   vm_prot_t cur_prot;
 194   vm_prot_t max_prot;
 195   kern_return_t kt;
 196   uint16_t i;
 197 
 198   /* Allocate two pages -- a config page and a placeholder page */
 199   config_page = 0x0;
 200   kt = vm_allocate (mach_task_self (), &config_page, PAGE_MAX_SIZE * 2,
 201             VM_FLAGS_ANYWHERE);
 202   if (kt != KERN_SUCCESS)
 203     return NULL;
 204 
 205   /* Remap the trampoline table on top of the placeholder page */
 206   trampoline_page = config_page + PAGE_MAX_SIZE;
 207   trampoline_page_template = (vm_address_t)&ffi_closure_trampoline_table_page;
 208 #ifdef __arm__
 209   /* ffi_closure_trampoline_table_page can be thumb-biased on some ARM archs */
 210   trampoline_page_template &= ~1UL;
 211 #endif
 212   kt = vm_remap (mach_task_self (), &trampoline_page, PAGE_MAX_SIZE, 0x0,
 213          VM_FLAGS_OVERWRITE, mach_task_self (), trampoline_page_template,
 214          FALSE, &cur_prot, &max_prot, VM_INHERIT_SHARE);
 215   if (kt != KERN_SUCCESS)
 216     {
 217       vm_deallocate (mach_task_self (), config_page, PAGE_MAX_SIZE * 2);
 218       return NULL;
 219     }
 220 
 221   /* We have valid trampoline and config pages */
 222   table = calloc (1, sizeof (ffi_trampoline_table));
 223   table->free_count = FFI_TRAMPOLINE_COUNT;
 224   table->config_page = config_page;
 225   table->trampoline_page = trampoline_page;
 226 
 227   /* Create and initialize the free list */
 228   table->free_list_pool =
 229     calloc (FFI_TRAMPOLINE_COUNT, sizeof (ffi_trampoline_table_entry));
 230 
 231   for (i = 0; i < table->free_count; i++)
 232     {
 233       ffi_trampoline_table_entry *entry = &table->free_list_pool[i];
 234       entry->trampoline =
 235     (void *) (table->trampoline_page + (i * FFI_TRAMPOLINE_SIZE));
 236 
 237       if (i < table->free_count - 1)
 238     entry->next = &table->free_list_pool[i + 1];
 239     }
 240 
 241   table->free_list = table->free_list_pool;
 242 
 243   return table;
 244 }
 245 
 246 static void
 247 ffi_trampoline_table_free (ffi_trampoline_table *table)
 248 {
 249   /* Remove from the list */
 250   if (table->prev != NULL)
 251     table->prev->next = table->next;
 252 
 253   if (table->next != NULL)
 254     table->next->prev = table->prev;
 255 
 256   /* Deallocate pages */
 257   vm_deallocate (mach_task_self (), table->config_page, PAGE_MAX_SIZE * 2);
 258 
 259   /* Deallocate free list */
 260   free (table->free_list_pool);
 261   free (table);
 262 }
 263 
 264 void *
 265 ffi_closure_alloc (size_t size, void **code)
 266 {
 267   /* Create the closure */
 268   ffi_closure *closure = malloc (size);
 269   if (closure == NULL)
 270     return NULL;
 271 
 272   pthread_mutex_lock (&ffi_trampoline_lock);
 273 
 274   /* Check for an active trampoline table with available entries. */
 275   ffi_trampoline_table *table = ffi_trampoline_tables;
 276   if (table == NULL || table->free_list == NULL)
 277     {
 278       table = ffi_trampoline_table_alloc ();
 279       if (table == NULL)
 280     {
 281       pthread_mutex_unlock (&ffi_trampoline_lock);
 282       free (closure);
 283       return NULL;
 284     }
 285 
 286       /* Insert the new table at the top of the list */
 287       table->next = ffi_trampoline_tables;
 288       if (table->next != NULL)
 289     table->next->prev = table;
 290 
 291       ffi_trampoline_tables = table;
 292     }
 293 
 294   /* Claim the free entry */
 295   ffi_trampoline_table_entry *entry = ffi_trampoline_tables->free_list;
 296   ffi_trampoline_tables->free_list = entry->next;
 297   ffi_trampoline_tables->free_count--;
 298   entry->next = NULL;
 299 
 300   pthread_mutex_unlock (&ffi_trampoline_lock);
 301 
 302   /* Initialize the return values */
 303   *code = entry->trampoline;
 304   closure->trampoline_table = table;
 305   closure->trampoline_table_entry = entry;
 306 
 307   return closure;
 308 }
 309 
 310 void
 311 ffi_closure_free (void *ptr)
 312 {
 313   ffi_closure *closure = ptr;
 314 
 315   pthread_mutex_lock (&ffi_trampoline_lock);
 316 
 317   /* Fetch the table and entry references */
 318   ffi_trampoline_table *table = closure->trampoline_table;
 319   ffi_trampoline_table_entry *entry = closure->trampoline_table_entry;
 320 
 321   /* Return the entry to the free list */
 322   entry->next = table->free_list;
 323   table->free_list = entry;
 324   table->free_count++;
 325 
 326   /* If all trampolines within this table are free, and at least one other table exists, deallocate
 327    * the table */
 328   if (table->free_count == FFI_TRAMPOLINE_COUNT
 329       && ffi_trampoline_tables != table)
 330     {
 331       ffi_trampoline_table_free (table);
 332     }
 333   else if (ffi_trampoline_tables != table)
 334     {
 335       /* Otherwise, bump this table to the top of the list */
 336       table->prev = NULL;
 337       table->next = ffi_trampoline_tables;
 338       if (ffi_trampoline_tables != NULL)
 339     ffi_trampoline_tables->prev = table;
 340 
 341       ffi_trampoline_tables = table;
 342     }
 343 
 344   pthread_mutex_unlock (&ffi_trampoline_lock);
 345 
 346   /* Free the closure */
 347   free (closure);
 348 }
 349 
 350 #endif
 351 
 352 // Per-target implementation; It's unclear what can reasonable be shared between two OS/architecture implementations.
 353 
 354 #elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
 355 
 356 #define USE_LOCKS 1
 357 #define USE_DL_PREFIX 1
 358 #ifdef __GNUC__
 359 #ifndef USE_BUILTIN_FFS
 360 #define USE_BUILTIN_FFS 1
 361 #endif
 362 #endif
 363 
 364 /* We need to use mmap, not sbrk.  */
 365 #define HAVE_MORECORE 0
 366 
 367 /* We could, in theory, support mremap, but it wouldn't buy us anything.  */
 368 #define HAVE_MREMAP 0
 369 
 370 /* We have no use for this, so save some code and data.  */
 371 #define NO_MALLINFO 1
 372 
 373 /* We need all allocations to be in regular segments, otherwise we
 374    lose track of the corresponding code address.  */
 375 #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
 376 
 377 /* Don't allocate more than a page unless needed.  */
 378 #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
 379 
 380 #include <sys/types.h>
 381 #include <sys/stat.h>
 382 #include <fcntl.h>
 383 #include <errno.h>
 384 #ifndef _MSC_VER
 385 #include <unistd.h>
 386 #endif
 387 #include <string.h>
 388 #include <stdio.h>
 389 #if !defined(X86_WIN32) && !defined(X86_WIN64) && !defined(_M_ARM64)
 390 #ifdef HAVE_MNTENT
 391 #include <mntent.h>
 392 #endif /* HAVE_MNTENT */
 393 #include <sys/param.h>
 394 #include <pthread.h>
 395 
 396 /* We don't want sys/mman.h to be included after we redefine mmap and
 397    dlmunmap.  */
 398 #include <sys/mman.h>
 399 #define LACKS_SYS_MMAN_H 1
 400 
 401 #if FFI_MMAP_EXEC_SELINUX
 402 #include <sys/statfs.h>
 403 #include <stdlib.h>
 404 
 405 static int selinux_enabled = -1;
 406 
 407 static int
 408 selinux_enabled_check (void)
 409 {
 410   struct statfs sfs;
 411   FILE *f;
 412   char *buf = NULL;
 413   size_t len = 0;
 414 
 415   if (statfs ("/selinux", &sfs) >= 0
 416       && (unsigned int) sfs.f_type == 0xf97cff8cU)
 417     return 1;
 418   f = fopen ("/proc/mounts", "r");
 419   if (f == NULL)
 420     return 0;
 421   while (getline (&buf, &len, f) >= 0)
 422     {
 423       char *p = strchr (buf, ' ');
 424       if (p == NULL)
 425         break;
 426       p = strchr (p + 1, ' ');
 427       if (p == NULL)
 428         break;
 429       if (strncmp (p + 1, "selinuxfs ", 10) == 0)
 430         {
 431           free (buf);
 432           fclose (f);
 433           return 1;
 434         }
 435     }
 436   free (buf);
 437   fclose (f);
 438   return 0;
 439 }
 440 
 441 #define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
 442                   : (selinux_enabled = selinux_enabled_check ()))
 443 
 444 #else
 445 
 446 #define is_selinux_enabled() 0
 447 
 448 #endif /* !FFI_MMAP_EXEC_SELINUX */
 449 
 450 /* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. */
 451 #ifdef FFI_MMAP_EXEC_EMUTRAMP_PAX
 452 #include <stdlib.h>
 453 
 454 static int emutramp_enabled = -1;
 455 
 456 static int
 457 emutramp_enabled_check (void)
 458 {
 459   char *buf = NULL;
 460   size_t len = 0;
 461   FILE *f;
 462   int ret;
 463   f = fopen ("/proc/self/status", "r");
 464   if (f == NULL)
 465     return 0;
 466   ret = 0;
 467 
 468   while (getline (&buf, &len, f) != -1)
 469     if (!strncmp (buf, "PaX:", 4))
 470       {
 471         char emutramp;
 472         if (sscanf (buf, "%*s %*c%c", &emutramp) == 1)
 473           ret = (emutramp == 'E');
 474         break;
 475       }
 476   free (buf);
 477   fclose (f);
 478   return ret;
 479 }
 480 
 481 #define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
 482                                : (emutramp_enabled = emutramp_enabled_check ()))
 483 #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
 484 
 485 #elif defined (__CYGWIN__) || defined(__INTERIX)
 486 
 487 #include <sys/mman.h>
 488 
 489 /* Cygwin is Linux-like, but not quite that Linux-like.  */
 490 #define is_selinux_enabled() 0
 491 
 492 #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
 493 
 494 #ifndef FFI_MMAP_EXEC_EMUTRAMP_PAX
 495 #define is_emutramp_enabled() 0
 496 #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
 497 
 498 /* Declare all functions defined in dlmalloc.c as static.  */
 499 static void *dlmalloc(size_t);
 500 static void dlfree(void*);
 501 static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
 502 static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
 503 static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
 504 static void *dlvalloc(size_t) MAYBE_UNUSED;
 505 static int dlmallopt(int, int) MAYBE_UNUSED;
 506 static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
 507 static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
 508 static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
 509 static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
 510 static void *dlpvalloc(size_t) MAYBE_UNUSED;
 511 static int dlmalloc_trim(size_t) MAYBE_UNUSED;
 512 static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
 513 static void dlmalloc_stats(void) MAYBE_UNUSED;
 514 
 515 #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(_M_ARM64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
 516 /* Use these for mmap and munmap within dlmalloc.c.  */
 517 static void *dlmmap(void *, size_t, int, int, int, off_t);
 518 static int dlmunmap(void *, size_t);
 519 #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
 520 
 521 #define mmap dlmmap
 522 #define munmap dlmunmap
 523 
 524 #include "dlmalloc.c"
 525 
 526 #undef mmap
 527 #undef munmap
 528 
 529 #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(_M_ARM64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
 530 
 531 /* A mutex used to synchronize access to *exec* variables in this file.  */
 532 static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
 533 
 534 /* A file descriptor of a temporary file from which we'll map
 535    executable pages.  */
 536 static int execfd = -1;
 537 
 538 /* The amount of space already allocated from the temporary file.  */
 539 static size_t execsize = 0;
 540 
 541 /* Open a temporary file name, and immediately unlink it.  */
 542 static int
 543 open_temp_exec_file_name (char *name, int flags)
 544 {
 545   int fd;
 546 
 547 #ifdef HAVE_MKOSTEMP
 548   fd = mkostemp (name, flags);
 549 #else
 550   fd = mkstemp (name);
 551 #endif
 552 
 553   if (fd != -1)
 554     unlink (name);
 555 
 556   return fd;
 557 }
 558 
 559 /* Open a temporary file in the named directory.  */
 560 static int
 561 open_temp_exec_file_dir (const char *dir)
 562 {
 563   static const char suffix[] = "/ffiXXXXXX";
 564   int lendir, flags;
 565   char *tempname;
 566 #ifdef O_TMPFILE
 567   int fd;
 568 #endif
 569 
 570 #ifdef O_CLOEXEC
 571   flags = O_CLOEXEC;
 572 #else
 573   flags = 0;
 574 #endif
 575 
 576 #ifdef O_TMPFILE
 577   fd = open (dir, flags | O_RDWR | O_EXCL | O_TMPFILE, 0700);
 578   /* If the running system does not support the O_TMPFILE flag then retry without it. */
 579   if (fd != -1 || (errno != EINVAL && errno != EISDIR && errno != EOPNOTSUPP)) {
 580     return fd;
 581   } else {
 582     errno = 0;
 583   }
 584 #endif
 585 
 586   lendir = (int) strlen (dir);
 587   tempname = __builtin_alloca (lendir + sizeof (suffix));
 588 
 589   if (!tempname)
 590     return -1;
 591 
 592   memcpy (tempname, dir, lendir);
 593   memcpy (tempname + lendir, suffix, sizeof (suffix));
 594 
 595   return open_temp_exec_file_name (tempname, flags);
 596 }
 597 
 598 /* Open a temporary file in the directory in the named environment
 599    variable.  */
 600 static int
 601 open_temp_exec_file_env (const char *envvar)
 602 {
 603   const char *value = getenv (envvar);
 604 
 605   if (!value)
 606     return -1;
 607 
 608   return open_temp_exec_file_dir (value);
 609 }
 610 
 611 #ifdef HAVE_MNTENT
 612 /* Open a temporary file in an executable and writable mount point
 613    listed in the mounts file.  Subsequent calls with the same mounts
 614    keep searching for mount points in the same file.  Providing NULL
 615    as the mounts file closes the file.  */
 616 static int
 617 open_temp_exec_file_mnt (const char *mounts)
 618 {
 619   static const char *last_mounts;
 620   static FILE *last_mntent;
 621 
 622   if (mounts != last_mounts)
 623     {
 624       if (last_mntent)
 625     endmntent (last_mntent);
 626 
 627       last_mounts = mounts;
 628 
 629       if (mounts)
 630     last_mntent = setmntent (mounts, "r");
 631       else
 632     last_mntent = NULL;
 633     }
 634 
 635   if (!last_mntent)
 636     return -1;
 637 
 638   for (;;)
 639     {
 640       int fd;
 641       struct mntent mnt;
 642       char buf[MAXPATHLEN * 3];
 643 
 644       if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
 645     return -1;
 646 
 647       if (hasmntopt (&mnt, "ro")
 648       || hasmntopt (&mnt, "noexec")
 649       || access (mnt.mnt_dir, W_OK))
 650     continue;
 651 
 652       fd = open_temp_exec_file_dir (mnt.mnt_dir);
 653 
 654       if (fd != -1)
 655     return fd;
 656     }
 657 }
 658 #endif /* HAVE_MNTENT */
 659 
 660 /* Instructions to look for a location to hold a temporary file that
 661    can be mapped in for execution.  */
 662 static struct
 663 {
 664   int (*func)(const char *);
 665   const char *arg;
 666   int repeat;
 667 } open_temp_exec_file_opts[] = {
 668   { open_temp_exec_file_env, "TMPDIR", 0 },
 669   { open_temp_exec_file_dir, "/tmp", 0 },
 670   { open_temp_exec_file_dir, "/var/tmp", 0 },
 671   { open_temp_exec_file_dir, "/dev/shm", 0 },
 672   { open_temp_exec_file_env, "HOME", 0 },
 673 #ifdef HAVE_MNTENT
 674   { open_temp_exec_file_mnt, "/etc/mtab", 1 },
 675   { open_temp_exec_file_mnt, "/proc/mounts", 1 },
 676 #endif /* HAVE_MNTENT */
 677 };
 678 
 679 /* Current index into open_temp_exec_file_opts.  */
 680 static int open_temp_exec_file_opts_idx = 0;
 681 
 682 /* Reset a current multi-call func, then advances to the next entry.
 683    If we're at the last, go back to the first and return nonzero,
 684    otherwise return zero.  */
 685 static int
 686 open_temp_exec_file_opts_next (void)
 687 {
 688   if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
 689     open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
 690 
 691   open_temp_exec_file_opts_idx++;
 692   if (open_temp_exec_file_opts_idx
 693       == (sizeof (open_temp_exec_file_opts)
 694       / sizeof (*open_temp_exec_file_opts)))
 695     {
 696       open_temp_exec_file_opts_idx = 0;
 697       return 1;
 698     }
 699 
 700   return 0;
 701 }
 702 
 703 /* Return a file descriptor of a temporary zero-sized file in a
 704    writable and executable filesystem.  */
 705 static int
 706 open_temp_exec_file (void)
 707 {
 708   int fd;
 709 
 710   do
 711     {
 712       fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
 713     (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
 714 
 715       if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
 716       || fd == -1)
 717     {
 718       if (open_temp_exec_file_opts_next ())
 719         break;
 720     }
 721     }
 722   while (fd == -1);
 723 
 724   return fd;
 725 }
 726 
 727 /* We need to allocate space in a file that will be backing a writable
 728    mapping.  Several problems exist with the usual approaches:
 729    - fallocate() is Linux-only
 730    - posix_fallocate() is not available on all platforms
 731    - ftruncate() does not allocate space on filesystems with sparse files
 732    Failure to allocate the space will cause SIGBUS to be thrown when
 733    the mapping is subsequently written to.  */
 734 static int
 735 allocate_space (int fd, off_t offset, off_t len)
 736 {
 737   static size_t page_size;
 738 
 739   /* Obtain system page size. */
 740   if (!page_size)
 741     page_size = sysconf(_SC_PAGESIZE);
 742 
 743   unsigned char buf[page_size];
 744   memset (buf, 0, page_size);
 745 
 746   while (len > 0)
 747     {
 748       off_t to_write = (len < page_size) ? len : page_size;
 749       if (write (fd, buf, to_write) < to_write)
 750         return -1;
 751       len -= to_write;
 752     }
 753 
 754   return 0;
 755 }
 756 
 757 /* Map in a chunk of memory from the temporary exec file into separate
 758    locations in the virtual memory address space, one writable and one
 759    executable.  Returns the address of the writable portion, after
 760    storing an offset to the corresponding executable portion at the
 761    last word of the requested chunk.  */
 762 static void *
 763 dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
 764 {
 765   void *ptr;
 766 
 767   if (execfd == -1)
 768     {
 769       open_temp_exec_file_opts_idx = 0;
 770     retry_open:
 771       execfd = open_temp_exec_file ();
 772       if (execfd == -1)
 773     return MFAIL;
 774     }
 775 
 776   offset = execsize;
 777 
 778   if (allocate_space (execfd, offset, length))
 779     return MFAIL;
 780 
 781   flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
 782   flags |= MAP_SHARED;
 783 
 784   ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
 785           flags, execfd, offset);
 786   if (ptr == MFAIL)
 787     {
 788       if (!offset)
 789     {
 790       close (execfd);
 791       goto retry_open;
 792     }
 793       if (ftruncate (execfd, offset) != 0)
 794       {
 795         /* Fixme : Error logs can be added here. Returning an error for
 796          * ftruncte() will not add any advantage as it is being
 797          * validating in the error case. */
 798       }
 799 
 800       return MFAIL;
 801     }
 802   else if (!offset
 803        && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
 804     open_temp_exec_file_opts_next ();
 805 
 806   start = mmap (start, length, prot, flags, execfd, offset);
 807 
 808   if (start == MFAIL)
 809     {
 810       munmap (ptr, length);
 811       if (ftruncate (execfd, offset) != 0)
 812       {
 813         /* Fixme : Error logs can be added here. Returning an error for
 814          * ftruncte() will not add any advantage as it is being
 815          * validating in the error case. */
 816       }
 817       return start;
 818     }
 819 
 820   mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
 821 
 822   execsize += length;
 823 
 824   return start;
 825 }
 826 
 827 /* Map in a writable and executable chunk of memory if possible.
 828    Failing that, fall back to dlmmap_locked.  */
 829 static void *
 830 dlmmap (void *start, size_t length, int prot,
 831     int flags, int fd, off_t offset)
 832 {
 833   void *ptr;
 834 
 835   assert (start == NULL && length % malloc_getpagesize == 0
 836       && prot == (PROT_READ | PROT_WRITE)
 837       && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
 838       && fd == -1 && offset == 0);
 839 
 840   if (execfd == -1 && is_emutramp_enabled ())
 841     {
 842       ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
 843       return ptr;
 844     }
 845 
 846   if (execfd == -1 && !is_selinux_enabled ())
 847     {
 848       ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
 849 
 850       if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
 851     /* Cool, no need to mess with separate segments.  */
 852     return ptr;
 853 
 854       /* If MREMAP_DUP is ever introduced and implemented, try mmap
 855      with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
 856      MREMAP_DUP and prot at this point.  */
 857     }
 858 
 859   if (execsize == 0 || execfd == -1)
 860     {
 861       pthread_mutex_lock (&open_temp_exec_file_mutex);
 862       ptr = dlmmap_locked (start, length, prot, flags, offset);
 863       pthread_mutex_unlock (&open_temp_exec_file_mutex);
 864 
 865       return ptr;
 866     }
 867 
 868   return dlmmap_locked (start, length, prot, flags, offset);
 869 }
 870 
 871 /* Release memory at the given address, as well as the corresponding
 872    executable page if it's separate.  */
 873 static int
 874 dlmunmap (void *start, size_t length)
 875 {
 876   /* We don't bother decreasing execsize or truncating the file, since
 877      we can't quite tell whether we're unmapping the end of the file.
 878      We don't expect frequent deallocation anyway.  If we did, we
 879      could locate pages in the file by writing to the pages being
 880      deallocated and checking that the file contents change.
 881      Yuck.  */
 882   msegmentptr seg = segment_holding (gm, start);
 883   void *code;
 884 
 885   if (seg && (code = add_segment_exec_offset (start, seg)) != start)
 886     {
 887       int ret = munmap (code, length);
 888       if (ret)
 889     return ret;
 890     }
 891 
 892   return munmap (start, length);
 893 }
 894 
 895 #if FFI_CLOSURE_FREE_CODE
 896 /* Return segment holding given code address.  */
 897 static msegmentptr
 898 segment_holding_code (mstate m, char* addr)
 899 {
 900   msegmentptr sp = &m->seg;
 901   for (;;) {
 902     if (addr >= add_segment_exec_offset (sp->base, sp)
 903     && addr < add_segment_exec_offset (sp->base, sp) + sp->size)
 904       return sp;
 905     if ((sp = sp->next) == 0)
 906       return 0;
 907   }
 908 }
 909 #endif
 910 
 911 #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(_M_ARM64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
 912 
 913 /* Allocate a chunk of memory with the given size.  Returns a pointer
 914    to the writable address, and sets *CODE to the executable
 915    corresponding virtual address.  */
 916 void *
 917 ffi_closure_alloc (size_t size, void **code)
 918 {
 919   void *ptr;
 920 
 921   if (!code)
 922     return NULL;
 923 
 924   ptr = dlmalloc (size);
 925 
 926   if (ptr)
 927     {
 928       msegmentptr seg = segment_holding (gm, ptr);
 929 #ifdef GSTREAMER_LITE
 930       if (seg == NULL)
 931         return NULL;
 932 #endif // GSTREAMER_LITE
 933 
 934       *code = add_segment_exec_offset (ptr, seg);
 935     }
 936 
 937   return ptr;
 938 }
 939 
 940 void *
 941 ffi_data_to_code_pointer (void *data)
 942 {
 943   msegmentptr seg = segment_holding (gm, data);
 944   /* We expect closures to be allocated with ffi_closure_alloc(), in
 945      which case seg will be non-NULL.  However, some users take on the
 946      burden of managing this memory themselves, in which case this
 947      we'll just return data. */
 948   if (seg)
 949     return add_segment_exec_offset (data, seg);
 950   else
 951     return data;
 952 }
 953 
 954 /* Release a chunk of memory allocated with ffi_closure_alloc.  If
 955    FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
 956    writable or the executable address given.  Otherwise, only the
 957    writable address can be provided here.  */
 958 void
 959 ffi_closure_free (void *ptr)
 960 {
 961 #if FFI_CLOSURE_FREE_CODE
 962   msegmentptr seg = segment_holding_code (gm, ptr);
 963 
 964   if (seg)
 965     ptr = sub_segment_exec_offset (ptr, seg);
 966 #endif
 967 
 968   dlfree (ptr);
 969 }
 970 
 971 # else /* ! FFI_MMAP_EXEC_WRIT */
 972 
 973 /* On many systems, memory returned by malloc is writable and
 974    executable, so just use it.  */
 975 
 976 #include <stdlib.h>
 977 
 978 void *
 979 ffi_closure_alloc (size_t size, void **code)
 980 {
 981   if (!code)
 982     return NULL;
 983 
 984   return *code = malloc (size);
 985 }
 986 
 987 void
 988 ffi_closure_free (void *ptr)
 989 {
 990   free (ptr);
 991 }
 992 
 993 void *
 994 ffi_data_to_code_pointer (void *data)
 995 {
 996   return data;
 997 }
 998 
 999 # endif /* ! FFI_MMAP_EXEC_WRIT */
1000 #endif /* FFI_CLOSURES */
1001 
1002 #endif /* NetBSD with PROT_MPROTECT */