mmap-cache.c revision 739731cdace09ff179fdd75ae0714da0d81e384d
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen This file is part of systemd.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Copyright 2012 Lennart Poettering
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen systemd is free software; you can redistribute it and/or modify it
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen under the terms of the GNU Lesser General Public License as published by
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen the Free Software Foundation; either version 2.1 of the License, or
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen (at your option) any later version.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen systemd is distributed in the hope that it will be useful, but
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Lesser General Public License for more details.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen You should have received a copy of the GNU Lesser General Public License
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen/* Tiny windows increase mmap activity and the chance of exposing unsafe use. */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Replace the window with anonymous pages. This is useful
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen * when we hit a SIGBUS and want to make sure the file cannot
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen * trigger any further SIGBUS, possibly overrunning the sigbus
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_se(mmap(w->ptr, w->size, w->prot, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == w->ptr);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen_pure_ static bool window_matches(Window *w, int fd, int prot, uint64_t offset, size_t size) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!m->last_unused || m->n_windows <= WINDOWS_MIN) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Allocate a new window */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Reuse an existing one */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void context_detach_window(Context *c) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Not used anymore? */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Unmap unused windows immediately to expose use-after-unmap
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen * by SIGSEGV. */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void context_attach_window(Context *c, Window *w) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Used again? */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic Context *context_add(MMapCache *m, unsigned id) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_se(hashmap_remove(f->cache->fds, FD_TO_PTR(f->fd)));
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic FileDescriptor* fd_add(MMapCache *m, int fd) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen for (i = 0; i < MMAP_CACHE_MAX_CONTEXTS; i++)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!window_matches(c->window, fd, prot, offset, size)) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Drop the reference to the window, since it's unnecessary now */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c->window->keep_always = c->window->keep_always || keep_always;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (window_matches(w, fd, prot, offset, size))
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen w->keep_always = w->keep_always || keep_always;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen *ret = (uint8_t*) w->ptr + (offset - w->offset);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen woffset = offset & ~((uint64_t) page_size() - 1ULL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen delta = PAGE_ALIGN((WINDOW_SIZE - wsize) / 2);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Memory maps that are larger then the files
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen underneath have undefined behavior. Hence, clamp
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen things to the file size if we know it */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen d = mmap(NULL, wsize, prot, MAP_SHARED, fd, woffset);
goto outofmem;
goto outofmem;
w = window_add(m);
goto outofmem;
w->ptr = d;
w->fd = f;
c->window = w;
return -ENOMEM;
int mmap_cache_get(
MMapCache *m,
int fd,
int prot,
unsigned context,
bool keep_always,
void **ret) {
assert(m);
m->n_hit ++;
m->n_hit ++;
m->n_missed++;
assert(m);
return m->n_hit;
assert(m);
return m->n_missed;
bool found = false;
FileDescriptor *f;
Iterator i;
assert(m);
bool ours;
void *addr;
if (_likely_(r == 0))
abort();
ours = false;
Window *w;
if (ours)
if (!ours) {
abort();
Window *w;
if (!f->sigbus)
FileDescriptor *f;
assert(m);
return f->sigbus;
FileDescriptor *f;
assert(m);
fd_free(f);