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

chenBright pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new 79666636 Fix char-signedness out-of-bounds read in url and header 
lookup tables (#3376)
79666636 is described below

commit 796666368ac775e876be28645d4bbebe89a3ad07
Author: UB <[email protected]>
AuthorDate: Thu Jul 16 14:49:06 2026 +0530

    Fix char-signedness out-of-bounds read in url and header lookup tables 
(#3376)
    
    * fix char-signedness out-of-bounds read in url and header lookup tables
    
    * test: cover high-bit bytes in uri and ascii_tolower lookups
    
    Signed-off-by: ubeddulla khan <[email protected]>
    
    ---------
    
    Signed-off-by: ubeddulla khan <[email protected]>
---
 src/brpc/uri.cpp                             |  4 ++--
 src/butil/containers/case_ignored_flat_map.h |  6 +++++-
 test/brpc_uri_unittest.cpp                   | 22 ++++++++++++++++++++++
 test/flat_map_unittest.cpp                   | 10 ++++++++++
 4 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/src/brpc/uri.cpp b/src/brpc/uri.cpp
index 5391368c..cb48a43d 100644
--- a/src/brpc/uri.cpp
+++ b/src/brpc/uri.cpp
@@ -168,7 +168,7 @@ int URI::SetHttpURL(const char* url) {
     bool need_scheme = true;
     bool need_user_info = true;
     for (; true; ++p) {
-        const char action = g_url_parsing_fast_action_map[(int)*p];
+        const char action = g_url_parsing_fast_action_map[(signed char)*p];
         if (action == URI_PARSE_CONTINUE) {
             continue;
         }
@@ -256,7 +256,7 @@ int ParseURL(const char* url,
     bool need_scheme = true;
     bool need_user_info = true;
     for (; true; ++p) {
-        const char action = g_url_parsing_fast_action_map[(int)*p];
+        const char action = g_url_parsing_fast_action_map[(signed char)*p];
         if (action == URI_PARSE_CONTINUE) {
             continue;
         }
diff --git a/src/butil/containers/case_ignored_flat_map.h 
b/src/butil/containers/case_ignored_flat_map.h
index 909e7317..13b36eb6 100644
--- a/src/butil/containers/case_ignored_flat_map.h
+++ b/src/butil/containers/case_ignored_flat_map.h
@@ -29,7 +29,11 @@ namespace butil {
 // note: using char caused crashes on ubuntu 20.04 aarch64 (VM on apple M1)
 inline char ascii_tolower(int/*note*/ c) {
     extern const signed char* const g_tolower_map;
-    return g_tolower_map[c];
+    // g_tolower_map is biased by +128 and sized for a signed-char index
+    // ([-128,127]). Callers pass a `char`; on platforms where `char` is
+    // unsigned (aarch64, riscv64) a byte >= 0x80 arrives here as 128..255 and
+    // indexes past the 256-entry table. Fold back into signed-char range.
+    return g_tolower_map[(signed char)c];
 }
 
 struct CaseIgnoredHasher {
diff --git a/test/brpc_uri_unittest.cpp b/test/brpc_uri_unittest.cpp
index 2e8965d2..14638b86 100644
--- a/test/brpc_uri_unittest.cpp
+++ b/test/brpc_uri_unittest.cpp
@@ -303,6 +303,28 @@ TEST(URITest, invalid_query) {
     ASSERT_EQ("a-b-c:def", uri.query());
 }
 
+TEST(URITest, high_bit_bytes) {
+    // Bytes >= 0x80 (e.g. UTF-8 in the host/path) index the +128-biased
+    // action table. On unsigned-char platforms they would read past the
+    // 256-entry table; this trips a buffer-overflow read under ASan without
+    // the signed-char fold. They are ordinary characters for the parser.
+    brpc::URI uri;
+    const std::string url =
+        "http://user:passwd@\xc3\xa9.example.com/\xc3\xa9?k=\xc3\xa9#\xc3\xa9";;
+    ASSERT_EQ(0, uri.SetHttpURL(url)) << uri.status();
+    ASSERT_EQ("\xc3\xa9.example.com", uri.host());
+    ASSERT_EQ("/\xc3\xa9", uri.path());
+    ASSERT_EQ("k=\xc3\xa9", uri.query());
+    ASSERT_EQ("\xc3\xa9", uri.fragment());
+
+    std::string scheme;
+    std::string host_out;
+    int port_out = -1;
+    ASSERT_EQ(0, brpc::ParseURL(url.c_str(), &scheme, &host_out, &port_out));
+    ASSERT_EQ("http", scheme);
+    ASSERT_EQ("\xc3\xa9.example.com", host_out);
+}
+
 TEST(URITest, print_url) {
     brpc::URI uri;
 
diff --git a/test/flat_map_unittest.cpp b/test/flat_map_unittest.cpp
index a394218a..2721d57e 100644
--- a/test/flat_map_unittest.cpp
+++ b/test/flat_map_unittest.cpp
@@ -245,6 +245,16 @@ TEST_F(FlatMapTest, to_lower) {
         ASSERT_EQ((char)::tolower(c), butil::ascii_tolower(c)) << "c=" << c;
     }
 
+    // Real callers (header names, HPACK) reach ascii_tolower with a `char`.
+    // On unsigned-char platforms a byte >= 0x80 promotes to 128..255, which
+    // would index past the +128-biased 256-entry map. Exercise every byte
+    // value as a `char` and check the result against a safe reference; this
+    // trips a buffer-overflow read under ASan without the signed-char fold.
+    for (int i = 0; i < 256; ++i) {
+        const char ch = (char)i;
+        ASSERT_EQ((char)::tolower(i), butil::ascii_tolower(ch)) << "i=" << i;
+    }
+
     const size_t input_len = 102;
     char input[input_len + 1];
     char input2[input_len + 1];


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to