diff --git a/printbuf.c b/printbuf.c index 12d3b33..f9964ad 100644 --- a/printbuf.c +++ b/printbuf.c @@ -28,6 +28,7 @@ #endif /* HAVE_STDARG_H */ #include "debug.h" +#include "json_inttypes.h" #include "printbuf.h" #include "snprintf_compat.h" #include "vasprintf_compat.h" @@ -96,6 +97,8 @@ static int printbuf_extend(struct printbuf *p, int min_size) int printbuf_memappend(struct printbuf *p, const char *buf, int size) { + int buf_offset = -1; + /* Prevent signed integer overflows with large buffers. */ if (size < 0 || size > INT_MAX - p->bpos - 1) { @@ -104,10 +107,18 @@ int printbuf_memappend(struct printbuf *p, const char *buf, int size) } if (p->size <= p->bpos + size + 1) { + const uintptr_t buf_addr = (uintptr_t)(const void *)buf; + const uintptr_t p_buf_addr = (uintptr_t)(const void *)p->buf; + + /* realloc() invalidates source pointers into the old buffer. */ + if (buf_addr >= p_buf_addr && buf_addr - p_buf_addr < (uintptr_t)p->size) + buf_offset = (int)(buf_addr - p_buf_addr); if (printbuf_extend(p, p->bpos + size + 1) < 0) return -1; + if (buf_offset >= 0) + buf = p->buf + buf_offset; } - memcpy(p->buf + p->bpos, buf, size); + memmove(p->buf + p->bpos, buf, size); p->bpos += size; p->buf[p->bpos] = '\0'; return size; diff --git a/tests/test_printbuf.c b/tests/test_printbuf.c index 3b1540f..1b57f52 100644 --- a/tests/test_printbuf.c +++ b/tests/test_printbuf.c @@ -126,6 +126,38 @@ static void test_printbuf_memappend(int *before_resize) printf("%s: end test\n", __func__); } +static void test_printbuf_self_append(void); +static void test_printbuf_self_append(void) +{ + struct printbuf *pb; + char *data; + int data_size; + int i; + int initial_size; + + printf("%s: starting test\n", __func__); + pb = printbuf_new(); + assert(pb != NULL); + initial_size = pb->size; + data_size = initial_size / 2 + 1; + data = malloc(data_size); + assert(data != NULL); + memset(data, 'X', data_size); + assert(printbuf_memappend(pb, data, data_size) == data_size); + free(data); + assert(pb->size == initial_size); + + assert(printbuf_memappend(pb, pb->buf, pb->bpos) == data_size); + assert(pb->size > initial_size); + assert(pb->bpos == data_size * 2); + for (i = 0; i < pb->bpos; i++) + assert(pb->buf[i] == 'X'); + assert(pb->buf[pb->bpos] == '\0'); + + printbuf_free(pb); + printf("%s: end test\n", __func__); +} + static void test_sprintbuf(int before_resize); static void test_sprintbuf(int before_resize) { @@ -182,6 +214,8 @@ int main(int argc, char **argv) printf("========================================\n"); test_printbuf_memappend(&before_resize); printf("========================================\n"); + test_printbuf_self_append(); + printf("========================================\n"); test_sprintbuf(before_resize); printf("========================================\n"); diff --git a/tests/test_printbuf.expected b/tests/test_printbuf.expected index a4ebc2a..5f89ac4 100644 --- a/tests/test_printbuf.expected +++ b/tests/test_printbuf.expected @@ -21,6 +21,9 @@ Append to just after resize: 32, [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX] Buffer size after printbuf_strappend(): 16, [XXXXXXXXXXXXXXXX] test_printbuf_memappend: end test ======================================== +test_printbuf_self_append: starting test +test_printbuf_self_append: end test +======================================== test_sprintbuf: starting test Buffer length: 0 sprintbuf to just after resize(31+1): 32, [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX], strlen(buf)=32