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__
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 
130 static int selinux_enabled = -1;
131 
132 static int
selinux_enabled_check(void)133 selinux_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 
179 static int emutramp_enabled = -1;
180 
181 static int
emutramp_enabled_check(void)182 emutramp_enabled_check (void)
183 {
184   if (getenv ("FFI_DISABLE_EMUTRAMP") == NULL)
185     return 1;
186   else
187     return 0;
188 }
189 
190 #define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
191                                : (emutramp_enabled = emutramp_enabled_check ()))
192 #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
193 
194 #elif defined (__CYGWIN__) || defined(__INTERIX)
195 
196 #include <sys/mman.h>
197 
198 /* Cygwin is Linux-like, but not quite that Linux-like.  */
199 #define is_selinux_enabled() 0
200 
201 #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
202 
203 #ifndef FFI_MMAP_EXEC_EMUTRAMP_PAX
204 #define is_emutramp_enabled() 0
205 #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
206 
207 /* Declare all functions defined in dlmalloc.c as static.  */
208 static void *dlmalloc(size_t);
209 static void dlfree(void*);
210 static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
211 static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
212 static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
213 static void *dlvalloc(size_t) MAYBE_UNUSED;
214 static int dlmallopt(int, int) MAYBE_UNUSED;
215 static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
216 static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
217 static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
218 static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
219 static void *dlpvalloc(size_t) MAYBE_UNUSED;
220 static int dlmalloc_trim(size_t) MAYBE_UNUSED;
221 static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
222 static void dlmalloc_stats(void) MAYBE_UNUSED;
223 
224 #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
225 /* Use these for mmap and munmap within dlmalloc.c.  */
226 static void *dlmmap(void *, size_t, int, int, int, off_t);
227 static int dlmunmap(void *, size_t);
228 #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
229 
230 #define mmap dlmmap
231 #define munmap dlmunmap
232 
233 #include "dlmalloc.c"
234 
235 #undef mmap
236 #undef munmap
237 
238 #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
239 
240 /* A mutex used to synchronize access to *exec* variables in this file.  */
241 static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
242 
243 /* A file descriptor of a temporary file from which we'll map
244    executable pages.  */
245 static int execfd = -1;
246 
247 /* The amount of space already allocated from the temporary file.  */
248 static size_t execsize = 0;
249 
250 /* Open a temporary file name, and immediately unlink it.  */
251 static int
open_temp_exec_file_name(char * name)252 open_temp_exec_file_name (char *name)
253 {
254   int fd = mkstemp (name);
255 
256   if (fd != -1)
257     unlink (name);
258 
259   return fd;
260 }
261 
262 /* Open a temporary file in the named directory.  */
263 static int
open_temp_exec_file_dir(const char * dir)264 open_temp_exec_file_dir (const char *dir)
265 {
266   static const char suffix[] = "/ffiXXXXXX";
267   int lendir = strlen (dir);
268   char *tempname = __builtin_alloca (lendir + sizeof (suffix));
269 
270   if (!tempname)
271     return -1;
272 
273   memcpy (tempname, dir, lendir);
274   memcpy (tempname + lendir, suffix, sizeof (suffix));
275 
276   return open_temp_exec_file_name (tempname);
277 }
278 
279 /* Open a temporary file in the directory in the named environment
280    variable.  */
281 static int
open_temp_exec_file_env(const char * envvar)282 open_temp_exec_file_env (const char *envvar)
283 {
284   const char *value = getenv (envvar);
285 
286   if (!value)
287     return -1;
288 
289   return open_temp_exec_file_dir (value);
290 }
291 
292 #ifdef HAVE_MNTENT
293 /* Open a temporary file in an executable and writable mount point
294    listed in the mounts file.  Subsequent calls with the same mounts
295    keep searching for mount points in the same file.  Providing NULL
296    as the mounts file closes the file.  */
297 static int
open_temp_exec_file_mnt(const char * mounts)298 open_temp_exec_file_mnt (const char *mounts)
299 {
300   static const char *last_mounts;
301   static FILE *last_mntent;
302 
303   if (mounts != last_mounts)
304     {
305       if (last_mntent)
306 	endmntent (last_mntent);
307 
308       last_mounts = mounts;
309 
310       if (mounts)
311 	last_mntent = setmntent (mounts, "r");
312       else
313 	last_mntent = NULL;
314     }
315 
316   if (!last_mntent)
317     return -1;
318 
319   for (;;)
320     {
321       int fd;
322       struct mntent mnt;
323       char buf[MAXPATHLEN * 3];
324 
325       if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
326 	return -1;
327 
328       if (hasmntopt (&mnt, "ro")
329 	  || hasmntopt (&mnt, "noexec")
330 	  || access (mnt.mnt_dir, W_OK))
331 	continue;
332 
333       fd = open_temp_exec_file_dir (mnt.mnt_dir);
334 
335       if (fd != -1)
336 	return fd;
337     }
338 }
339 #endif /* HAVE_MNTENT */
340 
341 /* Instructions to look for a location to hold a temporary file that
342    can be mapped in for execution.  */
343 static struct
344 {
345   int (*func)(const char *);
346   const char *arg;
347   int repeat;
348 } open_temp_exec_file_opts[] = {
349   { open_temp_exec_file_env, "TMPDIR", 0 },
350   { open_temp_exec_file_dir, "/tmp", 0 },
351   { open_temp_exec_file_dir, "/var/tmp", 0 },
352   { open_temp_exec_file_dir, "/dev/shm", 0 },
353   { open_temp_exec_file_env, "HOME", 0 },
354 #ifdef HAVE_MNTENT
355   { open_temp_exec_file_mnt, "/etc/mtab", 1 },
356   { open_temp_exec_file_mnt, "/proc/mounts", 1 },
357 #endif /* HAVE_MNTENT */
358 };
359 
360 /* Current index into open_temp_exec_file_opts.  */
361 static int open_temp_exec_file_opts_idx = 0;
362 
363 /* Reset a current multi-call func, then advances to the next entry.
364    If we're at the last, go back to the first and return nonzero,
365    otherwise return zero.  */
366 static int
open_temp_exec_file_opts_next(void)367 open_temp_exec_file_opts_next (void)
368 {
369   if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
370     open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
371 
372   open_temp_exec_file_opts_idx++;
373   if (open_temp_exec_file_opts_idx
374       == (sizeof (open_temp_exec_file_opts)
375 	  / sizeof (*open_temp_exec_file_opts)))
376     {
377       open_temp_exec_file_opts_idx = 0;
378       return 1;
379     }
380 
381   return 0;
382 }
383 
384 /* Return a file descriptor of a temporary zero-sized file in a
385    writable and exexutable filesystem.  */
386 static int
open_temp_exec_file(void)387 open_temp_exec_file (void)
388 {
389   int fd;
390 
391   do
392     {
393       fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
394 	(open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
395 
396       if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
397 	  || fd == -1)
398 	{
399 	  if (open_temp_exec_file_opts_next ())
400 	    break;
401 	}
402     }
403   while (fd == -1);
404 
405   return fd;
406 }
407 
408 /* Map in a chunk of memory from the temporary exec file into separate
409    locations in the virtual memory address space, one writable and one
410    executable.  Returns the address of the writable portion, after
411    storing an offset to the corresponding executable portion at the
412    last word of the requested chunk.  */
413 static void *
dlmmap_locked(void * start,size_t length,int prot,int flags,off_t offset)414 dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
415 {
416   void *ptr;
417 
418   if (execfd == -1)
419     {
420       open_temp_exec_file_opts_idx = 0;
421     retry_open:
422       execfd = open_temp_exec_file ();
423       if (execfd == -1)
424 	return MFAIL;
425     }
426 
427   offset = execsize;
428 
429   if (ftruncate (execfd, offset + length))
430     return MFAIL;
431 
432   flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
433   flags |= MAP_SHARED;
434 
435   ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
436 	      flags, execfd, offset);
437   if (ptr == MFAIL)
438     {
439       if (!offset)
440 	{
441 	  close (execfd);
442 	  goto retry_open;
443 	}
444       ftruncate (execfd, offset);
445       return MFAIL;
446     }
447   else if (!offset
448 	   && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
449     open_temp_exec_file_opts_next ();
450 
451   start = mmap (start, length, prot, flags, execfd, offset);
452 
453   if (start == MFAIL)
454     {
455       munmap (ptr, length);
456       ftruncate (execfd, offset);
457       return start;
458     }
459 
460   mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
461 
462   execsize += length;
463 
464   return start;
465 }
466 
467 /* Map in a writable and executable chunk of memory if possible.
468    Failing that, fall back to dlmmap_locked.  */
469 static void *
dlmmap(void * start,size_t length,int prot,int flags,int fd,off_t offset)470 dlmmap (void *start, size_t length, int prot,
471 	int flags, int fd, off_t offset)
472 {
473   void *ptr;
474 
475   assert (start == NULL && length % malloc_getpagesize == 0
476 	  && prot == (PROT_READ | PROT_WRITE)
477 	  && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
478 	  && fd == -1 && offset == 0);
479 
480 #if FFI_CLOSURE_TEST
481   printf ("mapping in %zi\n", length);
482 #endif
483 
484   if (execfd == -1 && is_emutramp_enabled ())
485     {
486       ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
487       return ptr;
488     }
489 
490   if (execfd == -1 && !is_selinux_enabled ())
491     {
492       ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
493 
494       if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
495 	/* Cool, no need to mess with separate segments.  */
496 	return ptr;
497 
498       /* If MREMAP_DUP is ever introduced and implemented, try mmap
499 	 with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
500 	 MREMAP_DUP and prot at this point.  */
501     }
502 
503   if (execsize == 0 || execfd == -1)
504     {
505       pthread_mutex_lock (&open_temp_exec_file_mutex);
506       ptr = dlmmap_locked (start, length, prot, flags, offset);
507       pthread_mutex_unlock (&open_temp_exec_file_mutex);
508 
509       return ptr;
510     }
511 
512   return dlmmap_locked (start, length, prot, flags, offset);
513 }
514 
515 /* Release memory at the given address, as well as the corresponding
516    executable page if it's separate.  */
517 static int
dlmunmap(void * start,size_t length)518 dlmunmap (void *start, size_t length)
519 {
520   /* We don't bother decreasing execsize or truncating the file, since
521      we can't quite tell whether we're unmapping the end of the file.
522      We don't expect frequent deallocation anyway.  If we did, we
523      could locate pages in the file by writing to the pages being
524      deallocated and checking that the file contents change.
525      Yuck.  */
526   msegmentptr seg = segment_holding (gm, start);
527   void *code;
528 
529 #if FFI_CLOSURE_TEST
530   printf ("unmapping %zi\n", length);
531 #endif
532 
533   if (seg && (code = add_segment_exec_offset (start, seg)) != start)
534     {
535       int ret = munmap (code, length);
536       if (ret)
537 	return ret;
538     }
539 
540   return munmap (start, length);
541 }
542 
543 #if FFI_CLOSURE_FREE_CODE
544 /* Return segment holding given code address.  */
545 static msegmentptr
segment_holding_code(mstate m,char * addr)546 segment_holding_code (mstate m, char* addr)
547 {
548   msegmentptr sp = &m->seg;
549   for (;;) {
550     if (addr >= add_segment_exec_offset (sp->base, sp)
551 	&& addr < add_segment_exec_offset (sp->base, sp) + sp->size)
552       return sp;
553     if ((sp = sp->next) == 0)
554       return 0;
555   }
556 }
557 #endif
558 
559 #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
560 
561 /* Allocate a chunk of memory with the given size.  Returns a pointer
562    to the writable address, and sets *CODE to the executable
563    corresponding virtual address.  */
564 void *
ffi_closure_alloc(size_t size,void ** code)565 ffi_closure_alloc (size_t size, void **code)
566 {
567   void *ptr;
568 
569   if (!code)
570     return NULL;
571 
572   ptr = dlmalloc (size);
573 
574   if (ptr)
575     {
576       msegmentptr seg = segment_holding (gm, ptr);
577 
578       *code = add_segment_exec_offset (ptr, seg);
579     }
580 
581   return ptr;
582 }
583 
584 /* Release a chunk of memory allocated with ffi_closure_alloc.  If
585    FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
586    writable or the executable address given.  Otherwise, only the
587    writable address can be provided here.  */
588 void
ffi_closure_free(void * ptr)589 ffi_closure_free (void *ptr)
590 {
591 #if FFI_CLOSURE_FREE_CODE
592   msegmentptr seg = segment_holding_code (gm, ptr);
593 
594   if (seg)
595     ptr = sub_segment_exec_offset (ptr, seg);
596 #endif
597 
598   dlfree (ptr);
599 }
600 
601 
602 #if FFI_CLOSURE_TEST
603 /* Do some internal sanity testing to make sure allocation and
604    deallocation of pages are working as intended.  */
main()605 int main ()
606 {
607   void *p[3];
608 #define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
609 #define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
610   GET (0, malloc_getpagesize / 2);
611   GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
612   PUT (1);
613   GET (1, 2 * malloc_getpagesize);
614   GET (2, malloc_getpagesize / 2);
615   PUT (1);
616   PUT (0);
617   PUT (2);
618   return 0;
619 }
620 #endif /* FFI_CLOSURE_TEST */
621 # else /* ! FFI_MMAP_EXEC_WRIT */
622 
623 /* On many systems, memory returned by malloc is writable and
624    executable, so just use it.  */
625 
626 #include <stdlib.h>
627 
628 void *
ffi_closure_alloc(size_t size,void ** code)629 ffi_closure_alloc (size_t size, void **code)
630 {
631   if (!code)
632     return NULL;
633 
634   return *code = malloc (size);
635 }
636 
637 void
ffi_closure_free(void * ptr)638 ffi_closure_free (void *ptr)
639 {
640   free (ptr);
641 }
642 
643 # endif /* ! FFI_MMAP_EXEC_WRIT */
644 #endif /* FFI_CLOSURES */
645