[glibc] elf: Bound the tunable cache string table against the mapping size
Adhemerval Zanella via Glibc-cvs <[email protected]> Mon, 13 Jul 2026 16:56:06 +0000 (GMT)
| Newsgroups | gmane.comp.lib.glibc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=3d3cd10c4ec8bf5fb48faaad8c0acea3da225a35 commit 3d3cd10c4ec8bf5fb48faaad8c0acea3da225a35 Author: Adhemerval Zanella <[email protected]> Date: Mon Jul 6 16:56:12 2026 -0300 elf: Bound the tunable cache string table against the mapping size _dl_load_cache_tunables bounds each entry's string offsets against [s_start, start + cache_new->len_strings], but len_strings is an unvalidated 32-bit field from ld.so.cache and s_start/s_end were int. A corrupt cache with an oversized len_strings could make s_end exceed the mapping (or overflow), letting an offset point outside the mmap; the following strcmp/__strdup would then read unmapped memory. Compute the offsets as size_t and clamp s_end to cachesize, matching how the regular library lookup bounds string offsets against the mapping size. Checked on x86_64-linux-gnu and i686-linux-gnu. Reviewed-by: DJ Delorie <[email protected]> Diff: --- elf/dl-cache.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/elf/dl-cache.c b/elf/dl-cache.c index d05afe2700..5d8d3cae90 100644 --- a/elf/dl-cache.c +++ b/elf/dl-cache.c @@ -17,6 +17,7 @@ <https://www.gnu.org/licenses/>. */ #include <assert.h> +#include <intprops.h> #include <unistd.h> #include <ldsodefs.h> #include <sys/mman.h> @@ -649,9 +650,16 @@ _dl_load_cache_tunables (const char **data) != (void *) & tec[count]) return NULL; - /* Validate each entry. */ - int s_start = (const char *) (&cache_new->libs[cache_new->nlibs]) - *data; - int s_end = s_start + cache_new->len_strings; + /* Validate each entry. The string table lies between the file entries + and the end of the mapping; clamp its end to CACHESIZE so that a bogus + len_strings cannot make an offset point outside the mapped file. */ + size_t s_start = (const char *) (&cache_new->libs[cache_new->nlibs]) - *data; + size_t s_end; + if (s_start >= cachesize + || INT_ADD_WRAPV (s_start, cache_new->len_strings, &s_end)) + return NULL; + if (s_end > cachesize) + s_end = cachesize; for (i = 0; i < count; i ++) { if (thc->tunables[i].name_offset < s_start