This is an automated email from the ASF dual-hosted git repository.

swebb2066 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git


The following commit(s) were added to refs/heads/master by this push:
     new 7f30d205 reject invalid UTF-8 lead bytes F8..FF in Transcoder::decode 
(#699)
7f30d205 is described below

commit 7f30d205ca2780e3fa47031c768dcb1a761ac299
Author: metsw24-max <[email protected]>
AuthorDate: Tue Jun 2 09:13:10 2026 +0530

    reject invalid UTF-8 lead bytes F8..FF in Transcoder::decode (#699)
---
 src/main/cpp/transcoder.cpp                 |  6 +++++-
 src/test/cpp/helpers/transcodertestcase.cpp | 29 +++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/src/main/cpp/transcoder.cpp b/src/main/cpp/transcoder.cpp
index fb646958..19404745 100644
--- a/src/main/cpp/transcoder.cpp
+++ b/src/main/cpp/transcoder.cpp
@@ -298,7 +298,11 @@ unsigned int Transcoder::decode(const std::string& src,
                                // is not a Unicode code point. Without this 
bound, encodeUTF16
                                // later silently aliases the bogus value to a 
valid in-range
                                // code point — a substitution-collision 
filter-bypass primitive.
-                               if (rv > 0xFFFF && rv <= 0x10FFFF)
+                               // Lead bytes F8..FF are never valid UTF-8, but 
the & 0x07 mask
+                               // discards their high bits, so without the 
(ch1 & 0xF8) == 0xF0
+                               // guard F8 BF BF BF would alias to U+3FFFF 
instead of being
+                               // rejected.
+                               if ((ch1 & 0xF8) == 0xF0 && rv > 0xFFFF && rv 
<= 0x10FFFF)
                                {
                                        return rv;
                                }
diff --git a/src/test/cpp/helpers/transcodertestcase.cpp 
b/src/test/cpp/helpers/transcodertestcase.cpp
index 1bc1431e..7887b82a 100644
--- a/src/test/cpp/helpers/transcodertestcase.cpp
+++ b/src/test/cpp/helpers/transcodertestcase.cpp
@@ -69,6 +69,7 @@ LOGUNIT_CLASS(TranscoderTestCase)
        LOGUNIT_TEST(testDecodeUTF8_U0800);
        LOGUNIT_TEST(testDecodeUTF8_RejectAboveMax);
        LOGUNIT_TEST(testDecodeUTF8_MaxBoundary);
+       LOGUNIT_TEST(testDecodeUTF8_RejectInvalidLeadByte);
        LOGUNIT_TEST(testEncodeUTF16BE_BMP);
        LOGUNIT_TEST(testEncodeUTF16BE_Supplementary);
        LOGUNIT_TEST(testEncodeUTF16LE_Supplementary);
@@ -436,6 +437,34 @@ public:
                }
        }
 
+       /**
+        * Lead bytes F8..FF never start a valid UTF-8 sequence. The four-byte
+        * branch masks the lead byte with 0x07, discarding those high bits, so
+        * F8 BF BF BF used to slip past the U+10FFFF bound and decode to 
U+3FFFF
+        * (and FB/FC likewise to other in-range planes) — the same aliasing
+        * filter-bypass that the F5..F7 rejection guards against. Each byte of 
an
+        * invalid lead sequence must be replaced with Transcoder::LOSSCHAR.
+        */
+       void testDecodeUTF8_RejectInvalidLeadByte()
+       {
+               struct { const char* bytes; size_t len; bool reject; } cases[] =
+               {
+                       { "\xF0\x9F\x98\x80", 4, false }, // U+1F600 — valid 
four-byte
+                       { "\xF8\xBF\xBF\xBF", 4, true  }, // F8 lead: masked to 
U+3FFFF (reject)
+                       { "\xFB\xBF\xBF\xBF", 4, true  }, // FB lead: masked to 
U+FFFFF (reject)
+                       { "\xFC\x8F\xBF\xBF", 4, true  }, // FC lead: masked to 
U+10FFFF (reject)
+                       { "\xFF\xBF\xBF\xBF", 4, true  }, // FF lead (reject)
+               };
+               for (auto& c : cases)
+               {
+                       std::string src(c.bytes, c.len);
+                       LogString out;
+                       Transcoder::decodeUTF8(src, out);
+                       bool hasLoss = out.find(Transcoder::LOSSCHAR) != 
LogString::npos;
+                       LOGUNIT_ASSERT_EQUAL(c.reject, hasLoss);
+               }
+       }
+
        void testEncodeUTF16BE_BMP()
        {
                char raw[4] = { 0, 0, 0, 0 };

Reply via email to