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]