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