[PATCH] Core: fixed environment on exit better

Roman Arutyunyan arut at nginx.com
Wed Jun 28 12:45:16 UTC 2023


Hi,

On Sat, Jun 24, 2023 at 05:06:46AM +0300, Maxim Dounin wrote:
> Hello!
> 
> On Fri, Jun 23, 2023 at 07:55:47PM +0400, Sergey Kandaurov wrote:
> 
> > # HG changeset patch
> > # User Sergey Kandaurov <pluknet at nginx.com>
> > # Date 1687535739 -14400
> > #      Fri Jun 23 19:55:39 2023 +0400
> > # Node ID 34a8e1a4161542896c11c4a5c60d6a6fe1931e3d
> > # Parent  c681f4906d838ac45b517fada4c4274ade03fd3c
> > Core: fixed environment on exit better.
> > 
> > The fix in 6822:c045b4926b2c to replace environment allocations from a pool
> > was incomplete, such that it left pool owned allocations for variables set
> > with values using the "env" directive.  Another consumer of this interface
> > is the QUIC BPF helper that uses the environment variable NGINX_BPF_MAPS.
> > The fix is to re-allocate variables with malloc, to avoid use after free.
> > If memory allocation failed, the only option is to expel from environment.
> > 
> > The observed symptoms are similar to described in 6822:c045b4926b2c, that
> > is a segmentation fault on worker process exit from atexit() handler seen
> > with 3rd party modules or if nginx was built with lcov profiling.
> > 
> > diff --git a/src/core/nginx.c b/src/core/nginx.c
> > --- a/src/core/nginx.c
> > +++ b/src/core/nginx.c
> > @@ -631,17 +631,48 @@ ngx_cleanup_environment(void *data)
> >  {
> >      char  **env = data;
> >  
> > -    if (environ == env) {
> > +    char        *p, **penv;
> > +    size_t       size;
> > +    ngx_uint_t   i, n;
> >  
> > -        /*
> > -         * if the environment is still used, as it happens on exit,
> > -         * the only option is to leak it
> > -         */
> > -
> > +    if (environ != env) {
> > +        ngx_free(env);
> >          return;
> >      }
> 
> Note: if we assume that the environment can be arbitrary modified, 
> I don't think it's enough.  For example, a setenv() call can 
> replace environ with something different, so this code will free 
> env and exit, but pool-allocated variables will be still 
> referenced by the new environ.

Another problem is if setenv() was called, environ may have been
reallocated with realloc() which may result in double-free in this function,
even with current code.

> This is probably out of the scope of this patch though.
> 
> >  
> > -    ngx_free(env);
> > +    /*
> > +     * if the environment is still used, as it happens on exit,
> > +     * the only option is to leak it
> > +     */
> > +
> > +    for (n = 0; env[n]; n++) { }
> > +
> > +    for (i = 0; env[i]; i++) {
> > +
> > +        for (penv = ngx_os_environ; *penv; penv++) {
> > +            if (ngx_strcmp(*penv, env[i]) == 0) {
> 
> I don't think it's going to work.  If I'm reading this correctly, 
> with the initial environment "FOO=bar" and "env FOO=bar;" in the 
> configuration this will assume the string is from ngx_os_environ, 
> but it will be from ccf->env instead.
> 
> > +                break;
> > +            }
> > +        }
> > +
> > +        if (*penv) {
> > +            continue;
> > +        }
> > +
> > +        /* substitute pool allocated variables */
> > +
> > +        size = ngx_strlen(env[i]) + 1;
> > +        p = malloc(size);
> 
> There should be no direct malloc() calls except in low-level code 
> where logging is not available.  I don't think this particular 
> place counts, so ngx_alloc() should be used instead.
> 
> > +
> > +        if (p) {
> > +            ngx_memcpy(p, env[i], size);
> > +            env[i] = p;
> > +
> > +        } else {
> > +            ngx_memmove(&env[i], &env[i + 1], sizeof(char *) * (n - i));
> > +            i--; n--;
> > +        }
> > +    }
> >  }
> 
> Overall, it looks like a better idea might be to simply add all 
> the variables added to the environment to the environ allocation 
> in ngx_set_environment().

Again, if setenv() is called, realloc() will free everything, including the
variables.  On the other hand, since setenv() is not supported anyway, and no
one complained, we can try this simple approach.

> In ngx_set_environment() we explicitly know if the particular 
> variable is from OS environment or from ccf->environ, so there 
> will be no need to guess.  Also, the appropriate log is readily 
> available (and already used in the existing ngx_alloc() call), and 
> the code to cleanup things is already there and will work without 
> additional modifications.
> 
> -- 
> Maxim Dounin
> http://mdounin.ru/
> _______________________________________________
> nginx-devel mailing list
> nginx-devel at nginx.org
> https://mailman.nginx.org/mailman/listinfo/nginx-devel

--
Roman Arutyunyan


More information about the nginx-devel mailing list