From 2a431ccee6eecb2203384bb8867d38a4b278b895 Mon Sep 17 00:00:00 2001 From: Julien Letessier Date: Thu, 15 Jan 2026 12:57:36 +0100 Subject: [PATCH] Handle vsnprintf errors in buffer --- src/Buffer.cpp | 9 +++++++ test/buffer_vsnprintf.cpp | 20 ++++++++++++++++ test/buffer_vsnprintf.py | 49 +++++++++++++++++++++++++++++++++++++++ test/run.sh | 1 + 4 files changed, 79 insertions(+) create mode 100644 test/buffer_vsnprintf.cpp create mode 100644 test/buffer_vsnprintf.py diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 6128dbf..4f80ae8 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -82,6 +82,10 @@ Buffer* Buffer::fappend(const char* fmt, ...) int len = room(); int n = vsnprintf(dat, len, fmt, ap); va_end(ap); + if (n < 0) { + // Formatting failed; keep buffer length unchanged. + return nullptr; + } if (n >= len) { if (n > MaxBufFmtAppendLen) { return nullptr; @@ -104,6 +108,11 @@ Buffer* Buffer::vfappend(const char* fmt, va_list ap) va_list aq; va_copy(aq, ap); int n = vsnprintf(dat, len, fmt, ap); + if (n < 0) { + // Formatting failed; keep buffer length unchanged. + va_end(aq); + return nullptr; + } if (n >= len) { if (n > MaxBufFmtAppendLen) { va_end(aq); diff --git a/test/buffer_vsnprintf.cpp b/test/buffer_vsnprintf.cpp new file mode 100644 index 0000000..df60970 --- /dev/null +++ b/test/buffer_vsnprintf.cpp @@ -0,0 +1,20 @@ +/* + * Minimal test for Buffer::fappend negative vsnprintf handling. + */ + +#include "../src/Buffer.h" +#include "../src/Logger.h" + +int main() { + Logger::gInst = nullptr; + Buffer* buf = BufferAlloc::create(); + int before = buf->length(); + Buffer* ret = buf->fappend("%"); + if (ret && ret->length() < before) { + return 1; + } + if (buf->length() < before) { + return 2; + } + return 0; +} diff --git a/test/buffer_vsnprintf.py b/test/buffer_vsnprintf.py new file mode 100644 index 0000000..0ce815c --- /dev/null +++ b/test/buffer_vsnprintf.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# +# Build and run the Buffer::fappend vsnprintf edge-case test. +# + +import os +import subprocess +import tempfile +from test_util import parse_args, exit_with_result + + +def run_test(project_root): + src = os.path.join(project_root, "test", "buffer_vsnprintf.cpp") + if not os.path.exists(src): + print("FAIL: buffer_vsnprintf.cpp not found") + return False + + with tempfile.TemporaryDirectory() as tmp: + exe = os.path.join(tmp, "buffer_vsnprintf") + cmd = [ + "g++", + "-std=c++11", + src, + os.path.join(project_root, "src", "Buffer.cpp"), + os.path.join(project_root, "src", "Alloc.cpp"), + os.path.join(project_root, "src", "Logger.cpp"), + os.path.join(project_root, "src", "LogFileSink.cpp"), + os.path.join(project_root, "src", "Timer.cpp"), + "-o", + exe, + ] + try: + subprocess.check_call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except Exception as exc: + print("FAIL: compile buffer_vsnprintf:", exc) + return False + try: + subprocess.check_call([exe], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except subprocess.CalledProcessError as exc: + print("FAIL: buffer_vsnprintf returned", exc.returncode) + return False + return True + + +if __name__ == "__main__": + _ = parse_args("Buffer vsnprintf test") + root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + success = run_test(root) + exit_with_result(success, "buffer vsnprintf", "buffer vsnprintf") diff --git a/test/run.sh b/test/run.sh index 4525dd1..5a2d465 100755 --- a/test/run.sh +++ b/test/run.sh @@ -145,6 +145,7 @@ TESTS=( "test/response_parser_error_log.py" "test/request_parser_long_command.py" "test/signal_handling.py" + "test/buffer_vsnprintf.py" "test/pubsub_long_name.py" "test/pubsub_large_message.py" "test/transaction_forbid.py"