From 0456f5a06ba7490f9cd286865859c9f4ff6e015a Mon Sep 17 00:00:00 2001 From: nmzik Date: Sun, 6 Sep 2026 07:28:19 +0400 Subject: [PATCH] libc: fix UTF-16 printf strings and precision limits --- src/common/stringUtils.h | 5 ++-- src/libs/guestPrintf.cpp | 49 +++++++++++++++++++++++++--------------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/common/stringUtils.h b/src/common/stringUtils.h index e460693ef..ac61bc430 100644 --- a/src/common/stringUtils.h +++ b/src/common/stringUtils.h @@ -316,10 +316,9 @@ inline std::string SafeCsv(std::string_view text) { return std::string(text); } -inline std::string Utf16ToUtf8(const char16_t* utf16) { - std::u16string input(utf16); +inline std::string Utf16ToUtf8(std::u16string_view utf16) { std::wstring_convert, char16_t> convert; - return convert.to_bytes(input); + return convert.to_bytes(utf16.begin(), utf16.end()); } inline ByteBuffer HexToBin(std::string_view text) { diff --git a/src/libs/guestPrintf.cpp b/src/libs/guestPrintf.cpp index c004a8ae6..0637d653d 100644 --- a/src/libs/guestPrintf.cpp +++ b/src/libs/guestPrintf.cpp @@ -11,6 +11,7 @@ #include "common/assert.h" #include "common/common.h" #include "common/logging/log.h" +#include "common/stringUtils.h" #include "libs/vaContext.h" #include @@ -431,12 +432,13 @@ static size_t _etoa(out_fct_type out, std::vector* buffer, size_t idx, siz return idx; } -static inline unsigned int _strnlen_s(const char* str, size_t maxsize) { - const char* s = nullptr; - for (s = str; (*s != 0) && ((maxsize--) != 0u); ++s) { - ; +template +static size_t _strnlen_s(const Char* str, size_t maxsize) { + size_t size = 0; + while (size < maxsize && str[size] != 0) { + ++size; } - return static_cast(s - str); + return size; } // NOLINTNEXTLINE(readability-function-cognitive-complexity) @@ -694,26 +696,37 @@ static int kyty_printf_internal(bool sn, char* sn_s, size_t sn_n, const char* fo } case 's': { - // const char* p = va_arg(va, char*); - const char* p = VaArg_ptr(va_list); - unsigned int l = - _strnlen_s(p, precision != 0u ? precision : static_cast(-1)); - // pre padding - if ((flags & FLAGS_PRECISION) != 0u) { - l = (l < precision ? l : precision); + const size_t limit = (flags & FLAGS_PRECISION) != 0u ? precision : maxlen; + const char* p = VaArg_ptr(va_list); + std::string converted; + if ((flags & FLAGS_LONG) != 0u) { + // The guest ABI uses a 16-bit code unit for wchar_t. + const auto* wide = reinterpret_cast(p); + std::u16string_view text(wide, _strnlen_s(wide, limit)); + if (text.size() == limit && !text.empty() && + text.back() >= 0xd800 && text.back() <= 0xdbff) { + text.remove_suffix(1); + } + converted = Common::Utf16ToUtf8(text); + p = converted.c_str(); + } + size_t length = _strnlen_s(p, limit); + if ((flags & FLAGS_LONG) != 0u && length < converted.size()) { + // A wide-string precision cannot split a multibyte character. + while (length != 0 && (static_cast(p[length]) & 0xc0) == 0x80) { + --length; + } } if ((flags & FLAGS_LEFT) == 0u) { - while (l++ < width) { + for (size_t i = length; i < width; ++i) { out(' ', &buffer, idx++, maxlen); } } - // string output - while ((*p != 0) && (((flags & FLAGS_PRECISION) == 0u) || ((precision--) != 0u))) { - out(*(p++), &buffer, idx++, maxlen); + for (size_t i = 0; i < length; ++i) { + out(p[i], &buffer, idx++, maxlen); } - // post padding if ((flags & FLAGS_LEFT) != 0u) { - while (l++ < width) { + for (size_t i = length; i < width; ++i) { out(' ', &buffer, idx++, maxlen); } }