>Synopsis: HTTP suffix-range requests omits file contents
>Category: user
>Environment:
System : OpenBSD 7.9
Details : OpenBSD 7.9 (GENERIC.MP) #449: Wed May 6 13:17:25 MDT
2026
[email protected]:/usr/src/sys/arch/amd64/compile/GENERIC.MP
Architecture: OpenBSD.amd64
Machine : amd64
>Description:
There is a logic flaw inside httpd's parse_range_sepc when
processing HTTP Byte-Range Requests.
When a client issues a suffix-range request (Range: bytes=-num to
request the trailing num bytes of a resource) where the requested
count is larger than the target file size, an overflow guard caps
r->end to size - 1 prematurely.
Because this truncation happens before the evaluation block
calculates the starting byte offset (r->start = size - r->end), the
resulting offset math evaluates to 1 instead of 0. As a result,
httpd serves a 206 Partial Content response that completely omits
the very first byte (offset 0) of the target file.
>How-To-Repeat:
# Create an 11-byte file
$ echo "0123456789" > /var/www/htdocs/test
# Send an HTTP request with a suffix range exceeding the file size.
# Note the response headers show an incorrect range and omit the
# leading character
$ curl -v -H "Range: bytes=-15" http://127.0.0.1/test
< HTTP/1.1 206 Partial Content
< Content-Range: bytes 1-10/11
< Content-Length: 10
123456789
>Fix:
Decouple the suffix range processing from standard ranges within
parse_range_spec so that ceiling adjustments to r->end don't corrupt
the initial offset computation.
--- a/usr.sbin/httpd/server_file.c
+++ b/usr.sbin/httpd/server_file.c
@@ -797,15 +797,18 @@ parse_range_spec(char *str, size_t size, struct range *r)
if ((start_str_len == 0) && (end_str_len == 0))
return (0);
- if (end_str_len) {
+ if (start_str_len == 0) {
r->end = strtonum(end_str, 0, LLONG_MAX, &errstr);
- if (errstr)
+ if (errstr || r->end == 0)
return (0);
- if ((size_t)r->end >= size)
- r->end = size - 1;
- } else
+ if ((size_t)r->end > size)
+ r->start = 0;
+ else
+ r->start = size - r->end;
r->end = size - 1;
+ return (1);
+ }
if (start_str_len) {
r->start = strtonum(start_str, 0, LLONG_MAX, &errstr);
@@ -814,11 +817,17 @@ parse_range_spec(char *str, size_t size, struct range *r)
if ((size_t)r->start >= size)
return (0);
- } else {
- r->start = size - r->end;
- r->end = size - 1;
}
+ if (end_str_len) {
+ r->end = strtonum(end_str, 0, LLONG_MAX, &errstr);
+ if (errstr)
+ return (0);
+ if ((size_t)r->end >= size)
+ r->end = size - 1;
+ } else
+ r->end = size - 1;
+
if (r->end < r->start)
return (0);