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 b3d9d651 Adopt Safe Buffers Model in hexdump() using std::span and add 
unsafe-buffer regression checks (#716)
b3d9d651 is described below

commit b3d9d651c4f078df87be4fdd0ee36af063000d1c
Author: sadd amr3e <[email protected]>
AuthorDate: Wed Jun 10 09:14:54 2026 +0530

    Adopt Safe Buffers Model in hexdump() using std::span and add unsafe-buffer 
regression checks (#716)
    
    Co-authored-by: metsw24-max <[email protected]>
---
 .../compiler-features/check-compiler-support.cmake |  7 +++
 src/main/cpp/CMakeLists.txt                        | 31 +++++++++++++
 src/main/cpp/hexdump.cpp                           | 53 ++++++++++++++++++----
 src/test/cpp/hexdumptestcase.cpp                   | 10 ++++
 4 files changed, 92 insertions(+), 9 deletions(-)

diff --git a/src/cmake/compiler-features/check-compiler-support.cmake 
b/src/cmake/compiler-features/check-compiler-support.cmake
index efa29f81..c2e86f73 100644
--- a/src/cmake/compiler-features/check-compiler-support.cmake
+++ b/src/cmake/compiler-features/check-compiler-support.cmake
@@ -1,5 +1,12 @@
 # This module checks compiler and standard library support
 #
+include(CheckCXXCompilerFlag)
+
+# Does the compiler support the Safe Buffers diagnostic -Wunsafe-buffer-usage?
+# (Clang >= 16.)  Used to enforce, on a per-file basis, that translation units
+# migrated to the Safe Buffers Programming Model stay free of unchecked raw
+# pointer/array arithmetic.  See LOG4CXX_ENABLE_SAFE_BUFFERS.
+check_cxx_compiler_flag("-Wunsafe-buffer-usage" 
LOG4CXX_HAS_WUNSAFE_BUFFER_USAGE)
 
 # Does the compiler support thread_local?
 if(MINGW)
diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt
index 5a2d3d22..44e2b4ba 100644
--- a/src/main/cpp/CMakeLists.txt
+++ b/src/main/cpp/CMakeLists.txt
@@ -264,3 +264,34 @@ endif()
 if(${ENABLE_FMT_LAYOUT})
     target_link_libraries(log4cxx PUBLIC fmt::fmt)
 endif()
+
+# --- Safe Buffers Programming Model enforcement ------------------------------
+# Translation units below have been migrated to the Safe Buffers Programming
+# Model: raw C buffers and pointer arithmetic over untrusted input are replaced
+# by bounds-aware views/containers (e.g. std::span).  Compiling them with
+# Clang's -Wunsafe-buffer-usage prevents the unsafe patterns from being
+# reintroduced.  Append a file to this list once it has been migrated.
+set(LOG4CXX_SAFE_BUFFERS_SOURCES
+  hexdump.cpp
+)
+
+option(LOG4CXX_ENABLE_SAFE_BUFFERS
+  "Compile Safe-Buffers-migrated sources with -Wunsafe-buffer-usage (Clang)" 
OFF)
+option(LOG4CXX_SAFE_BUFFERS_AS_ERROR
+  "Treat -Wunsafe-buffer-usage as an error in Safe-Buffers-migrated sources" 
OFF)
+
+if(LOG4CXX_ENABLE_SAFE_BUFFERS)
+  if(LOG4CXX_HAS_WUNSAFE_BUFFER_USAGE)
+    set(_safe_buffers_flags -Wunsafe-buffer-usage)
+    if(LOG4CXX_SAFE_BUFFERS_AS_ERROR)
+      list(APPEND _safe_buffers_flags -Werror=unsafe-buffer-usage)
+    endif()
+    set_source_files_properties(${LOG4CXX_SAFE_BUFFERS_SOURCES}
+      PROPERTIES COMPILE_OPTIONS "${_safe_buffers_flags}")
+    message(STATUS "Safe Buffers enforcement enabled for: 
${LOG4CXX_SAFE_BUFFERS_SOURCES}")
+  else()
+    message(WARNING
+      "LOG4CXX_ENABLE_SAFE_BUFFERS is ON but the compiler does not support "
+      "-Wunsafe-buffer-usage; Safe Buffers enforcement is disabled.")
+  endif()
+endif()
diff --git a/src/main/cpp/hexdump.cpp b/src/main/cpp/hexdump.cpp
index f535d184..69f89bfb 100644
--- a/src/main/cpp/hexdump.cpp
+++ b/src/main/cpp/hexdump.cpp
@@ -26,14 +26,49 @@
 #include <ios>
 #include <iomanip>
 #include <cctype>
+#include <cstddef>
+#include <cstdint>
+#if defined(__cpp_lib_span) && __cpp_lib_span >= 202002L
+       #include <span>
+#endif
 
 using namespace LOG4CXX_NS;
 
 typedef std::basic_stringstream<logchar> LogStream;
 
+namespace {
+
+// Safe Buffers Programming Model: hexdump() receives a raw (pointer, length)
+// pair describing an untrusted byte buffer (typically bytes just read from a
+// socket or file).  Indexing that pointer directly with computed offsets is
+// exactly the unsafe pattern that Clang's -Wunsafe-buffer-usage flags, because
+// a single off-by-one in the bookkeeping below would read out of bounds.
+//
+// To remove that hazard the formatting code is written once against a
+// bounds-aware contiguous view.  When std::span is available (C++20) it is
+// used directly so -Wunsafe-buffer-usage accepts the body; on older toolchains
+// a minimal drop-in view provides the same subset of the API.
+#if defined(__cpp_lib_span) && __cpp_lib_span >= 202002L
+using ByteView = std::span<const uint8_t>;
+#else
+class ByteView
+{
+       public:
+               ByteView(const uint8_t* data, std::size_t size) noexcept
+                       : m_data(data), m_size(size) {}
+               std::size_t size() const noexcept { return m_size; }
+               uint8_t operator[](std::size_t index) const noexcept { return 
m_data[index]; }
+       private:
+               const uint8_t* m_data;
+               std::size_t m_size;
+};
+#endif
+
+} // namespace
+
 LogString LOG4CXX_NS::hexdump(const void* bytes, uint32_t len, HexdumpFlags 
flags){
        LogString ret;
-       const uint8_t* bytes_u8 = static_cast<const uint8_t*>(bytes);
+       const ByteView data{ static_cast<const uint8_t*>(bytes), len };
        LogStream sstream;
 #if LOG4CXX_LOGCHAR_IS_WCHAR
        const wchar_t fill_char = L'0';
@@ -47,7 +82,7 @@ LogString LOG4CXX_NS::hexdump(const void* bytes, uint32_t 
len, HexdumpFlags flag
                sstream << LOG4CXX_EOL;
        }
 
-       for(uint32_t offset = 0; offset < len; offset += 16){
+       for(uint32_t offset = 0; offset < data.size(); offset += 16){
                if(offset != 0){
                        sstream << LOG4CXX_EOL;
                }
@@ -59,7 +94,7 @@ LogString LOG4CXX_NS::hexdump(const void* bytes, uint32_t 
len, HexdumpFlags flag
 
                // Print out the first 8 bytes
                for(int byte = 0; byte < 8; byte++){
-                       if(offset + byte >= len){
+                       if(offset + byte >= data.size()){
                                sstream << LOG4CXX_STR("  ");
                                if(byte != 8){
                                        sstream << LOG4CXX_STR(" ");
@@ -67,7 +102,7 @@ LogString LOG4CXX_NS::hexdump(const void* bytes, uint32_t 
len, HexdumpFlags flag
                                continue;
                        }
 
-                       sstream << std::hex << std::setw(2) << 
std::setfill(fill_char) << static_cast<int>(bytes_u8[offset + byte]) << 
std::resetiosflags(std::ios_base::fmtflags(0));
+                       sstream << std::hex << std::setw(2) << 
std::setfill(fill_char) << static_cast<int>(data[offset + byte]) << 
std::resetiosflags(std::ios_base::fmtflags(0));
                        sstream << std::setfill(space_fill_char);
                        if(byte != 8){
                                sstream << LOG4CXX_STR(" ");
@@ -78,7 +113,7 @@ LogString LOG4CXX_NS::hexdump(const void* bytes, uint32_t 
len, HexdumpFlags flag
 
                // Print out the last 8 bytes
                for(int byte = 8; byte < 16; byte++){
-                       if(offset + byte >= len){
+                       if(offset + byte >= data.size()){
                                sstream << LOG4CXX_STR("  ");
                                if(byte != 15){
                                        sstream << LOG4CXX_STR(" ");
@@ -86,7 +121,7 @@ LogString LOG4CXX_NS::hexdump(const void* bytes, uint32_t 
len, HexdumpFlags flag
                                continue;
                        }
 
-                       sstream << std::hex << std::setw(2) << 
std::setfill(fill_char) << static_cast<int>(bytes_u8[offset + byte]) << 
std::resetiosflags(std::ios_base::fmtflags(0));
+                       sstream << std::hex << std::setw(2) << 
std::setfill(fill_char) << static_cast<int>(data[offset + byte]) << 
std::resetiosflags(std::ios_base::fmtflags(0));
                        if(byte != 15){
                                sstream << LOG4CXX_STR(" ");
                        }
@@ -95,11 +130,11 @@ LogString LOG4CXX_NS::hexdump(const void* bytes, uint32_t 
len, HexdumpFlags flag
                // Print out the ASCII text
                sstream << LOG4CXX_STR("  |");
                for(int byte = 0; byte < 16; byte++){
-                       if(offset + byte >= len){
+                       if(offset + byte >= data.size()){
                                break;
                        }
-                       if(std::isprint(bytes_u8[offset + byte])){
-                               logchar to_append = bytes_u8[offset + byte];
+                       if(std::isprint(static_cast<unsigned char>(data[offset 
+ byte]))){
+                               logchar to_append = data[offset + byte];
                                sstream << to_append;
                        }else{
                                sstream << LOG4CXX_STR(".");
diff --git a/src/test/cpp/hexdumptestcase.cpp b/src/test/cpp/hexdumptestcase.cpp
index 66624c23..2f7b806b 100644
--- a/src/test/cpp/hexdumptestcase.cpp
+++ b/src/test/cpp/hexdumptestcase.cpp
@@ -28,6 +28,7 @@ LOGUNIT_CLASS(HexdumpTestCase)
        LOGUNIT_TEST(test2);
        LOGUNIT_TEST(test_newline);
        LOGUNIT_TEST(test_newline2);
+       LOGUNIT_TEST(test_empty);
        LOGUNIT_TEST_SUITE_END();
 
 public:
@@ -100,6 +101,15 @@ public:
                LOGUNIT_ASSERT_EQUAL(expectedOutput, dumped);
        }
 
+       // A zero-length buffer must not read any bytes and produces no rows.
+       // This exercises the lower bound of the Safe Buffers view used 
internally.
+       void test_empty()
+       {
+               LogString expectedOutput;
+               LogString dumped = log4cxx::hexdump(nullptr, 0);
+               LOGUNIT_ASSERT_EQUAL(expectedOutput, dumped);
+       }
+
 };
 
 

Reply via email to