1/* -----------------------------------------------------------------------
2   closures.c - Copyright (c) 2007, 2009, 2010  Red Hat, Inc.
3                Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc
4                Copyright (c) 2011 Plausible Labs Cooperative, Inc.
5
6   Code to allocate and deallocate memory for closures.
7
8   Permission is hereby granted, free of charge, to any person obtaining
9   a copy of this software and associated documentation files (the
10   ``Software''), to deal in the Software without restriction, including
11   without limitation the rights to use, copy, modify, merge, publish,
12   distribute, sublicense, and/or sell copies of the Software, and to
13   permit persons to whom the Software is furnished to do so, subject to
14   the following conditions:
15
16   The above copyright notice and this permission notice shall be included
17   in all copies or substantial portions of the Software.
18
19   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
20   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22   NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26   DEALINGS IN THE SOFTWARE.
27   ----------------------------------------------------------------------- */
28
29#if defined __linux__ && !defined _GNU_SOURCE
30#define _GNU_SOURCE 1
31#endif
32
33#include <ffi.h>
34#include <ffi_common.h>
35
36#if !FFI_MMAP_EXEC_WRIT && !FFI_EXEC_TRAMPOLINE_TABLE
37# if __gnu_linux__ && !defined(__ANDROID__)
38/* This macro indicates it may be forbidden to map anonymous memory
39   with both write and execute permission.  Code compiled when this
40   option is defined will attempt to map such pages once, but if it
41   fails, it falls back to creating a temporary file in a writable and
42   executable filesystem and mapping pages from it into separate
43   locations in the virtual memory space, one location writable and
44   another executable.  */
45#  define FFI_MMAP_EXEC_WRIT 1
46#  define HAVE_MNTENT 1
47# endif
48# if defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)
49/* Windows systems may have Data Execution Protection (DEP) enabled,
50   which requires the use of VirtualMalloc/VirtualFree to alloc/free
51   executable memory. */
52#  define FFI_MMAP_EXEC_WRIT 1
53# endif
54#endif
55
56#if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
57# ifdef __linux__
58/* When defined to 1 check for SELinux and if SELinux is active,
59   don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
60   might cause audit messages.  */
61#  define FFI_MMAP_EXEC_SELINUX 1
62# endif
63#endif
64
65#if FFI_CLOSURES
66
67# if FFI_EXEC_TRAMPOLINE_TABLE
68
69// Per-target implementation; It's unclear what can reasonable be shared between two OS/architecture implementations.
70
71# elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
72
73#define USE_LOCKS 1
74#define USE_DL_PREFIX 1
75#ifdef __GNUC__
76#ifndef USE_BUILTIN_FFS
77#define USE_BUILTIN_FFS 1
78#endif
79#endif
80
81/* We need to use mmap, not sbrk.  */
82#define HAVE_MORECORE 0
83
84/* We could, in theory, support mremap, but it wouldn't buy us anything.  */
85#define HAVE_MREMAP 0
86
87/* We have no use for this, so save some code and data.  */
88#define NO_MALLINFO 1
89
90/* We need all allocations to be in regular segments, otherwise we
91   lose track of the corresponding code address.  */
92#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
93
94/* Don't allocate more than a page unless needed.  */
95#define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
96
97#if FFI_CLOSURE_TEST
98/* Don't release single pages, to avoid a worst-case scenario of
99   continuously allocating and releasing single pages, but release
100   pairs of pages, which should do just as well given that allocations
101   are likely to be small.  */
102#define DEFAULT_TRIM_THRESHOLD ((size_t)malloc_getpagesize)
103#endif
104
105#include <sys/types.h>
106#include <sys/stat.h>
107#include <fcntl.h>
108#include <errno.h>
109#ifndef _MSC_VER
110#include <unistd.h>
111#endif
112#include <string.h>
113#include <stdio.h>
114#if !defined(X86_WIN32) && !defined(X86_WIN64)
115#ifdef HAVE_MNTENT
116#include <mntent.h>
117#endif /* HAVE_MNTENT */
118#include <sys/param.h>
119#include <pthread.h>
120
121/* We don't want sys/mman.h to be included after we redefine mmap and
122   dlmunmap.  */
123#include <sys/mman.h>
124#define LACKS_SYS_MMAN_H 1
125
126#if FFI_MMAP_EXEC_SELINUX
127#include <sys/statfs.h>
128#include <stdlib.h>
129
130static int selinux_enabled = -1;
131
132static int
133selinux_enabled_check (void)
134{
135  struct statfs sfs;
136  FILE *f;
137  char *buf = NULL;
138  size_t len = 0;
139
140  if (statfs ("/selinux", &sfs) >= 0
141      && (unsigned int) sfs.f_type == 0xf97cff8cU)
142    return 1;
143  f = fopen ("/proc/mounts", "r");
144  if (f == NULL)
145    return 0;
146  while (getline (&buf, &len, f) >= 0)
147    {
148      char *p = strchr (buf, ' ');
149      if (p == NULL)
150        break;
151      p = strchr (p + 1, ' ');
152      if (p == NULL)
153        break;
154      if (strncmp (p + 1, "selinuxfs ", 10) == 0)
155        {
156          free (buf);
157          fclose (f);
158          return 1;
159        }
160    }
161  free (buf);
162  fclose (f);
163  return 0;
164}
165
166#define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
167			      : (selinux_enabled = selinux_enabled_check ()))
168
169#else
170
171#define is_selinux_enabled() 0
172
173#endif /* !FFI_MMAP_EXEC_SELINUX */
174
175/* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. */
176#ifdef FFI_MMAP_EXEC_EMUTRAMP_PAX
177#include <stdlib.h>
178
179static int emutramp_enabled = -1;
180
181static int
182emutramp_enabled_check (void)
183{
184  char *buf = NULL;
185  size_t len = 0;
186  FILE *f;
187  int ret;
188  f = fopen ("/proc/self/status", "r");
189  if (f == NULL)
190    return 0;
191  ret = 0;
192
193  while (getline (&buf, &len, f) != -1)
194    if (!strncmp (buf, "PaX:", 4))
195      {
196        char emutramp;
197        if (sscanf (buf, "%*s %*c%c", &emutramp) == 1)
198          ret = (emutramp == 'E');
199        break;
200      }
201  free (buf);
202  fclose (f);
203  return ret;
204}
205
206#define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
207                               : (emutramp_enabled = emutramp_enabled_check ()))
208#endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
209
210#elif defined (__CYGWIN__) || defined(__INTERIX)
211
212#include <sys/mman.h>
213
214/* Cygwin is Linux-like, but not quite that Linux-like.  */
215#define is_selinux_enabled() 0
216
217#endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
218
219#ifndef FFI_MMAP_EXEC_EMUTRAMP_PAX
220#define is_emutramp_enabled() 0
221#endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
222
223/* Declare all functions defined in dlmalloc.c as static.  */
224static void *dlmalloc(size_t);
225static void dlfree(void*);
226static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
227static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
228static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
229static void *dlvalloc(size_t) MAYBE_UNUSED;
230static int dlmallopt(int, int) MAYBE_UNUSED;
231static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
232static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
233static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
234static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
235static void *dlpvalloc(size_t) MAYBE_UNUSED;
236static int dlmalloc_trim(size_t) MAYBE_UNUSED;
237static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
238static void dlmalloc_stats(void) MAYBE_UNUSED;
239
240#if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
241/* Use these for mmap and munmap within dlmalloc.c.  */
242static void *dlmmap(void *, size_t, int, int, int, off_t);
243static int dlmunmap(void *, size_t);
244#endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
245
246#define mmap dlmmap
247#define munmap dlmunmap
248
249#include "dlmalloc.c"
250
251#undef mmap
252#undef munmap
253
254#if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
255
256/* A mutex used to synchronize access to *exec* variables in this file.  */
257static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
258
259/* A file descriptor of a temporary file from which we'll map
260   executable pages.  */
261static int execfd = -1;
262
263/* The amount of space already allocated from the temporary file.  */
264static size_t execsize = 0;
265
266/* Open a temporary file name, and immediately unlink it.  */
267static int
268open_temp_exec_file_name (char *name, int flags)
269{
270  int fd;
271
272#ifdef HAVE_MKOSTEMP
273  fd = mkostemp (name, flags);
274#else
275  fd = mkstemp (name);
276#endif
277
278  if (fd != -1)
279    unlink (name);
280
281  return fd;
282}
283
284/* Open a temporary file in the named directory.  */
285static int
286open_temp_exec_file_dir (const char *dir)
287{
288  static const char suffix[] = "/ffiXXXXXX";
289  int lendir, flags;
290  char *tempname;
291#ifdef O_TMPFILE
292  int fd;
293#endif
294
295#ifdef O_CLOEXEC
296  flags = O_CLOEXEC;
297#else
298  flags = 0;
299#endif
300
301#ifdef O_TMPFILE
302  fd = open (dir, flags | O_RDWR | O_EXCL | O_TMPFILE, 0700);
303  /* If the running system does not support the O_TMPFILE flag then retry without it. */
304  if (fd != -1 || (errno != EINVAL && errno != EISDIR && errno != EOPNOTSUPP)) {
305    return fd;
306  } else {
307    errno = 0;
308  }
309#endif
310
311  lendir = strlen (dir);
312  tempname = __builtin_alloca (lendir + sizeof (suffix));
313
314  if (!tempname)
315    return -1;
316
317  memcpy (tempname, dir, lendir);
318  memcpy (tempname + lendir, suffix, sizeof (suffix));
319
320  return open_temp_exec_file_name (tempname, flags);
321}
322
323/* Open a temporary file in the directory in the named environment
324   variable.  */
325static int
326open_temp_exec_file_env (const char *envvar)
327{
328  const char *value = getenv (envvar);
329
330  if (!value)
331    return -1;
332
333  return open_temp_exec_file_dir (value);
334}
335
336#ifdef HAVE_MNTENT
337/* Open a temporary file in an executable and writable mount point
338   listed in the mounts file.  Subsequent calls with the same mounts
339   keep searching for mount points in the same file.  Providing NULL
340   as the mounts file closes the file.  */
341static int
342open_temp_exec_file_mnt (const char *mounts)
343{
344  static const char *last_mounts;
345  static FILE *last_mntent;
346
347  if (mounts != last_mounts)
348    {
349      if (last_mntent)
350	endmntent (last_mntent);
351
352      last_mounts = mounts;
353
354      if (mounts)
355	last_mntent = setmntent (mounts, "r");
356      else
357	last_mntent = NULL;
358    }
359
360  if (!last_mntent)
361    return -1;
362
363  for (;;)
364    {
365      int fd;
366      struct mntent mnt;
367      char buf[MAXPATHLEN * 3];
368
369      if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
370	return -1;
371
372      if (hasmntopt (&mnt, "ro")
373	  || hasmntopt (&mnt, "noexec")
374	  || access (mnt.mnt_dir, W_OK))
375	continue;
376
377      fd = open_temp_exec_file_dir (mnt.mnt_dir);
378
379      if (fd != -1)
380	return fd;
381    }
382}
383#endif /* HAVE_MNTENT */
384
385/* Instructions to look for a location to hold a temporary file that
386   can be mapped in for execution.  */
387static struct
388{
389  int (*func)(const char *);
390  const char *arg;
391  int repeat;
392} open_temp_exec_file_opts[] = {
393  { open_temp_exec_file_env, "TMPDIR", 0 },
394  { open_temp_exec_file_dir, "/tmp", 0 },
395  { open_temp_exec_file_dir, "/var/tmp", 0 },
396  { open_temp_exec_file_dir, "/dev/shm", 0 },
397  { open_temp_exec_file_env, "HOME", 0 },
398#ifdef HAVE_MNTENT
399  { open_temp_exec_file_mnt, "/etc/mtab", 1 },
400  { open_temp_exec_file_mnt, "/proc/mounts", 1 },
401#endif /* HAVE_MNTENT */
402};
403
404/* Current index into open_temp_exec_file_opts.  */
405static int open_temp_exec_file_opts_idx = 0;
406
407/* Reset a current multi-call func, then advances to the next entry.
408   If we're at the last, go back to the first and return nonzero,
409   otherwise return zero.  */
410static int
411open_temp_exec_file_opts_next (void)
412{
413  if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
414    open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
415
416  open_temp_exec_file_opts_idx++;
417  if (open_temp_exec_file_opts_idx
418      == (sizeof (open_temp_exec_file_opts)
419	  / sizeof (*open_temp_exec_file_opts)))
420    {
421      open_temp_exec_file_opts_idx = 0;
422      return 1;
423    }
424
425  return 0;
426}
427
428/* Return a file descriptor of a temporary zero-sized file in a
429   writable and executable filesystem.  */
430static int
431open_temp_exec_file (void)
432{
433  int fd;
434
435  do
436    {
437      fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
438	(open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
439
440      if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
441	  || fd == -1)
442	{
443	  if (open_temp_exec_file_opts_next ())
444	    break;
445	}
446    }
447  while (fd == -1);
448
449  return fd;
450}
451
452/* Map in a chunk of memory from the temporary exec file into separate
453   locations in the virtual memory address space, one writable and one
454   executable.  Returns the address of the writable portion, after
455   storing an offset to the corresponding executable portion at the
456   last word of the requested chunk.  */
457static void *
458dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
459{
460  void *ptr;
461
462  if (execfd == -1)
463    {
464      open_temp_exec_file_opts_idx = 0;
465    retry_open:
466      execfd = open_temp_exec_file ();
467      if (execfd == -1)
468	return MFAIL;
469    }
470
471  offset = execsize;
472
473  if (ftruncate (execfd, offset + length))
474    return MFAIL;
475
476  flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
477  flags |= MAP_SHARED;
478
479  ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
480	      flags, execfd, offset);
481  if (ptr == MFAIL)
482    {
483      if (!offset)
484	{
485	  close (execfd);
486	  goto retry_open;
487	}
488      ftruncate (execfd, offset);
489      return MFAIL;
490    }
491  else if (!offset
492	   && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
493    open_temp_exec_file_opts_next ();
494
495  start = mmap (start, length, prot, flags, execfd, offset);
496
497  if (start == MFAIL)
498    {
499      munmap (ptr, length);
500      ftruncate (execfd, offset);
501      return start;
502    }
503
504  mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
505
506  execsize += length;
507
508  return start;
509}
510
511/* Map in a writable and executable chunk of memory if possible.
512   Failing that, fall back to dlmmap_locked.  */
513static void *
514dlmmap (void *start, size_t length, int prot,
515	int flags, int fd, off_t offset)
516{
517  void *ptr;
518
519  assert (start == NULL && length % malloc_getpagesize == 0
520	  && prot == (PROT_READ | PROT_WRITE)
521	  && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
522	  && fd == -1 && offset == 0);
523
524#if FFI_CLOSURE_TEST
525  printf ("mapping in %zi\n", length);
526#endif
527
528  if (execfd == -1 && is_emutramp_enabled ())
529    {
530      ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
531      return ptr;
532    }
533
534  if (execfd == -1 && !is_selinux_enabled ())
535    {
536      ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
537
538      if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
539	/* Cool, no need to mess with separate segments.  */
540	return ptr;
541
542      /* If MREMAP_DUP is ever introduced and implemented, try mmap
543	 with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
544	 MREMAP_DUP and prot at this point.  */
545    }
546
547  if (execsize == 0 || execfd == -1)
548    {
549      pthread_mutex_lock (&open_temp_exec_file_mutex);
550      ptr = dlmmap_locked (start, length, prot, flags, offset);
551      pthread_mutex_unlock (&open_temp_exec_file_mutex);
552
553      return ptr;
554    }
555
556  return dlmmap_locked (start, length, prot, flags, offset);
557}
558
559/* Release memory at the given address, as well as the corresponding
560   executable page if it's separate.  */
561static int
562dlmunmap (void *start, size_t length)
563{
564  /* We don't bother decreasing execsize or truncating the file, since
565     we can't quite tell whether we're unmapping the end of the file.
566     We don't expect frequent deallocation anyway.  If we did, we
567     could locate pages in the file by writing to the pages being
568     deallocated and checking that the file contents change.
569     Yuck.  */
570  msegmentptr seg = segment_holding (gm, start);
571  void *code;
572
573#if FFI_CLOSURE_TEST
574  printf ("unmapping %zi\n", length);
575#endif
576
577  if (seg && (code = add_segment_exec_offset (start, seg)) != start)
578    {
579      int ret = munmap (code, length);
580      if (ret)
581	return ret;
582    }
583
584  return munmap (start, length);
585}
586
587#if FFI_CLOSURE_FREE_CODE
588/* Return segment holding given code address.  */
589static msegmentptr
590segment_holding_code (mstate m, char* addr)
591{
592  msegmentptr sp = &m->seg;
593  for (;;) {
594    if (addr >= add_segment_exec_offset (sp->base, sp)
595	&& addr < add_segment_exec_offset (sp->base, sp) + sp->size)
596      return sp;
597    if ((sp = sp->next) == 0)
598      return 0;
599  }
600}
601#endif
602
603#endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
604
605/* Allocate a chunk of memory with the given size.  Returns a pointer
606   to the writable address, and sets *CODE to the executable
607   corresponding virtual address.  */
608void *
609ffi_closure_alloc (size_t size, void **code)
610{
611  void *ptr;
612
613  if (!code)
614    return NULL;
615
616  ptr = dlmalloc (size);
617
618  if (ptr)
619    {
620      msegmentptr seg = segment_holding (gm, ptr);
621
622      *code = add_segment_exec_offset (ptr, seg);
623    }
624
625  return ptr;
626}
627
628/* Release a chunk of memory allocated with ffi_closure_alloc.  If
629   FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
630   writable or the executable address given.  Otherwise, only the
631   writable address can be provided here.  */
632void
633ffi_closure_free (void *ptr)
634{
635#if FFI_CLOSURE_FREE_CODE
636  msegmentptr seg = segment_holding_code (gm, ptr);
637
638  if (seg)
639    ptr = sub_segment_exec_offset (ptr, seg);
640#endif
641
642  dlfree (ptr);
643}
644
645
646#if FFI_CLOSURE_TEST
647/* Do some internal sanity testing to make sure allocation and
648   deallocation of pages are working as intended.  */
649int main ()
650{
651  void *p[3];
652#define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
653#define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
654  GET (0, malloc_getpagesize / 2);
655  GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
656  PUT (1);
657  GET (1, 2 * malloc_getpagesize);
658  GET (2, malloc_getpagesize / 2);
659  PUT (1);
660  PUT (0);
661  PUT (2);
662  return 0;
663}
664#endif /* FFI_CLOSURE_TEST */
665# else /* ! FFI_MMAP_EXEC_WRIT */
666
667/* On many systems, memory returned by malloc is writable and
668   executable, so just use it.  */
669
670#include <stdlib.h>
671
672void *
673ffi_closure_alloc (size_t size, void **code)
674{
675  if (!code)
676    return NULL;
677
678  return *code = malloc (size);
679}
680
681void
682ffi_closure_free (void *ptr)
683{
684  free (ptr);
685}
686
687# endif /* ! FFI_MMAP_EXEC_WRIT */
688#endif /* FFI_CLOSURES */
689