From: Tobias Stoeckmann Date: Tue, 24 Feb 2026 20:43:16 +0000 (+0100) Subject: lib/fileeq: Check arithmetic in ul_fileeq_set_size X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0e6b542b41e9681f70f7b90b2a96bce7beacd978;p=thirdparty%2Futil-linux.git lib/fileeq: Check arithmetic in ul_fileeq_set_size Make sure that arithmetics do not overflow data types. Such overflows could occur with large hardlink options or on 32 bit systems with large files (due to size_t usage). If possible, reduce sizes so operations can continue successfully. Signed-off-by: Tobias Stoeckmann --- diff --git a/include/fileeq.h b/include/fileeq.h index df5e6d822..90b8d5118 100644 --- a/include/fileeq.h +++ b/include/fileeq.h @@ -53,8 +53,8 @@ extern void ul_fileeq_data_init(struct ul_fileeq_data *data); extern void ul_fileeq_data_deinit(struct ul_fileeq_data *data); extern void ul_fileeq_data_set_file(struct ul_fileeq_data *data, const char *name); -extern size_t ul_fileeq_set_size(struct ul_fileeq *eq, uint64_t filesiz, - size_t readsiz, size_t memsiz); +extern bool ul_fileeq_set_size(struct ul_fileeq *eq, int64_t st_size, + size_t readsiz, size_t memsiz); extern int ul_fileeq(struct ul_fileeq *eq, struct ul_fileeq_data *a, struct ul_fileeq_data *b); diff --git a/lib/fileeq.c b/lib/fileeq.c index d13fc4045..5a56b4753 100644 --- a/lib/fileeq.c +++ b/lib/fileeq.c @@ -250,16 +250,23 @@ void ul_fileeq_data_set_file(struct ul_fileeq_data *data, const char *name) data->name = name; } -size_t ul_fileeq_set_size(struct ul_fileeq *eq, uint64_t filesiz, - size_t readsiz, size_t memsiz) +bool ul_fileeq_set_size(struct ul_fileeq *eq, int64_t st_size, + size_t readsiz, size_t memsiz) { - uint64_t nreads, maxdigs; + uint64_t filesiz, nreads, maxdigs; size_t digsiz; assert(eq); + assert(st_size >= 0); + assert(readsiz); + + filesiz = (uint64_t) st_size; eq->filesiz = filesiz; + if (filesiz != 0 && readsiz > filesiz) + readsiz = filesiz; + switch (eq->method->id) { case UL_FILEEQ_MEMCMP: /* align file size */ @@ -275,22 +282,28 @@ size_t ul_fileeq_set_size(struct ul_fileeq *eq, uint64_t filesiz, maxdigs = memsiz / digsiz; if (maxdigs == 0) maxdigs = 1; + else if (maxdigs > filesiz) + maxdigs = filesiz; nreads = filesiz / readsiz; /* enlarge readsize for large files */ - if (nreads > maxdigs) - readsiz = (filesiz + maxdigs - 1) / maxdigs; + if (nreads > maxdigs) { + uint64_t ceiling = filesiz + maxdigs - 1; + if (ceiling / maxdigs > SIZE_MAX) + return false; + readsiz = ceiling / maxdigs; + } break; } eq->readsiz = readsiz; eq->blocksmax = (filesiz + readsiz - 1) / readsiz; - DBG(EQ, ul_debugobj(eq, "set sizes: filesiz=%" PRIu64 ", maxblocks=%" PRIu64 ", readsiz=%zu", + DBG(EQ, ul_debugobj(eq, "set sizes: filesiz=%" PRIu64 ", blocksmax=%" PRIu64 ", readsiz=%zu", eq->filesiz, eq->blocksmax, eq->readsiz)); reset_fileeq_bufs(eq); - return eq->blocksmax; + return true; } static unsigned char *get_buffer(struct ul_fileeq *eq) @@ -624,8 +637,9 @@ int main(int argc, char *argv[]) if (file_c) ul_fileeq_data_set_file(&c, file_c); - /* filesiz, readsiz, memsiz */ - ul_fileeq_set_size(&eq, st_a.st_size, 1024*1024, 4*1024); + /* st_size, readsiz, memsiz */ + if (!ul_fileeq_set_size(&eq, st_a.st_size, 1024*1024, 4*1024)) + err(EXIT_FAILURE, "failed to set sizes"); rc = ul_fileeq(&eq, &a, &b); diff --git a/misc-utils/hardlink.c b/misc-utils/hardlink.c index 114b5c4c9..2c02e25d4 100644 --- a/misc-utils/hardlink.c +++ b/misc-utils/hardlink.c @@ -1097,8 +1097,12 @@ static void visitor(const void *nodep, const VISIT which, const int depth) /* per-file cache size */ memsiz = opts.cache_size / nnodes; - /* filesiz, readsiz, memsiz */ - ul_fileeq_set_size(&fileeq, master->st.st_size, opts.io_size, memsiz); + /* st_size, readsiz, memsiz */ + if (!ul_fileeq_set_size(&fileeq, master->st.st_size, opts.io_size, memsiz)) { + jlog(VERBOSE2, + printf(_("Skipped (memory constraints) %s"), master->links->path)); + continue; + } #ifdef USE_REFLINK if (reflink_mode || reflinks_skip) {