Merge pull request #29601 from yuwata/mmap-check-overflow

mmap: check offset and size more carefully
This commit is contained in:
Luca Boccassi
2023-10-24 12:20:04 +01:00
committed by GitHub
27 changed files with 365 additions and 82 deletions
+5 -2
View File
@@ -27,8 +27,11 @@
#include "memory-util.h"
void close_and_munmap(int fd, void *address, size_t size) {
if (size > 0)
assert_se(munmap(address, PAGE_ALIGN(size)) >= 0);
if (size > 0) {
size = PAGE_ALIGN(size);
assert(size < SIZE_MAX);
assert_se(munmap(address, size) >= 0);
}
safe_close(fd);
}
+2
View File
@@ -2490,6 +2490,8 @@ int bus_body_part_map(struct bus_body_part *part) {
shift = PAGE_OFFSET(part->memfd_offset);
psz = PAGE_ALIGN(part->size + shift);
if (psz >= SIZE_MAX)
return -EFBIG;
if (part->memfd >= 0)
p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE, part->memfd, part->memfd_offset - shift);
@@ -379,7 +379,9 @@ int journal_file_fss_load(JournalFile *f) {
if (le64toh(header->start_usec) <= 0 || le64toh(header->interval_usec) <= 0)
return -EBADMSG;
f->fss_file = mmap(NULL, PAGE_ALIGN(f->fss_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
size_t sz = PAGE_ALIGN(f->fss_file_size);
assert(sz < SIZE_MAX);
f->fss_file = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (f->fss_file == MAP_FAILED) {
f->fss_file = NULL;
return -errno;
+35 -32
View File
@@ -47,30 +47,31 @@
#define DEFAULT_COMPRESS_THRESHOLD (512ULL)
#define MIN_COMPRESS_THRESHOLD (8ULL)
/* This is the minimum journal file size */
#define JOURNAL_FILE_SIZE_MIN (512 * 1024ULL) /* 512 KiB */
#define JOURNAL_COMPACT_SIZE_MAX UINT32_MAX /* 4 GiB */
#define U64_KB UINT64_C(1024)
#define U64_MB (UINT64_C(1024) * U64_KB)
#define U64_GB (UINT64_C(1024) * U64_MB)
/* These are the lower and upper bounds if we deduce the max_use value
* from the file system size */
#define MAX_USE_LOWER (1 * 1024 * 1024ULL) /* 1 MiB */
#define MAX_USE_UPPER (4 * 1024 * 1024 * 1024ULL) /* 4 GiB */
/* This is the minimum journal file size */
#define JOURNAL_FILE_SIZE_MIN (512 * U64_KB) /* 512 KiB */
#define JOURNAL_COMPACT_SIZE_MAX ((uint64_t) UINT32_MAX) /* 4 GiB */
/* These are the lower and upper bounds if we deduce the max_use value from the file system size */
#define MAX_USE_LOWER (1 * U64_MB) /* 1 MiB */
#define MAX_USE_UPPER (4 * U64_GB) /* 4 GiB */
/* Those are the lower and upper bounds for the minimal use limit,
* i.e. how much we'll use even if keep_free suggests otherwise. */
#define MIN_USE_LOW (1 * 1024 * 1024ULL) /* 1 MiB */
#define MIN_USE_HIGH (16 * 1024 * 1024ULL) /* 16 MiB */
#define MIN_USE_LOW (1 * U64_MB) /* 1 MiB */
#define MIN_USE_HIGH (16 * U64_MB) /* 16 MiB */
/* This is the upper bound if we deduce max_size from max_use */
#define MAX_SIZE_UPPER (128 * 1024 * 1024ULL) /* 128 MiB */
#define MAX_SIZE_UPPER (128 * U64_MB) /* 128 MiB */
/* This is the upper bound if we deduce the keep_free value from the
* file system size */
#define KEEP_FREE_UPPER (4 * 1024 * 1024 * 1024ULL) /* 4 GiB */
/* This is the upper bound if we deduce the keep_free value from the file system size */
#define KEEP_FREE_UPPER (4 * U64_GB) /* 4 GiB */
/* This is the keep_free value when we can't determine the system
* size */
#define DEFAULT_KEEP_FREE (1024 * 1024ULL) /* 1 MB */
/* This is the keep_free value when we can't determine the system size */
#define DEFAULT_KEEP_FREE (1 * U64_MB) /* 1 MB */
/* This is the default maximum number of journal files to keep around. */
#define DEFAULT_N_MAX_FILES 100
@@ -82,7 +83,7 @@
#define CHAIN_CACHE_MAX 20
/* How much to increase the journal file size at once each time we allocate something new. */
#define FILE_SIZE_INCREASE (8 * 1024 * 1024ULL) /* 8MB */
#define FILE_SIZE_INCREASE (8 * U64_MB) /* 8MB */
/* Reread fstat() of the file for detecting deletions at least this often */
#define LAST_STAT_REFRESH_USEC (5*USEC_PER_SEC)
@@ -301,9 +302,11 @@ JournalFile* journal_file_close(JournalFile *f) {
#endif
#if HAVE_GCRYPT
if (f->fss_file)
munmap(f->fss_file, PAGE_ALIGN(f->fss_file_size));
else
if (f->fss_file) {
size_t sz = PAGE_ALIGN(f->fss_file_size);
assert(sz < SIZE_MAX);
munmap(f->fss_file, sz);
} else
free(f->fsprg_state);
free(f->fsprg_seed);
@@ -750,7 +753,7 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size)
/* We assume that this file is not sparse, and we know that for sure, since we always call
* posix_fallocate() ourselves */
if (size > PAGE_ALIGN_DOWN(UINT64_MAX) - offset)
if (size > PAGE_ALIGN_DOWN_U64(UINT64_MAX) - offset)
return -EINVAL;
if (mmap_cache_fd_got_sigbus(f->cache_fd))
@@ -758,12 +761,12 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size)
old_header_size = le64toh(READ_NOW(f->header->header_size));
old_arena_size = le64toh(READ_NOW(f->header->arena_size));
if (old_arena_size > PAGE_ALIGN_DOWN(UINT64_MAX) - old_header_size)
if (old_arena_size > PAGE_ALIGN_DOWN_U64(UINT64_MAX) - old_header_size)
return -EBADMSG;
old_size = old_header_size + old_arena_size;
new_size = MAX(PAGE_ALIGN(offset + size), old_header_size);
new_size = MAX(PAGE_ALIGN_U64(offset + size), old_header_size);
if (new_size <= old_size) {
@@ -794,7 +797,7 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size)
if (fstatvfs(f->fd, &svfs) >= 0) {
uint64_t available;
available = LESS_BY((uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize, f->metrics.keep_free);
available = LESS_BY(u64_multiply_safe(svfs.f_bfree, svfs.f_bsize), f->metrics.keep_free);
if (new_size - old_size > available)
return -E2BIG;
@@ -3880,19 +3883,19 @@ static void journal_default_metrics(JournalMetrics *m, int fd, bool compact) {
assert(fd >= 0);
if (fstatvfs(fd, &ss) >= 0)
fs_size = ss.f_frsize * ss.f_blocks;
fs_size = u64_multiply_safe(ss.f_frsize, ss.f_blocks);
else
log_debug_errno(errno, "Failed to determine disk size: %m");
if (m->max_use == UINT64_MAX) {
if (fs_size > 0)
m->max_use = CLAMP(PAGE_ALIGN(fs_size / 10), /* 10% of file system size */
m->max_use = CLAMP(PAGE_ALIGN_U64(fs_size / 10), /* 10% of file system size */
MAX_USE_LOWER, MAX_USE_UPPER);
else
m->max_use = MAX_USE_LOWER;
} else {
m->max_use = PAGE_ALIGN(m->max_use);
m->max_use = PAGE_ALIGN_U64(m->max_use);
if (m->max_use != 0 && m->max_use < JOURNAL_FILE_SIZE_MIN*2)
m->max_use = JOURNAL_FILE_SIZE_MIN*2;
@@ -3900,7 +3903,7 @@ static void journal_default_metrics(JournalMetrics *m, int fd, bool compact) {
if (m->min_use == UINT64_MAX) {
if (fs_size > 0)
m->min_use = CLAMP(PAGE_ALIGN(fs_size / 50), /* 2% of file system size */
m->min_use = CLAMP(PAGE_ALIGN_U64(fs_size / 50), /* 2% of file system size */
MIN_USE_LOW, MIN_USE_HIGH);
else
m->min_use = MIN_USE_LOW;
@@ -3910,10 +3913,10 @@ static void journal_default_metrics(JournalMetrics *m, int fd, bool compact) {
m->min_use = m->max_use;
if (m->max_size == UINT64_MAX)
m->max_size = MIN(PAGE_ALIGN(m->max_use / 8), /* 8 chunks */
m->max_size = MIN(PAGE_ALIGN_U64(m->max_use / 8), /* 8 chunks */
MAX_SIZE_UPPER);
else
m->max_size = PAGE_ALIGN(m->max_size);
m->max_size = PAGE_ALIGN_U64(m->max_size);
if (compact && m->max_size > JOURNAL_COMPACT_SIZE_MAX)
m->max_size = JOURNAL_COMPACT_SIZE_MAX;
@@ -3929,13 +3932,13 @@ static void journal_default_metrics(JournalMetrics *m, int fd, bool compact) {
if (m->min_size == UINT64_MAX)
m->min_size = JOURNAL_FILE_SIZE_MIN;
else
m->min_size = CLAMP(PAGE_ALIGN(m->min_size),
m->min_size = CLAMP(PAGE_ALIGN_U64(m->min_size),
JOURNAL_FILE_SIZE_MIN,
m->max_size ?: UINT64_MAX);
if (m->keep_free == UINT64_MAX) {
if (fs_size > 0)
m->keep_free = MIN(PAGE_ALIGN(fs_size / 20), /* 5% of file system size */
m->keep_free = MIN(PAGE_ALIGN_U64(fs_size / 20), /* 5% of file system size */
KEEP_FREE_UPPER);
else
m->keep_free = DEFAULT_KEEP_FREE;
+20 -20
View File
@@ -71,7 +71,7 @@ struct MMapCache {
/* Tiny windows increase mmap activity and the chance of exposing unsafe use. */
# define WINDOW_SIZE (page_size())
#else
# define WINDOW_SIZE (8ULL*1024ULL*1024ULL)
# define WINDOW_SIZE ((size_t) (UINT64_C(8) * UINT64_C(1024) * UINT64_C(1024)))
#endif
MMapCache* mmap_cache_new(void) {
@@ -269,7 +269,6 @@ static int add_mmap(
struct stat *st,
Window **ret) {
uint64_t woffset, wsize;
Window *w;
void *d;
int r;
@@ -278,41 +277,42 @@ static int add_mmap(
assert(size > 0);
assert(ret);
woffset = offset & ~((uint64_t) page_size() - 1ULL);
wsize = size + (offset - woffset);
wsize = PAGE_ALIGN(wsize);
/* overflow check */
if (size > SIZE_MAX - PAGE_OFFSET_U64(offset))
return -EADDRNOTAVAIL;
if (wsize < WINDOW_SIZE) {
size = PAGE_ALIGN(size + PAGE_OFFSET_U64(offset));
offset = PAGE_ALIGN_DOWN_U64(offset);
if (size < WINDOW_SIZE) {
uint64_t delta;
delta = PAGE_ALIGN((WINDOW_SIZE - wsize) / 2);
if (delta > offset)
woffset = 0;
else
woffset -= delta;
wsize = WINDOW_SIZE;
delta = PAGE_ALIGN((WINDOW_SIZE - size) / 2);
offset = LESS_BY(offset, delta);
size = WINDOW_SIZE;
}
if (st) {
/* Memory maps that are larger then the files underneath have undefined behavior. Hence,
* clamp things to the file size if we know it */
if (woffset >= (uint64_t) st->st_size)
if (offset >= (uint64_t) st->st_size)
return -EADDRNOTAVAIL;
if (woffset + wsize > (uint64_t) st->st_size)
wsize = PAGE_ALIGN(st->st_size - woffset);
if (size > (uint64_t) st->st_size - offset)
size = PAGE_ALIGN((uint64_t) st->st_size - offset);
}
r = mmap_try_harder(f, NULL, MAP_SHARED, woffset, wsize, &d);
if (size >= SIZE_MAX)
return -EADDRNOTAVAIL;
r = mmap_try_harder(f, NULL, MAP_SHARED, offset, size, &d);
if (r < 0)
return r;
w = window_add(f, woffset, wsize, d);
w = window_add(f, offset, size, d);
if (!w) {
(void) munmap(d, wsize);
(void) munmap(d, size);
return -ENOMEM;
}