From 3146ae0adcd5cbce048c5db17a52dd119624222f Mon Sep 17 00:00:00 2001 From: Julien Letessier Date: Thu, 15 Jan 2026 12:59:03 +0100 Subject: [PATCH] Handle vsnprintf errors in logger --- src/Logger.cpp | 10 ++++++++ test/logunit_vsnprintf.cpp | 14 ++++++++++++ test/logunit_vsnprintf.py | 47 ++++++++++++++++++++++++++++++++++++++ test/run.sh | 1 + 4 files changed, 72 insertions(+) create mode 100644 test/logunit_vsnprintf.cpp create mode 100644 test/logunit_vsnprintf.py diff --git a/src/Logger.cpp b/src/Logger.cpp index b819224..ced0e53 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -59,6 +59,16 @@ void LogUnit::vformat(LogLevel::Type level, const char* file, int line, const ch mLen += n; len -= n; n = vsnprintf(p, len, fmt, ap); + if (n < 0) { + // Formatting failed; keep existing prefix and finish the line. + if (mLen < MaxLogLen - 1) { + mBuf[mLen++] = '\n'; + } else { + mBuf[MaxLogLen - 1] = '\n'; + mLen = MaxLogLen; + } + return; + } mLen += n; if (mLen >= MaxLogLen) { mLen = MaxLogLen - 1; diff --git a/test/logunit_vsnprintf.cpp b/test/logunit_vsnprintf.cpp new file mode 100644 index 0000000..f61f9dc --- /dev/null +++ b/test/logunit_vsnprintf.cpp @@ -0,0 +1,14 @@ +/* + * Minimal test for LogUnit::vformat negative vsnprintf handling. + */ + +#include "../src/Logger.h" + +int main() { + LogUnit unit; + unit.format(LogLevel::Info, __FILE__, __LINE__, "%"); + if (unit.length() < 0) { + return 1; + } + return 0; +} diff --git a/test/logunit_vsnprintf.py b/test/logunit_vsnprintf.py new file mode 100644 index 0000000..d2adfba --- /dev/null +++ b/test/logunit_vsnprintf.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# +# Build and run the LogUnit::vformat 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", "logunit_vsnprintf.cpp") + if not os.path.exists(src): + print("FAIL: logunit_vsnprintf.cpp not found") + return False + + with tempfile.TemporaryDirectory() as tmp: + exe = os.path.join(tmp, "logunit_vsnprintf") + cmd = [ + "g++", + "-std=c++11", + src, + 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 logunit_vsnprintf:", exc) + return False + try: + subprocess.check_call([exe], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except subprocess.CalledProcessError as exc: + print("FAIL: logunit_vsnprintf returned", exc.returncode) + return False + return True + + +if __name__ == "__main__": + _ = parse_args("LogUnit vsnprintf test") + root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + success = run_test(root) + exit_with_result(success, "logunit vsnprintf", "logunit vsnprintf") diff --git a/test/run.sh b/test/run.sh index 5a2d465..d2baf7b 100755 --- a/test/run.sh +++ b/test/run.sh @@ -146,6 +146,7 @@ TESTS=( "test/request_parser_long_command.py" "test/signal_handling.py" "test/buffer_vsnprintf.py" + "test/logunit_vsnprintf.py" "test/pubsub_long_name.py" "test/pubsub_large_message.py" "test/transaction_forbid.py"