From 83d278d2907b02efa48a070dc0ae0ece889a6d84 Mon Sep 17 00:00:00 2001 From: "Adam C. Emerson" Date: Wed, 25 Mar 2026 14:54:27 -0400 Subject: [PATCH] rgw: Rest client header parsing does not use VLAs Signed-off-by: Adam C. Emerson --- src/rgw/rgw_rest_client.cc | 119 +++++++++++++++++-------------------- src/rgw/rgw_rest_client.h | 4 +- 2 files changed, 58 insertions(+), 65 deletions(-) diff --git a/src/rgw/rgw_rest_client.cc b/src/rgw/rgw_rest_client.cc index c442232f8d85..5062875d19f3 100644 --- a/src/rgw/rgw_rest_client.cc +++ b/src/rgw/rgw_rest_client.cc @@ -7,7 +7,6 @@ #include "rgw_auth_s3.h" #include "rgw_http_errors.h" -#include "common/armor.h" #include "common/strtol.h" #include "include/str_list.h" #include "rgw_crypt_sanitize.h" @@ -26,17 +25,16 @@ int RGWHTTPSimpleRequest::get_status() return status; } -int RGWHTTPSimpleRequest::handle_header(const string& name, const string& val) +int RGWHTTPSimpleRequest::handle_header(std::string_view name, std::string_view val) { if (name == "CONTENT_LENGTH") { - string err; - long len = strict_strtol(val.c_str(), 10, &err); - if (!err.empty()) { + auto len = ceph::parse(val); + if (!len) { ldpp_dout(this, 0) << "ERROR: failed converting content length (" << val << ") to int " << dendl; return -EINVAL; } - max_response = len; + max_response = *len; } return 0; @@ -46,63 +44,59 @@ int RGWHTTPSimpleRequest::receive_header(void *ptr, size_t len) { unique_lock guard(out_headers_lock); - char line[len + 1]; - char *s = (char *)ptr, *end = (char *)ptr + len; - char *p = line; + std::string_view line(static_cast(ptr), len); ldpp_dout(this, 30) << "receive_http_header" << dendl; - while (s != end) { - if (*s == '\r') { - s++; - continue; - } - if (*s == '\n') { - if (p == line) { - // End of headers (empty line "\r\n") - ldpp_dout(this, 30) << "All headers received" << dendl; - return handle_headers(out_headers, http_status); - } - *p = '\0'; - ldpp_dout(this, 30) << "received header: " << line << dendl; - // TODO: fill whatever data required here - char *l = line; - char *tok = strsep(&l, " \t:"); - if (tok && l) { - while (*l == ' ') - l++; - - if (strcmp(tok, "HTTP") == 0 || strncmp(tok, "HTTP/", 5) == 0) { - http_status = atoi(l); - if (http_status == 100) /* 100-continue response */ - continue; - status = rgw_http_error_to_errno(http_status); - } else { - /* convert header field name to upper case */ - char *src = tok; - char buf[len + 1]; - size_t i; - for (i = 0; i < len && *src; ++i, ++src) { - switch (*src) { - case '-': - buf[i] = '_'; - break; - default: - buf[i] = toupper(*src); - } - } - buf[i] = '\0'; - out_headers[buf] = l; - int r = handle_header(buf, l); - if (r < 0) - return r; - } - } - p = line; + if (line == "\r\n" || line == "\n") { + // End of headers (empty line "\r\n") + ldpp_dout(this, 30) << "All headers received" << dendl; + return handle_headers(out_headers, http_status); + } + + if (line.ends_with("\r\n")) { + line.remove_suffix(2); + } else if (line.ends_with("\n")) { + line.remove_suffix(1); + } + ldpp_dout(this, 30) << "received header: " << line << dendl; + + auto trim = [](std::string_view & v) { + auto n = v.find_first_not_of(" \t"); + if (n == v.npos) { + v = std::string_view{}; + } else { + v.remove_prefix(n); } - if (s != end) - *p++ = *s++; + }; + trim(line); + if (line.empty()) { + return 0; } + auto sep = line.find_first_of(" \t:"); + if (sep == line.npos) { + return 0; + } + auto left = line.substr(0, sep); + auto right = line.substr(sep + 1, line.npos); + trim(right); + + if (left == "HTTP" || left.starts_with("HTTP/")) { + // First line + sep = right.find_first_of(" \t"); + auto status_tok = right.substr(0, sep); + // .value_or(0) mimics atol's behavior on receiving no digits. + http_status = ceph::parse(status_tok).value_or(0); + if (http_status == 100) { + return 0; + } + status = rgw_http_error_to_errno(http_status); + } else { + std::string header_name; + uppercase_dash_transform(left, std::back_inserter(header_name)); + out_headers[header_name] = std::string{right}; + return handle_header(header_name, right); + } return 0; } @@ -1010,17 +1004,16 @@ int RGWHTTPStreamRWRequest::handle_headers(const map& headers, i return 0; } -int RGWHTTPStreamRWRequest::handle_header(const string& name, const string& val) +int RGWHTTPStreamRWRequest::handle_header(std::string_view name, std::string_view val) { if (name == "RGWX_EMBEDDED_METADATA_LEN") { - string err; - long len = strict_strtol(val.c_str(), 10, &err); - if (!err.empty()) { + auto len = ceph::parse(val); + if (!len) { ldpp_dout(this, 0) << "ERROR: failed converting embedded metadata len (" << val << ") to int " << dendl; return -EINVAL; } - cb->set_extra_data_len(len); + cb->set_extra_data_len(*len); } return 0; } diff --git a/src/rgw/rgw_rest_client.h b/src/rgw/rgw_rest_client.h index 7e68d681f0e9..2d30e8313f6d 100644 --- a/src/rgw/rgw_rest_client.h +++ b/src/rgw/rgw_rest_client.h @@ -24,7 +24,7 @@ protected: size_t max_response; /* we need this as we don't stream out response */ bufferlist response; - virtual int handle_header(const std::string& name, const std::string& val); + virtual int handle_header(std::string_view name, std::string_view val); virtual int handle_headers(const std::map& headers, int http_status) { return 0; } void get_params_str(std::map& extra_args, std::string& dest); @@ -126,7 +126,7 @@ private: protected: bufferlist outbl; - int handle_header(const std::string& name, const std::string& val) override; + int handle_header(std::string_view name, std::string_view val) override; int handle_headers(const std::map& headers, int http_status) override; public: int send_data(void *ptr, size_t len, bool *pause) override; -- 2.47.3