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 ba4d59c6 Fix UB and locale-dependent behavior in 
StringHelper::toLowerCase (#687)
ba4d59c6 is described below

commit ba4d59c6bb5eb6a7859f84ee952bc9c5ba597b3d
Author: metsw24-max <[email protected]>
AuthorDate: Mon May 25 07:35:04 2026 +0530

    Fix UB and locale-dependent behavior in StringHelper::toLowerCase (#687)
---
 src/main/cpp/stringhelper.cpp                 | 15 ++++++++++++--
 src/test/cpp/helpers/stringhelpertestcase.cpp | 28 +++++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/src/main/cpp/stringhelper.cpp b/src/main/cpp/stringhelper.cpp
index 6971ba60..c8142c11 100644
--- a/src/main/cpp/stringhelper.cpp
+++ b/src/main/cpp/stringhelper.cpp
@@ -67,9 +67,20 @@ bool StringHelper::equalsIgnoreCase(const LogString& s1, 
const LogString& upper,
 
 LogString StringHelper::toLowerCase(const LogString& s)
 {
+       // Fold ASCII A-Z only. Passing a value not representable as unsigned 
char
+       // (or EOF) to std::tolower(int) is undefined behaviour; with signed 
char,
+       // any byte > 0x7F sign-extends to a negative int. The previous 
implementation
+       // also produced locale-dependent output for the same input, which is 
wrong
+       // for the callers (class names, color names, column names — all ASCII).
        LogString d;
-       std::transform(s.begin(), s.end(),
-               std::insert_iterator<LogString>(d, d.begin()), tolower);
+       d.reserve(s.size());
+       for (auto ch : s)
+       {
+               if (ch >= static_cast<logchar>('A') && ch <= 
static_cast<logchar>('Z'))
+                       d.push_back(static_cast<logchar>(ch + ('a' - 'A')));
+               else
+                       d.push_back(ch);
+       }
        return d;
 }
 
diff --git a/src/test/cpp/helpers/stringhelpertestcase.cpp 
b/src/test/cpp/helpers/stringhelpertestcase.cpp
index d4cc2165..56064042 100644
--- a/src/test/cpp/helpers/stringhelpertestcase.cpp
+++ b/src/test/cpp/helpers/stringhelpertestcase.cpp
@@ -44,6 +44,8 @@ LOGUNIT_CLASS(StringHelperTestCase)
        LOGUNIT_TEST( testEndsWith5 );
        LOGUNIT_TEST( testFormatEmptyPattern );
        LOGUNIT_TEST( testFormatMissingArgument );
+       LOGUNIT_TEST( testToLowerCaseAscii );
+       LOGUNIT_TEST( testToLowerCaseNonAsciiPassesThrough );
        LOGUNIT_TEST_SUITE_END();
 
 
@@ -144,6 +146,32 @@ public:
                LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("first {1}"), 
StringHelper::format(LOG4CXX_STR("{0} {1}"), params));
        }
 
+       void testToLowerCaseAscii()
+       {
+               LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("hello world"),
+                       StringHelper::toLowerCase(LOG4CXX_STR("Hello World")));
+       }
+
+       // Regression: passing a non-ASCII byte (negative when char is signed, 
or > 0xFF
+       // for wchar_t/UniChar) to ::tolower(int) is undefined behaviour. The 
prior
+       // implementation also varied with the active C locale, producing 
different
+       // output on different machines for the same configuration file. Verify 
the
+       // non-ASCII bytes pass through unchanged regardless of locale.
+       void testToLowerCaseNonAsciiPassesThrough()
+       {
+               LogString input;
+               input.push_back(static_cast<logchar>('A'));
+               input.push_back(static_cast<logchar>(0xC9));   // uppercase 'É' 
in Latin-1 / Windows-1252
+               input.push_back(static_cast<logchar>(0xE9));   // lowercase 'é' 
in Latin-1 / Windows-1252
+               input.push_back(static_cast<logchar>('Z'));
+               LogString expected;
+               expected.push_back(static_cast<logchar>('a'));
+               expected.push_back(static_cast<logchar>(0xC9));
+               expected.push_back(static_cast<logchar>(0xE9));
+               expected.push_back(static_cast<logchar>('z'));
+               LOGUNIT_ASSERT_EQUAL(expected, 
StringHelper::toLowerCase(input));
+       }
+
 
 };
 

Reply via email to