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 30c82890 Centralize overflow-safe layout sizing and reserve validation 
(#688)
30c82890 is described below

commit 30c828900bd9f67d281f0824d9c31e71614f8671
Author: jmestwa-coder <[email protected]>
AuthorDate: Tue May 26 07:18:34 2026 +0530

    Centralize overflow-safe layout sizing and reserve validation (#688)
---
 src/main/cpp/fmtlayout.cpp                     |  6 ++--
 src/main/cpp/htmllayout.cpp                    |  7 ++--
 src/main/cpp/jsonlayout.cpp                    |  5 +--
 src/main/cpp/patternlayout.cpp                 |  6 ++--
 src/main/cpp/xmllayout.cpp                     | 10 +++---
 src/main/include/log4cxx/private/layout_priv.h | 48 +++++++++++++++++++++++++
 src/test/cpp/jsonlayouttest.cpp                | 49 +++++++++++++++++++++++++-
 7 files changed, 115 insertions(+), 16 deletions(-)

diff --git a/src/main/cpp/fmtlayout.cpp b/src/main/cpp/fmtlayout.cpp
index f072b879..719bf44e 100644
--- a/src/main/cpp/fmtlayout.cpp
+++ b/src/main/cpp/fmtlayout.cpp
@@ -21,11 +21,13 @@
 #include <log4cxx/helpers/pool.h>
 #include <log4cxx/helpers/optionconverter.h>
 #include <log4cxx/helpers/transcoder.h>
+#include <log4cxx/private/layout_priv.h>
 #include <log4cxx/level.h>
 #include <chrono>
 
 #include <fmt/format.h>
 #include <fmt/chrono.h>
+#include <iterator>
 
 using namespace LOG4CXX_NS;
 using namespace LOG4CXX_NS::spi;
@@ -81,13 +83,13 @@ void FMTLayout::setOption(const LogString& option, const 
LogString& value)
 
 void FMTLayout::activateOptions( LOG4CXX_ACTIVATE_OPTIONS_FORMAL_PARAMETERS )
 {
-       m_priv->expectedPatternLength = getFormattedEventCharacterCount() * 2;
+       m_priv->expectedPatternLength = 
priv::doubledLayoutSize(getFormattedEventCharacterCount());
 }
 
 void FMTLayout::format( LOG4CXX_FORMAT_LAYOUT_FORMAL_PARAMETERS ) const
 {
        auto& lsMsg = event->getRenderedMessage();
-       output.reserve(m_priv->expectedPatternLength + lsMsg.size());
+       priv::reserveFormattedEvent(output, m_priv->expectedPatternLength, 
lsMsg.size());
        auto locationFull = fmt::format("{}({})",
                                                                                
 event->getLocationInformation().getFileName(),
                                                                                
 event->getLocationInformation().getLineNumber());
diff --git a/src/main/cpp/htmllayout.cpp b/src/main/cpp/htmllayout.cpp
index d7404435..6ceed459 100644
--- a/src/main/cpp/htmllayout.cpp
+++ b/src/main/cpp/htmllayout.cpp
@@ -25,6 +25,7 @@
 #include <log4cxx/helpers/stringhelper.h>
 #include <log4cxx/helpers/transcoder.h>
 #include <log4cxx/helpers/date.h>
+#include <log4cxx/private/layout_priv.h>
 
 using namespace LOG4CXX_NS;
 using namespace LOG4CXX_NS::helpers;
@@ -57,7 +58,7 @@ HTMLLayout::HTMLLayout()
        : m_priv(std::make_unique<HTMLLayoutPrivate>())
 {
        m_priv->dateFormat.setTimeZone(TimeZone::getGMT());
-       m_priv->expectedPatternLength = getFormattedEventCharacterCount() * 2;
+       m_priv->expectedPatternLength = 
priv::doubledLayoutSize(getFormattedEventCharacterCount());
 }
 
 HTMLLayout::~HTMLLayout() {}
@@ -76,14 +77,14 @@ void HTMLLayout::setOption(const LogString& option,
                        LOG4CXX_STR("LOCATIONINFO"), 
LOG4CXX_STR("locationinfo")))
        {
                setLocationInfo(OptionConverter::toBoolean(value, false));
-               m_priv->expectedPatternLength = 
getFormattedEventCharacterCount() * 2;
+               m_priv->expectedPatternLength = 
priv::doubledLayoutSize(getFormattedEventCharacterCount());
        }
 }
 
 void HTMLLayout::format( LOG4CXX_FORMAT_LAYOUT_FORMAL_PARAMETERS ) const
 {
        auto& lsMsg = event->getRenderedMessage();
-       output.reserve(m_priv->expectedPatternLength + lsMsg.size());
+       priv::reserveFormattedEvent(output, m_priv->expectedPatternLength, 
lsMsg.size());
        output.append(LOG4CXX_EOL);
        output.append(LOG4CXX_STR("<tr>"));
        output.append(LOG4CXX_EOL);
diff --git a/src/main/cpp/jsonlayout.cpp b/src/main/cpp/jsonlayout.cpp
index 76e9b9d8..0833d031 100644
--- a/src/main/cpp/jsonlayout.cpp
+++ b/src/main/cpp/jsonlayout.cpp
@@ -24,6 +24,7 @@
 #include <log4cxx/helpers/simpledateformat.h>
 #include <log4cxx/helpers/stringhelper.h>
 #include <log4cxx/helpers/transcoder.h>
+#include <log4cxx/private/layout_priv.h>
 
 #include <string.h>
 
@@ -106,7 +107,7 @@ LogString JSONLayout::getContentType() const
 
 void JSONLayout::activateOptions( LOG4CXX_ACTIVATE_OPTIONS_FORMAL_PARAMETERS )
 {
-       m_priv->expectedPatternLength = getFormattedEventCharacterCount() * 2;
+       m_priv->expectedPatternLength = 
priv::doubledLayoutSize(getFormattedEventCharacterCount());
 }
 
 void JSONLayout::setOption(const LogString& option, const LogString& value)
@@ -131,7 +132,7 @@ void JSONLayout::setOption(const LogString& option, const 
LogString& value)
 void JSONLayout::format( LOG4CXX_FORMAT_LAYOUT_FORMAL_PARAMETERS ) const
 {
        auto& lsMsg = event->getRenderedMessage();
-       output.reserve(m_priv->expectedPatternLength + lsMsg.size());
+       priv::reserveFormattedEvent(output, m_priv->expectedPatternLength, 
lsMsg.size());
        output.append(LOG4CXX_STR("{"));
        output.append(m_priv->prettyPrint ? LOG4CXX_EOL : LOG4CXX_STR(" "));
 
diff --git a/src/main/cpp/patternlayout.cpp b/src/main/cpp/patternlayout.cpp
index 5d67854a..5ecc6db5 100644
--- a/src/main/cpp/patternlayout.cpp
+++ b/src/main/cpp/patternlayout.cpp
@@ -48,6 +48,7 @@
 #include <log4cxx/pattern/propertiespatternconverter.h>
 #include <log4cxx/pattern/throwableinformationpatternconverter.h>
 #include <log4cxx/pattern/threadusernamepatternconverter.h>
+#include <log4cxx/private/layout_priv.h>
 
 
 using namespace LOG4CXX_NS;
@@ -114,7 +115,7 @@ void PatternLayout::setConversionPattern(const LogString& 
pattern)
 void PatternLayout::format( LOG4CXX_FORMAT_LAYOUT_FORMAL_PARAMETERS ) const
 {
        auto& lsMsg = event->getRenderedMessage();
-       output.reserve(m_priv->expectedPatternLength + lsMsg.size());
+       priv::reserveFormattedEvent(output, m_priv->expectedPatternLength, 
lsMsg.size());
 
        for (auto item : m_priv->patternConverters)
        {
@@ -188,7 +189,7 @@ void PatternLayout::activateOptions( 
LOG4CXX_ACTIVATE_OPTIONS_FORMAL_PARAMETERS
                        m_priv->patternConverters.push_back(eventConverter);
                }
        }
-       m_priv->expectedPatternLength = getFormattedEventCharacterCount() * 2;
+       m_priv->expectedPatternLength = 
priv::doubledLayoutSize(getFormattedEventCharacterCount());
 }
 
 #define RULES_PUT(spec, cls) \
@@ -269,4 +270,3 @@ pattern::PatternConverterPtr 
PatternLayout::createColorStartPatternConverter(con
        return colorPatternConverter;
 }
 
-
diff --git a/src/main/cpp/xmllayout.cpp b/src/main/cpp/xmllayout.cpp
index 59ced7e0..e56c0de3 100644
--- a/src/main/cpp/xmllayout.cpp
+++ b/src/main/cpp/xmllayout.cpp
@@ -25,6 +25,7 @@
 #include <log4cxx/helpers/stringhelper.h>
 #include <log4cxx/helpers/transcoder.h>
 #include <log4cxx/ndc.h>
+#include <log4cxx/private/layout_priv.h>
 
 
 using namespace LOG4CXX_NS;
@@ -53,7 +54,7 @@ IMPLEMENT_LOG4CXX_OBJECT(XMLLayout)
 XMLLayout::XMLLayout()
        : m_priv(std::make_unique<XMLLayoutPrivate>())
 {
-       m_priv->expectedPatternLength = getFormattedEventCharacterCount() * 2;
+       m_priv->expectedPatternLength = 
priv::doubledLayoutSize(getFormattedEventCharacterCount());
 }
 
 XMLLayout::~XMLLayout() {}
@@ -64,20 +65,20 @@ void XMLLayout::setOption(const LogString& option,
        if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("LOCATIONINFO"), 
LOG4CXX_STR("locationinfo")))
        {
                setLocationInfo(OptionConverter::toBoolean(value, false));
-               m_priv->expectedPatternLength = 
getFormattedEventCharacterCount() * 2;
+               m_priv->expectedPatternLength = 
priv::doubledLayoutSize(getFormattedEventCharacterCount());
        }
 
        if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("PROPERTIES"), 
LOG4CXX_STR("properties")))
        {
                setProperties(OptionConverter::toBoolean(value, false));
-               m_priv->expectedPatternLength = 
getFormattedEventCharacterCount() * 2;
+               m_priv->expectedPatternLength = 
priv::doubledLayoutSize(getFormattedEventCharacterCount());
        }
 }
 
 void XMLLayout::format( LOG4CXX_FORMAT_LAYOUT_FORMAL_PARAMETERS ) const
 {
        auto& lsMsg = event->getRenderedMessage();
-       output.reserve(m_priv->expectedPatternLength + lsMsg.size());
+       priv::reserveFormattedEvent(output, m_priv->expectedPatternLength, 
lsMsg.size());
        output.append(LOG4CXX_STR("<log4j:event logger=\""));
        Transform::appendLegalCharacters(output, event->getLoggerName());
        output.append(LOG4CXX_STR("\" timestamp=\""));
@@ -193,4 +194,3 @@ bool XMLLayout::getProperties()
 {
        return m_priv->properties;
 }
-
diff --git a/src/main/include/log4cxx/private/layout_priv.h 
b/src/main/include/log4cxx/private/layout_priv.h
new file mode 100644
index 00000000..f4b9b02c
--- /dev/null
+++ b/src/main/include/log4cxx/private/layout_priv.h
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOG4CXX_PRIVATE_LAYOUT_PRIV_H
+#define _LOG4CXX_PRIVATE_LAYOUT_PRIV_H
+
+#include <log4cxx/logstring.h>
+
+#include <limits>
+
+namespace LOG4CXX_NS
+{
+namespace priv
+{
+       // Saturating doubling helper for layout size estimation.
+       inline size_t doubledLayoutSize(size_t value)
+       {
+               const size_t maxSize = (std::numeric_limits<size_t>::max)();
+               return value > maxSize / 2 ? maxSize : value * 2;
+       }
+
+       // Reserve only when the combined size fits within max_size().
+       inline void reserveFormattedEvent(LogString& output, size_t fixedSize, 
size_t messageSize)
+       {
+               const size_t maxSize = output.max_size();
+               if (messageSize <= maxSize && fixedSize <= maxSize - 
messageSize)
+               {
+                       output.reserve(fixedSize + messageSize);
+               }
+       }
+}
+}
+
+#endif // _LOG4CXX_PRIVATE_LAYOUT_PRIV_H
diff --git a/src/test/cpp/jsonlayouttest.cpp b/src/test/cpp/jsonlayouttest.cpp
index f6b175a2..870514cc 100644
--- a/src/test/cpp/jsonlayouttest.cpp
+++ b/src/test/cpp/jsonlayouttest.cpp
@@ -20,8 +20,10 @@
 #include <log4cxx/jsonlayout.h>
 #include <log4cxx/ndc.h>
 #include <log4cxx/mdc.h>
+#include <log4cxx/private/layout_priv.h>
 
 #include <iostream>
+#include <limits>
 #include <log4cxx/helpers/stringhelper.h>
 #include <log4cxx/helpers/transcoder.h>
 
@@ -61,6 +63,9 @@ LOGUNIT_CLASS(JSONLayoutTest), public JSONLayout
        LOGUNIT_TEST(testFormatWithPrettyPrint);
        LOGUNIT_TEST(testGetSetLocationInfo);
        LOGUNIT_TEST(testAppendQuotedEscapedString);
+       LOGUNIT_TEST(testLayoutSizeOverflowGuards);
+       LOGUNIT_TEST(testLayoutSizeEdgeCases);
+       LOGUNIT_TEST(testReserveFormattedEventSizing);
        LOGUNIT_TEST_SUITE_END();
 
 
@@ -520,8 +525,50 @@ public:
                appendQuotedEscapedString(escapedQuoted0xD822Name, 
problemNameLS);
                LOGUNIT_ASSERT_EQUAL(expectedQuotedEscapedName, 
escapedQuoted0xD822Name);
        }
+
+       void testLayoutSizeOverflowGuards()
+       {
+               const size_t maxSize = (std::numeric_limits<size_t>::max)();
+               LOGUNIT_ASSERT_EQUAL(maxSize, priv::doubledLayoutSize(maxSize));
+               LOGUNIT_ASSERT_EQUAL(maxSize - 1, 
priv::doubledLayoutSize((maxSize - 1) / 2));
+
+               LogString output;
+               const size_t initialCapacity = output.capacity();
+               priv::reserveFormattedEvent(output, output.max_size(), 1);
+               LOGUNIT_ASSERT_EQUAL(initialCapacity, output.capacity());
+       }
+
+       void testLayoutSizeEdgeCases()
+       {
+               const size_t maxSize = (std::numeric_limits<size_t>::max)();
+               LOGUNIT_ASSERT_EQUAL(static_cast<size_t>(0), 
priv::doubledLayoutSize(0));
+               LOGUNIT_ASSERT_EQUAL(static_cast<size_t>(2), 
priv::doubledLayoutSize(1));
+               LOGUNIT_ASSERT_EQUAL(maxSize - 1, 
priv::doubledLayoutSize(maxSize / 2));
+               LOGUNIT_ASSERT_EQUAL(maxSize, priv::doubledLayoutSize((maxSize 
/ 2) + 1));
+               LOGUNIT_ASSERT_EQUAL(maxSize, priv::doubledLayoutSize(maxSize));
+       }
+
+       void testReserveFormattedEventSizing()
+       {
+               LogString output;
+               const size_t initialCapacity = output.capacity();
+
+               priv::reserveFormattedEvent(output, 16, 8);
+               LOGUNIT_ASSERT(output.capacity() >= 24);
+               LOGUNIT_ASSERT(output.capacity() >= initialCapacity);
+
+               const size_t maxSize = output.max_size();
+               const size_t fixedSize = 16;
+               const size_t boundaryMessageSize = maxSize - fixedSize;
+               LOGUNIT_ASSERT_EQUAL(maxSize, fixedSize + boundaryMessageSize);
+               LOGUNIT_ASSERT(boundaryMessageSize <= maxSize);
+               LOGUNIT_ASSERT(fixedSize <= maxSize - boundaryMessageSize);
+
+               const size_t capacityBeforeSkip = output.capacity();
+               priv::reserveFormattedEvent(output, 16, 
(std::numeric_limits<size_t>::max)());
+               LOGUNIT_ASSERT_EQUAL(capacityBeforeSkip, output.capacity());
+       }
 };
 
 
 LOGUNIT_TEST_SUITE_REGISTRATION(JSONLayoutTest);
-

Reply via email to