summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHauke Mehrtens2026-04-23 22:17:01 +0000
committerHauke Mehrtens2026-06-07 11:23:54 +0000
commit440ca260622dd0701c343e40b1142a98e9ddd24c (patch)
tree780d5a8e9b38ec8c7984d0946803b8aeea0a1710
parent4c4a61a69ac39db20dad8dd556f235f1ae6523a3 (diff)
downloaduclient-440ca260622dd0701c343e40b1142a98e9ddd24c.tar.gz
uclient-http: validate Content-Length and chunk sizes from the server
Both Content-Length and the hex chunk size in chunked transfer encoding were parsed with strtoul() and then stored in a signed long. A server that returned a negative value, a value larger than LONG_MAX, or trailing garbage could wrap the stored length to a negative number or an otherwise bogus value. Downstream code then misinterpreted the state (e.g. content_length = -1 is "unknown length"), which could keep a connection open past its real body or confuse the chunked decoder. Parse both with strtoll() and reject empty input, negative values and overflow (LONG_MAX). For Content-Length also reject trailing garbage and fall back to -1 ("unknown length") on any malformed value. For the chunk size do not reject trailing characters, since a valid chunk size may be followed by a ';'-prefixed chunk extension; abort the transfer instead when the size itself cannot be parsed. Assisted-by: Claude:claude-opus-4-7 Link: https://github.com/openwrt/uclient/pull/16 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
-rw-r--r--uclient-http.c27
1 files changed, 23 insertions, 4 deletions
diff --git a/uclient-http.c b/uclient-http.c
index 8132e5a..6663628 100644
--- a/uclient-http.c
+++ b/uclient-http.c
@@ -22,6 +22,8 @@
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
#include <libubox/ustream.h>
#include <libubox/ustream-ssl.h>
@@ -310,8 +312,18 @@ static void uclient_http_process_headers(struct uclient_http *uh)
uh->connection_close = true;
cur = tb[HTTP_HDR_CONTENT_LENGTH];
- if (cur)
- uh->content_length = strtoul(blobmsg_data(cur), NULL, 10);
+ if (cur) {
+ const char *val = blobmsg_data(cur);
+ char *end;
+ long long len;
+
+ errno = 0;
+ len = strtoll(val, &end, 10);
+ if (end == val || *end || errno || len < 0 || len > LONG_MAX)
+ uh->content_length = -1;
+ else
+ uh->content_length = len;
+ }
cur = tb[HTTP_HDR_AUTH];
if (cur) {
@@ -1156,7 +1168,8 @@ uclient_http_read(struct uclient *cl, char *buf, unsigned int len)
read_len = 0;
if (uh->read_chunked == 0) {
- char *sep;
+ char *sep, *end;
+ long long chunk_len;
if (data[0] == '\r' && data[1] == '\n') {
data += 2;
@@ -1168,7 +1181,13 @@ uclient_http_read(struct uclient *cl, char *buf, unsigned int len)
return 0;
*sep = 0;
- uh->read_chunked = strtoul(data, NULL, 16);
+ errno = 0;
+ chunk_len = strtoll(data, &end, 16);
+ if (end == data || chunk_len < 0 || chunk_len > LONG_MAX || errno) {
+ uclient_http_error(uh, UCLIENT_ERROR_UNKNOWN);
+ return 0;
+ }
+ uh->read_chunked = chunk_len;
read_len += sep + 2 - data;
data = sep + 2;