[PATCH 1 of 2] HTTP: uniform overflow checks in ngx_http_alloc_large_header_buffer

Maxim Dounin mdounin at mdounin.ru
Thu Nov 30 01:15:53 UTC 2023


Hello!

On Wed, Nov 29, 2023 at 11:24:03AM +0300, Vladimir Homutov via nginx-devel wrote:

> On Tue, Nov 28, 2023 at 05:58:23AM +0300, Maxim Dounin wrote:
> > 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...".
> 
> totally agreed
> 
> >
> > 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.
> 
> The commit log was updated.
> 
> > > 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
> [...]
> > >          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.
> 
> I would prefer to remove both, so this patch has nothing about it.
> 
> updated patch:
> 
> 

> # HG changeset patch
> # User Vladimir Khomutov <vl at wbsrv.ru>
> # Date 1701245585 -10800
> #      Wed Nov 29 11:13:05 2023 +0300
> # Node ID 7c8ecb3fee20dfbb9a627441377dd09509988e2a
> # Parent  dacad3a9c7b8435a4c67ad2b67f261e7b4e36d8e
> HTTP: uniform 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 is flawed.
> 
> According to  C11, 6.5.6 Additive operators, p.9:

Seems to be an extra space in "to  C11".

> 
> : 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
> 
> Since "ptr" is not set, subtraction leads to undefined behaviour, because
> "ptr" and "old" are not in the same buffer (i.e. array objects).
> 
> Prodded by GCC undefined behaviour sanitizer.
> 
> 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) {
> @@ -1749,9 +1758,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, pushed to http://mdounin.ru/hg/nginx/ (with 
the extra space removed), thanks.

-- 
Maxim Dounin
http://mdounin.ru/


More information about the nginx-devel mailing list