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);
-