From a4efa699bfb5e03e6b7142729965caad36d1996d Mon Sep 17 00:00:00 2001 From: shixuantong Date: Thu, 13 Jun 2024 16:29:38 +0800 Subject: [PATCH] Check the validity of len before mmap Two cases: (1)If condition 'c->mapend + extra_len < c->mapstart + relro_len' is True, the result of "len" (size_t len = (c->mapend + extra_len) - (c->mapstart + relro_len)) will be a negative value. 'len' is of type size_t, so it overflows. later __mmap will fail, because 'len - mod' is a very large value at this point. (2)If the data segment is small, "len" may be equal to 0. In this case, __mmap also fails. In both cases, the mapping fails, the mapping is falled back, and hugepage feature of dynamic library becomes invalid. Case (1) is an exception, and the fallback is the expected. Case (2) should not be fallled back in its entirety. In this case, the code segment may continue to use huge page, and the data segment uses 4KB page. --- elf/dl-load.h | 2 ++ elf/dl-map-segments-hugepage.h | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/elf/dl-load.h b/elf/dl-load.h index f2428165..cd0a5143 100644 --- a/elf/dl-load.h +++ b/elf/dl-load.h @@ -142,6 +142,8 @@ static const char *_dl_map_segments (struct link_map *l, int fd, N_("fail to find exec prot segment") #define DL_MAP_SEGMENT_ERROR_EXTRA_SIZE \ N_("wrong segment extra size") +#define DL_MAP_SEGMENTS_GNU_RELRO \ + N_("relro segment size is not expected") #endif #endif /* dl-load.h */ diff --git a/elf/dl-map-segments-hugepage.h b/elf/dl-map-segments-hugepage.h index 218e93a0..3b863288 100644 --- a/elf/dl-map-segments-hugepage.h +++ b/elf/dl-map-segments-hugepage.h @@ -260,6 +260,9 @@ _mmap_segment_filesz(struct link_map *l, const struct loadcmd *c, ElfW(Addr) map void *map_addr = 0; size_t relro_len = _get_relro_len(l, c); + if (c->mapend + extra_len < c->mapstart + relro_len) + return DL_MAP_SEGMENTS_GNU_RELRO; + if (relro_len > 0) { if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) @@ -279,6 +282,12 @@ _mmap_segment_filesz(struct link_map *l, const struct loadcmd *c, ElfW(Addr) map size_t prev_map_len = ALIGN_UP(mapstart, SIZE_2MB) - mapstart; size_t len = (c->mapend + extra_len) - (c->mapstart + relro_len); + if (len == 0) + { + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf("\t\tAfter mmapping relro len, remain filesz is zero\n"); + return NULL; + } if (len <= prev_map_len || len - prev_map_len < SIZE_2MB) { if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) -- 2.33.0