On June 18, 2026 7:44:31 PM UTC, Andrew Kloet <[email protected]> wrote: >>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);
bump
