From: Elia Pinto Date: Fri, 30 Oct 2026 09:58:53 GMT Subject: [PATCH v2 4/4] convert: use size_t for text statistics and ident counts Message-ID: <84e9e38a3e63a2b135847471a13ea36d37bd64d4.1793352943.git.gitter.spiros@gmail.com> In-Reply-To: gather_stats() counts NULs, CRs, LFs, CRLFs and (non)printable characters in "unsigned" fields of struct text_stat, and count_ident() counts "$Id$" markers in an "int". Now that the buffer sizes they work on are size_t, a large enough buffer overflows them. Unlike the previous patches, this is not specific to LLP64: "unsigned" is 32 bits wide on all the platforms we care about. The counters only drive decisions, but a wrapped counter makes the wrong one. For example, a file with exactly 2^32 lone LFs gets lonelf == 0, so will_convert_lf_to_crlf() decides there is nothing to convert, and a wrapped "printable" or "nonprintable" can fool the heuristic in convert_is_binary(). In count_ident(), going past INT_MAX is undefined behaviour. This is not a memory safety problem: the counts are only used as hints to strbuf_grow() in crlf_to_worktree() and ident_to_worktree(), and the buffers are then filled with strbuf_add(), which grows them as needed. Use size_t for all of them, and compute the size hint in ident_to_worktree() with st_add() and st_mult(), so that it dies instead of wrapping around. Triggering any of this needs more than 4GiB of input, so no test is added. Suggested-by: Patrick Steinhardt Signed-off-by: Elia Pinto --- convert.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/convert.c b/convert.c index ca5594cab0..34fb4ce685 100644 --- a/convert.c +++ b/convert.c @@ -36,10 +36,10 @@ struct text_stat { /* NUL, CR, LF and CRLF counts */ - unsigned nul, lonecr, lonelf, crlf; + size_t nul, lonecr, lonelf, crlf; /* These are just approximations! */ - unsigned printable, nonprintable; + size_t printable, nonprintable; }; static void gather_stats(const char *buf, size_t size, struct text_stat *stats) @@ -1077,12 +1077,12 @@ static int read_convert_config(const char *var, const char *value, return 0; } -static int count_ident(const char *cp, size_t size) +static size_t count_ident(const char *cp, size_t size) { /* * "$Id: 0000000000000000000000000000000000000000 $" <=> "$Id$" */ - int cnt = 0; + size_t cnt = 0; char ch; while (size) { @@ -1170,7 +1170,7 @@ static int ident_to_worktree(const char *src, size_t len, struct object_id oid; char *to_free = NULL; const char *dollar, *spc; - int cnt; + size_t cnt; if (!ident) return 0; @@ -1184,7 +1184,7 @@ static int ident_to_worktree(const char *src, size_t len, to_free = strbuf_detach(buf, NULL); hash_object_file(the_hash_algo, src, len, OBJ_BLOB, &oid); - strbuf_grow(buf, len + cnt * (the_hash_algo->hexsz + 3)); + strbuf_grow(buf, st_add(len, st_mult(cnt, the_hash_algo->hexsz + 3))); for (;;) { /* step 1: run to the next '$' */ dollar = memchr(src, '$', len); -- 2.53.0