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