diff --git a/mongoose.c b/mongoose.c index a186418dec6c6160f0bfc8548f6d7fce33da5e53..01cd72ca9cce308c105aa7b44d6abb372b2b8cb3 100644 --- a/mongoose.c +++ b/mongoose.c @@ -731,21 +731,32 @@ static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) { va_list ap_copy; int len; - // Windows is not standard-compliant, and vsnprintf() returns -1 if - // buffer is too small. Also, older versions of msvcrt.dll do not have - // _vscprintf(). However, if size is 0, vsnprintf() behaves correctly. - // Therefore, we make two passes: on first pass, get required message length. - // On second pass, actually print the message. va_copy(ap_copy, ap); - len = vsnprintf(NULL, 0, fmt, ap_copy); + len = vsnprintf(*buf, size, fmt, ap_copy); + va_end(ap_copy); - if (len > (int) size && - (size = len + 1) > 0 && - (*buf = (char *) malloc(size)) == NULL) { - len = -1; // Allocation failed, mark failure - } else { - va_copy(ap_copy, ap); - vsnprintf(*buf, size, fmt, ap_copy); + if (len < 0) { + // eCos and Windows are not standard-compliant and return -1 when + // the buffer is too small. Keep allocating larger buffers until we + // succeed or out of memory. + *buf = NULL; + while (len < 0) { + if (*buf) free(*buf); + size *= 2; + if ((*buf = (char *) malloc(size)) == NULL) break; + va_copy(ap_copy, ap); + len = vsnprintf(*buf, size, fmt, ap_copy); + va_end(ap_copy); + } + } else if (len > (int) size) { + // Standard-compliant code path. Allocate a buffer that is large enough. + if ((*buf = (char *) malloc(len + 1)) == NULL) { + len = -1; + } else { + va_copy(ap_copy, ap); + len = vsnprintf(*buf, len + 1, fmt, ap_copy); + va_end(ap_copy); + } } return len; diff --git a/unit_test.c b/unit_test.c index b3d034492bae3347bc9f91cfa2fdfdbdc87878f4..3f9a39ed47841e1ae896fbce51c860ab5d3a0e29 100644 --- a/unit_test.c +++ b/unit_test.c @@ -613,6 +613,30 @@ static const char *test_rewrites(void) { return NULL; } +static int avt(char **buf, size_t buf_size, const char *fmt, ...) { + int result; + va_list ap; + va_start(ap, fmt); + result = alloc_vprintf(buf, buf_size, fmt, ap); + va_end(ap); + return result; +} + +static const char *test_alloc_vprintf(void) { + char buf[5], *p = buf; + + ASSERT(avt(&p, sizeof(buf), "%d", 123) == 3); + ASSERT(p == buf); + ASSERT(strcmp(p, "123") == 0); + + ASSERT(avt(&p, sizeof(buf), "%d", 123456789) == 9); + ASSERT(p != buf); + ASSERT(strcmp(p, "123456789") == 0); + free(p); + + return NULL; +} + static const char *run_all_tests(void) { RUN_TEST(test_should_keep_alive); RUN_TEST(test_match_prefix); @@ -630,6 +654,7 @@ static const char *run_all_tests(void) { RUN_TEST(test_mg_connect); RUN_TEST(test_mg_set_option); RUN_TEST(test_rewrites); + RUN_TEST(test_alloc_vprintf); #ifdef MONGOOSE_USE_SSL RUN_TEST(test_ssl); #endif