[PATCH 1 of 2] HTTP: uniform overflow checks in ngx_http_alloc_large_header_buffer
Maxim Dounin
mdounin at mdounin.ru
Tue Nov 28 02:58:23 UTC 2023
Hello!
On Fri, Nov 10, 2023 at 12:11:54PM +0300, Vladimir Homutov via nginx-devel wrote:
> If URI is not fully parsed yet, some pointers are not set.
> As a result, the calculation of "new + (ptr - old)" expression
> may overflow. In such a case, just avoid calculating it, as value
> will be set correctly later by the parser in any case.
>
> The issue was found by GCC undefined behaviour sanitizer.
>
>
> src/http/ngx_http_request.c | 34 ++++++++++++++++++++++++++--------
> 1 files changed, 26 insertions(+), 8 deletions(-)
>
>
> # HG changeset patch
> # User Vladimir Khomutov <vl at wbsrv.ru>
> # Date 1699604478 -10800
> # Fri Nov 10 11:21:18 2023 +0300
> # Node ID 505e927eb7a75f0fdce4caddb4ab9d9c71c9b9c8
> # Parent dadd13fdcf5228c8e8380e120d4621002e3b0919
> HTTP: uniform overflow checks in ngx_http_alloc_large_header_buffer.
>
> If URI is not fully parsed yet, some pointers are not set.
> As a result, the calculation of "new + (ptr - old)" expression
> may overflow. In such a case, just avoid calculating it, as value
> will be set correctly later by the parser in any case.
>
> The issue was found by GCC undefined behaviour sanitizer.
I would rather refrain from saying this is an issue, it is not
(unless a compiler actually starts to do silly things as long as
it sees something formally defined as "undefined behavior" in C
standard, and this would be indeed an issue - in the compiler).
As already noted in the initial review, the code as written is
completely safe in practice. For such mostly style commits we
usually write something like "Prodded by...".
Also note that the issue is not an overflow, but rather
subtraction of pointers which do not belong to the same array
object (C11, 6.5.6 Additive operators, p.9):
: When two pointers are subtracted, both shall point to elements
: of the same array object, or one past the last element of the
: array object
The undefined behaviour is present as long as "ptr" and "old" are
not in the same buffer (i.e., array object), which is the case
when "ptr" is not set. And another one follows when trying to add
the (already undefined) subtraction result to "new" (since the
result is not going to belong to the same array object):
: If both the pointer operand and the result point to elements of
: the same array object, or one past the last element of the array
: object, the evaluation shall not produce an overflow; otherwise,
: the behavior is undefined.
Overflow here might be an indicator that certainly there is an
undefined behaviour, but it's just an indicator.
You may want to rewrite commit log accordingly.
>
> diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
> --- a/src/http/ngx_http_request.c
> +++ b/src/http/ngx_http_request.c
> @@ -1718,14 +1718,23 @@ ngx_http_alloc_large_header_buffer(ngx_h
> r->request_end = new + (r->request_end - old);
> }
>
> - r->method_end = new + (r->method_end - old);
> -
> - r->uri_start = new + (r->uri_start - old);
> - r->uri_end = new + (r->uri_end - old);
> + if (r->method_end) {
> + r->method_end = new + (r->method_end - old);
> + }
> +
> + if (r->uri_start) {
> + r->uri_start = new + (r->uri_start - old);
> + }
> +
> + if (r->uri_end) {
> + r->uri_end = new + (r->uri_end - old);
> + }
>
> if (r->schema_start) {
> r->schema_start = new + (r->schema_start - old);
> - r->schema_end = new + (r->schema_end - old);
> + if (r->schema_end) {
> + r->schema_end = new + (r->schema_end - old);
> + }
> }
>
> if (r->host_start) {
See review of the second patch about r->port_start / r->port_end.
I would rather change it similarly for now.
> @@ -1754,9 +1763,18 @@ ngx_http_alloc_large_header_buffer(ngx_h
>
> } else {
> r->header_name_start = new;
> - r->header_name_end = new + (r->header_name_end - old);
> - r->header_start = new + (r->header_start - old);
> - r->header_end = new + (r->header_end - old);
> +
> + if (r->header_name_end) {
> + r->header_name_end = new + (r->header_name_end - old);
> + }
> +
> + if (r->header_start) {
> + r->header_start = new + (r->header_start - old);
> + }
> +
> + if (r->header_end) {
> + r->header_end = new + (r->header_end - old);
> + }
> }
>
> r->header_in = b;
Otherwise looks good.
--
Maxim Dounin
http://mdounin.ru/
More information about the nginx-devel
mailing list