Home | History | Annotate | Download | only in src
      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 
    109 static int selinux_enabled = -1;
    110 
    111 static int
    112 selinux_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.  */
    155 static void *dlmalloc(size_t);
    156 static void dlfree(void*);
    157 static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
    158 static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
    159 static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
    160 static void *dlvalloc(size_t) MAYBE_UNUSED;
    161 static int dlmallopt(int, int) MAYBE_UNUSED;
    162 static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
    163 static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
    164 static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
    165 static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
    166 static void *dlpvalloc(size_t) MAYBE_UNUSED;
    167 static int dlmalloc_trim(size_t) MAYBE_UNUSED;
    168 static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
    169 static void dlmalloc_stats(void) MAYBE_UNUSED;
    170 
    171 /* Use these for mmap and munmap within dlmalloc.c.  */
    172 static void *dlmmap(void *, size_t, int, int, int, off_t);
    173 static 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.  */
    184 static 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.  */
    188 static int execfd = -1;
    189 
    190 /* The amount of space already allocated from the temporary file.  */
    191 static size_t execsize = 0;
    192 
    193 /* Open a temporary file name, and immediately unlink it.  */
    194 static int
    195 open_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.  */
    206 static int
    207 open_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.  */
    224 static int
    225 open_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.  */
    239 static int
    240 open_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.  */
    284 static 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.  */
    300 static 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.  */
    305 static int
    306 open_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.  */
    325 static int
    326 open_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.  */
    352 static void *
    353 dlmmap_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.  */
    408 static void *
    409 dlmmap (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.  */
    450 static int
    451 dlmunmap (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.  */
    478 static msegmentptr
    479 segment_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.  */
    495 void *
    496 ffi_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.  */
    519 void
    520 ffi_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.  */
    536 int 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 
    559 void *
    560 ffi_closure_alloc (size_t size, void **code)
    561 {
    562   if (!code)
    563     return NULL;
    564 
    565   return *code = malloc (size);
    566 }
    567 
    568 void
    569 ffi_closure_free (void *ptr)
    570 {
    571   free (ptr);
    572 }
    573 
    574 # endif /* ! FFI_MMAP_EXEC_WRIT */
    575 #endif /* FFI_CLOSURES */
    576