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