utf8: refactor `strbuf_utf8_replace` to not rely on preallocated buffer

In `strbuf_utf8_replace`, we preallocate the destination buffer and then use `memcpy` to copy bytes into it at computed offsets. This feels rather fragile and is hard to understand at times. Refactor the code to instead use `strbuf_add` and `strbuf_addstr` so that we can be sure that there is no possibility to perform an out-of-bounds write. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Dec 1, 2022 at 15:47 UTC f930a2394303b902e2973f4308f96529f736b8bc
1 file changed +13 -21
utf8.c
+13 -21
@@ -365,26 +365,20 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,
365 void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,
366 const char *subst)
367 {
368 - struct strbuf sb_dst = STRBUF_INIT;
369 - char *src = sb_src->buf;
370 - char *end = src + sb_src->len;
371 - char *dst;
372 - int w = 0, subst_len = 0;
368 + const char *src = sb_src->buf, *end = sb_src->buf + sb_src->len;
369 + struct strbuf dst;
370 + int w = 0;
371
374 - if (subst)
375 - subst_len = strlen(subst);
376 - strbuf_grow(&sb_dst, sb_src->len + subst_len);
377 - dst = sb_dst.buf;
372 + strbuf_init(&dst, sb_src->len);
373
374 while (src < end) {
375 + const char *old;
376 int glyph_width;
381 - char *old;
377 size_t n;
378
379 while ((n = display_mode_esc_sequence_len(src))) {
385 - memcpy(dst, src, n);
380 + strbuf_add(&dst, src, n);
381 src += n;
387 - dst += n;
382 }
383
384 if (src >= end)
@@ -404,21 +398,19 @@ void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,
398
399 if (glyph_width && w >= pos && w < pos + width) {
400 if (subst) {
407 - memcpy(dst, subst, subst_len);
408 - dst += subst_len;
401 + strbuf_addstr(&dst, subst);
402 subst = NULL;
403 }
411 - w += glyph_width;
412 - continue;
404 + } else {
405 + strbuf_add(&dst, old, src - old);
406 }
414 - memcpy(dst, old, src - old);
415 - dst += src - old;
407 +
408 w += glyph_width;
409 }
418 - strbuf_setlen(&sb_dst, dst - sb_dst.buf);
419 - strbuf_swap(sb_src, &sb_dst);
410 +
411 + strbuf_swap(sb_src, &dst);
412 out:
421 - strbuf_release(&sb_dst);
413 + strbuf_release(&dst);
414 }
415
416 /*