Fix stack overflow: use small stack buffer with heap fallback in query_key_value

Revert direct req->uri access (unsafe — ESP-IDF uses parsed offsets,
not strchr). Instead fix the root cause: query_key_value used a full
CONFIG_HTTPD_MAX_URI_LEN+1 (513 byte) stack buffer while
search_query_sources had another 513 byte buffer on the stack
simultaneously, totaling ~1KB on the httpd thread's limited stack.

Use SmallBufferWithHeapFallback<128> for the value extraction buffer.
128 bytes covers typical parameter values on stack; longer values
(e.g. base64 IR data) fall back to heap.
This commit is contained in:
J. Nick Koston
2026-02-11 18:53:58 -06:00
parent 5a88bb6d8a
commit 2b5bd961ef
2 changed files with 13 additions and 13 deletions

View File

@@ -55,13 +55,16 @@ optional<std::string> query_key_value(const char *query_url, size_t query_len, c
return {};
}
char val[CONFIG_HTTPD_MAX_URI_LEN + 1];
if (httpd_query_key_value(query_url, key, val, query_len) != ESP_OK) {
// Value can't exceed query_len. Use small stack buffer for typical values,
// heap fallback for long ones (e.g. base64 IR data) to limit stack usage
// since callers may also have stack buffers for the query string.
SmallBufferWithHeapFallback<128, char> val(query_len);
if (httpd_query_key_value(query_url, key, val.get(), query_len) != ESP_OK) {
return {};
}
url_decode(val);
return {val};
url_decode(val.get());
return {val.get()};
}
bool query_has_key(const char *query_url, size_t query_len, const char *key) {

View File

@@ -408,7 +408,6 @@ AsyncWebParameter *AsyncWebServerRequest::getParam(const char *name) {
/// Search post_query then URL query with a callback.
/// Returns first truthy result, or value-initialized default.
/// URL query is accessed directly from req->uri to avoid stack buffer copy.
template<typename Func>
static auto search_query_sources(httpd_req_t *req, const std::string &post_query, const char *name, Func func)
-> decltype(func(nullptr, size_t{0}, name)) {
@@ -418,17 +417,15 @@ static auto search_query_sources(httpd_req_t *req, const std::string &post_query
return result;
}
}
// Access query string directly from URI — no copy needed
const char *query = strchr(req->uri, '?');
if (query == nullptr) {
return {};
}
query++; // skip '?'
size_t len = strlen(query);
auto len = httpd_req_get_url_query_len(req);
if (len == 0) {
return {};
}
return func(query, len, name);
char buf[AsyncWebServerRequest::URL_BUF_SIZE];
if (httpd_req_get_url_query_str(req, buf, len + 1) != ESP_OK) {
return {};
}
return func(buf, len, name);
}
optional<std::string> AsyncWebServerRequest::find_query_value_(const char *name) const {