httpd: HTTP suffix-range requests omits file contents
Andrew Kloet <[email protected]> Thu, 18 Jun 2026 15:44:31 -0400
| Newsgroups | gmane.os.openbsd.bugs |
|---|---|
| Message-ID | <[email protected]> |
>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=3D-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 =3D 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=3D-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 =3D=3D 0) && (end_str_len =3D=3D 0)) return (0); =20 - if (end_str_len) { + if (start_str_len =3D=3D 0) { r->end =3D strtonum(end_str, 0, LLONG_MAX, &errstr); - if (errstr) + if (errstr || r->end =3D=3D 0) return (0); =20 - if ((size_t)r->end >=3D size) - r->end =3D size - 1; - } else + if ((size_t)r->end > size) + r->start =3D 0; + else + r->start =3D size - r->end; r->end =3D size - 1; + return (1); + } =20 if (start_str_len) { r->start =3D strtonum(start_str, 0, LLONG_MAX, &errstr); @@ -814,11 +817,17 @@ parse_range_spec(char *str, size_t size, struct range= *r) =20 if ((size_t)r->start >=3D size) return (0); - } else { - r->start =3D size - r->end; - r->end =3D size - 1; } =20 + if (end_str_len) { + r->end =3D strtonum(end_str, 0, LLONG_MAX, &errstr); + if (errstr) + return (0); + if ((size_t)r->end >=3D size) + r->end =3D size - 1; + } else + r->end =3D size - 1; + if (r->end < r->start) return (0);