From 1579b18dfa49252318b4a2b3cfa83a4ce325ba0c Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 22 Jul 2024 13:58:49 +0200 Subject: [PATCH] QTest::formatFailMessage(): fix use of std::mbstowcs() This function is not re-entrant, and while we can probably rule out that our test macros are being executed concurrently, we can't rule out that other parts of the test concurrently execute std::mbstowcs(). Use the re-entrant version and also fix the format string (%* requires int, not size_t). Saturation is ok here, because, while std::mbsrtowcs() will happily return a value > maxMsgLen when dst == nullptr and the string is longer, we will never actually print more than maxMsgLen of it. As a drive-by, port to std and adjust the QNX comments to describe the quirky QNX behavior, if, indeed, still present in the version, better. This author had to go back in the git history to figure out what it wanted to tell him. Amends 2d8028d696a86102a7753f9d59fb41f4170181a8 (mbstowcs) and d040681b6f3d03b349e9b9487fe89a611d03ee3c (%*). Pick-to: 6.8 6.7 6.5 6.2 5.15 Change-Id: I6215713c643647727f73f4e2f2a7ac34204af40d Reviewed-by: Ivan Solovev --- src/testlib/qtestresult.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/testlib/qtestresult.cpp b/src/testlib/qtestresult.cpp index 7c5ce9ce548..4feb0ac7044 100644 --- a/src/testlib/qtestresult.cpp +++ b/src/testlib/qtestresult.cpp @@ -12,6 +12,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -325,6 +329,18 @@ static const char *rightArgNameForOp(QTest::ComparisonOperation op) return op == QTest::ComparisonOperation::CustomCompare ? "Expected " : "Baseline "; } +static int approx_wide_len(const char *s) +{ + std::mbstate_t state = {}; + // QNX might stop at max when dst == nullptr, so pass INT_MAX, + // being the largest value this function will return: + constexpr size_t max = INT_MAX; + auto r = std::mbsrtowcs(nullptr, &s, max, &state); + if (r == size_t(-1)) // encoding error, fall back to strlen() + r = strlen(s); // `s` was not advanced since `dst == nullptr` + return int(std::clamp(r, size_t(0), max)); +} + // Overload to format failures for "const char *" - no need to strdup(). void formatFailMessage(char *msg, size_t maxMsgLen, const char *failureMsg, @@ -332,8 +348,8 @@ void formatFailMessage(char *msg, size_t maxMsgLen, const char *actual, const char *expected, QTest::ComparisonOperation op) { - size_t len1 = mbstowcs(nullptr, actual, maxMsgLen); // Last parameter is not ignored on QNX - size_t len2 = mbstowcs(nullptr, expected, maxMsgLen); // (result is never larger than this). + const auto len1 = approx_wide_len(actual); + const auto len2 = approx_wide_len(expected); const int written = qsnprintf(msg, maxMsgLen, "%s\n", failureMsg); msg += written; maxMsgLen -= written;