Skip to content

Commit

Permalink
Try to fix memory leaks from mapping files on iOS
Browse files Browse the repository at this point in the history
  • Loading branch information
tbodt committed Aug 24, 2019
1 parent 3bd114a commit 32c5046
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 14 deletions.
24 changes: 19 additions & 5 deletions emu/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "kernel/signal.h"
#include "emu/memory.h"
#include "jit/jit.h"
#include "kernel/vdso.h"
#include "kernel/task.h"

// increment the change count
static void mem_changed(struct mem *mem);
Expand Down Expand Up @@ -102,24 +104,31 @@ bool pt_is_hole(struct mem *mem, page_t start, pages_t pages) {
return true;
}

int pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, unsigned flags) {
int pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, size_t offset, unsigned flags) {
if (memory == MAP_FAILED)
return errno_map();

// If this fails, the munmap in pt_unmap would probably fail.
assert((uintptr_t) memory % real_page_size == 0 || memory == vdso_data);

struct data *data = malloc(sizeof(struct data));
if (data == NULL)
return _ENOMEM;
data->data = memory;
data->size = pages * PAGE_SIZE;
data->refcount = 0;
#if LEAK_DEBUG
data->pid = current ? current->pid : 0;
data->dest = start << PAGE_BITS;
#endif

for (page_t page = start; page < start + pages; page++) {
if (mem_pt(mem, page) != NULL)
pt_unmap(mem, page, 1, 0);
data->refcount++;
struct pt_entry *pt = mem_pt_new(mem, page);
pt->data = data;
pt->offset = (page - start) << PAGE_BITS;
pt->offset = ((page - start) << PAGE_BITS) + offset;
pt->flags = flags;
}
return 0;
Expand All @@ -141,7 +150,12 @@ int pt_unmap(struct mem *mem, page_t start, pages_t pages, int force) {
struct data *data = pt->data;
mem_pt_del(mem, page);
if (--data->refcount == 0) {
munmap(data->data, data->size);
// vdso wasn't allocated with mmap, it's just in our data segment
if (data->data != vdso_data) {
int err = munmap(data->data, data->size);
if (err != 0)
die("munmap(%p, %lu) failed: %s", data->data, data->size, strerror(errno));
}
free(data);
}
}
Expand All @@ -153,7 +167,7 @@ int pt_map_nothing(struct mem *mem, page_t start, pages_t pages, unsigned flags)
if (pages == 0) return 0;
void *memory = mmap(NULL, pages * PAGE_SIZE,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
return pt_map(mem, start, pages, memory, flags | P_ANONYMOUS);
return pt_map(mem, start, pages, memory, 0, flags | P_ANONYMOUS);
}

int pt_set_flags(struct mem *mem, page_t start, pages_t pages, int flags) {
Expand Down Expand Up @@ -232,7 +246,7 @@ void *mem_ptr(struct mem *mem, addr_t addr, int type) {
void *copy = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
memcpy(copy, data, PAGE_SIZE);
pt_map(mem, page, 1, copy, entry->flags &~ P_COW);
pt_map(mem, page, 1, copy, 0, entry->flags &~ P_COW);
}
#if JIT
// get rid of any compiled blocks in this page
Expand Down
15 changes: 10 additions & 5 deletions emu/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,16 @@ typedef dword_t pages_t;
#define BYTES_ROUND_DOWN(bytes) (PAGE(bytes) << PAGE_BITS)
#define BYTES_ROUND_UP(bytes) (PAGE_ROUND_UP(bytes) << PAGE_BITS)

#define LEAK_DEBUG 0

struct data {
void *data; // immutable
size_t size; // also immutable
atomic_uint refcount;
#if LEAK_DEBUG
int pid;
addr_t dest;
#endif
};
struct pt_entry {
struct data *data;
Expand Down Expand Up @@ -81,11 +87,10 @@ page_t pt_find_hole(struct mem *mem, pages_t size);

#define PT_FORCE 1

// Map real memory into fake memory (unmaps existing mappings). The memory is
// freed with munmap, so it must be allocated with mmap
int pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, unsigned flags);
// Map fake file into fake memory
int pt_map_file(struct mem *mem, page_t start, pages_t pages, int fd, off_t off, unsigned flags);
// Map memory + offset into fake memory (unmaps existing mappings). Takes
// ownership of memory. It will be freed with:
// munmap(memory, pages * PAGE_SIZE)
int pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, size_t offset, unsigned flags);
// Map empty space into fake memory
int pt_map_nothing(struct mem *mem, page_t page, pages_t pages, unsigned flags);
// Unmap fake memory, return -1 if any part of the range isn't mapped and 0 otherwise
Expand Down
4 changes: 1 addition & 3 deletions fs/real.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,7 @@ int realfs_mmap(struct fd *fd, struct mem *mem, page_t start, pages_t pages, off
off_t correction = offset - real_offset;
char *memory = mmap(NULL, (pages * PAGE_SIZE) + correction,
mmap_prot, mmap_flags, fd->real_fd, real_offset);
if (memory != MAP_FAILED)
memory += correction;
return pt_map(mem, start, pages, memory, prot);
return pt_map(mem, start, pages, memory, correction, prot);
}

static ssize_t realfs_readlink(struct mount *mount, const char *path, char *buf, size_t bufsize) {
Expand Down
2 changes: 1 addition & 1 deletion kernel/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ static int elf_exec(struct fd *fd, const char *file, const char *argv, const cha
if (vdso_page == BAD_PAGE)
goto beyond_hope;
vdso_page += 1;
if ((err = pt_map(current->mem, vdso_page, vdso_pages, (void *) vdso_data, 0)) < 0)
if ((err = pt_map(current->mem, vdso_page, vdso_pages, (void *) vdso_data, 0, 0)) < 0)
goto beyond_hope;
current->mm->vdso = vdso_page << PAGE_BITS;
addr_t vdso_entry = current->mm->vdso + ((struct elf_header *) vdso_data)->entry_point;
Expand Down

0 comments on commit 32c5046

Please sign in to comment.