From aa7272a6e1184cdd21ab8f89200219abd8053eda Mon Sep 17 00:00:00 2001 From: "Arnold D. Robbins" Date: Mon, 6 Apr 2026 10:56:38 +0300 Subject: [PATCH] Add overflow checking in do_sub for 32 bit systems. --- ChangeLog | 9 +++++++++ builtin.c | 25 ++++++++++++++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/builtin.c b/builtin.c index f6a37ac76..f33525d5b 100644 --- a/builtin.c +++ b/builtin.c @@ -1819,11 +1819,13 @@ do_sub(int nargs, unsigned int flags) long current; bool lastmatchnonzero; char *mb_indices = NULL; + const char *fname = NULL; // for fatal message, below if ((flags & GENSUB) != 0) { double d; NODE *glob_flag; + fname = "gensub"; tmp = PEEK(3); rp = re_update(tmp); @@ -1855,6 +1857,7 @@ do_sub(int nargs, unsigned int flags) } DEREF(glob_flag); } else { + fname = ((flags & GSUB) != 0) ? "gsub" : "sub"; /* take care of regexp early, in case re_update is fatal */ tmp = PEEK(2); @@ -1975,6 +1975,21 @@ do_sub(int nargs, unsigned int flags) * vary since ampersand is actual text of regexp match. */ + // 4/2026: This overflow check simply provides a fatal + // message instead of letting realloc() die later after + // a buffer overrun. It simply makes the user experience better, + // but does not prevent gawk from dying miserably. I suppose + // it's worth the trouble, but just barely. + + /* uint64_t so the product is 64-bit even on 32-bit ILP32 builds */ + uint64_t repl_contribution = + (uint64_t)(unsigned int)ampersands + * (uint64_t)(uintptr_t)(matchend - matchstart); + if (repl_contribution > (uint64_t)SIZE_MAX + || repl_contribution > (uint64_t)SIZE_MAX - (size_t)(matchend - text) + - repllen - 1) + fatal(_("%s: replacement expansion too large"), fname); + /* * add 1 to len to handle "empty" case where * matchend == matchstart and we force a match on a single