On 6/29/23 5:08 PM, Yann Ylavic wrote:
> On Tue, Apr 25, 2023 at 1:57 PM <rpl...@apache.org> wrote:
>>
>> --- httpd/httpd/trunk/modules/proxy/proxy_util.c (original)
>> +++ httpd/httpd/trunk/modules/proxy/proxy_util.c Tue Apr 25 11:57:22 2023
>> @@ -2790,7 +2790,11 @@ ap_proxy_determine_connection(apr_pool_t
>>                       * The single DNS lookup is used once per worker.
>>                       * If dynamic change is needed then set the addr to NULL
>>                       * inside dynamic config to force the lookup.
>> +                     *
>> +                     * Clear the dns_pool before to avoid a memory leak in 
>> case
>> +                     * we did the lookup again.
>>                       */
>> +                    apr_pool_clear(worker->cp->dns_pool);
>>                      err = apr_sockaddr_info_get(&addr,
>>                                                  conn->hostname, APR_UNSPEC,
>>                                                  conn->port, 0,
> 
> I'm not sure we can clear the dns_pool, some conn->addr allocated on
> it may be still in use by other threads.
> While reviewing your backport proposal, I noticed that
> worker->cp->addr was used concurrently in several places in mod_proxy
> with no particular care, so I started to code a follow up, but it
> needs that apr_pool_clear() too somewhere to avoid the leak and
> figured it may not be safe (like the above).
> I attach the patch here to show what I mean by insecure
> worker->cp->addr usage if we start to modify it concurrently, though
> more work is needed it seems..
> 
> If I am correct we need to refcount the worker->cp->addr (or rather a
> worker->address struct). I had a patch which does that to handle a
> proxy address_ttl parameter (above which address is renewed), I can
> try to revive and mix it with the attached one, in a PR. WDYT?

Thanks for the good catch. With regards to mod_proxy_ajp I think we should
use conn->addr instead of worker->cp->addr. We cannot be sure that 
worker->cp->addr is really set.
I guess it did not cause any problems so far as in the AJP case we always reuse 
connections by default
and hence worker->cp->addr is set.

I think using a refcount could be burdensome as we need to ensure correct 
increase and decrease of the refcount and
we might have 3rd party modules that do not play well. Hence I propose a copy 
approach (but thinking of it, it might
fail in the same or other ways with 3rd party modules using worker->cp->addr 
directly in a similar way like mod_proxy_ajp does today)

I modified some code from your patch especially ap_proxy_worker_addr_get:

Notes:

1. I am aware that apr_sockaddr_info_copy is only available in APR >= 1.6
2. ttlexpired is a pseudocode boolean to incorporate your idea of a TTL. 
Details would need to be worked out.
3. If *addrp != NULL the idea is that we have an acceptable apr_sockaddr_t 
provided that neither the TTL expired
   or worker->cp->addr is NULL and thus we would need to do another lookup.

PROXY_DECLARE(apr_status_t) ap_proxy_worker_addr_get(proxy_worker *worker,
                                                     apr_sockaddr_t **addrp,
                                                     const char *host,
                                                     apr_port_t port,
                                                     request_rec *r,
                                                     server_rec *s,
                                                     proxy_conn_rec *conn,  /* 
Can be NULL */
                                                     apr_pool_t *p)
{
    apr_sockaddr_t *addr = NULL;
    apr_status_t status = APR_SUCCESS;
    int addr_reusable = (worker->s->is_address_reusable
                         && !worker->s->disablereuse);
#if APR_HAS_THREADS
    int worker_locked = 0;
#endif
    apr_status_t rv;

    if (conn) {
        p = conn->pool;
        addrp = &(conn->addr);
    }

    if (addr_reusable) {
        addr = AP_VOLATILIZE_T(apr_sockaddr_t *, worker->cp->addr);

        if (addr && !ttlexpired && *addrp) {
            return APR_SUCCESS;
        }
        if (!addr || ttlexpired) {
#if APR_HAS_THREADS
            if ((rv = PROXY_THREAD_LOCK(worker))) {
                if (r) {
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO()
                                  "proxy lock");
                }
                else {
                    ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO()
                                  "proxy lock");
                }
                *addrp = NULL;
                return rv;
            }
            /* Reload under the lock (might have been resolved in the meantime) 
*/
            addr = AP_VOLATILIZE_T(apr_sockaddr_t *, worker->cp->addr);
            worker_locked = 1;
#endif
        }
    }

    *addrp = NULL;

    /* Need a DNS lookup? */
    if (!addr || ttlexpired) {
        apr_pool_t *lookup_pool;

        if (conn) {
            /*
             * If we need to do a lookup we should do a fresh connect 
afterwards.
             * Hence cleanup conn from scratch.
             */
            apr_pool_clear(p);
            conn = apr_pcalloc(p, sizeof(proxy_conn_rec));
            conn->pool = p;
            conn->worker = worker;
            apr_pool_create(&(conn->scpool), p);
            apr_pool_tag(conn->scpool, "proxy_conn_scpool");
        }
        if (addr_reusable) {
            lookup_pool = worker->cp->dns_pool;
            apr_pool_clear(lookup_pool);
        }
        else {
            lookup_pool = p;
        }
        status = apr_sockaddr_info_get(&addr, host, APR_UNSPEC, port, 0, 
lookup_pool);
        if (addr_reusable && status == APR_SUCCESS) {
            AP_VOLATILIZE_T(apr_sockaddr_t *, worker->cp->addr) = addr;
        }
    }
    if (status == APR_SUCCESS) {
        if (addr_reusable) {
            apr_sockaddr_info_copy(addrp, addr, p);
        }
        else {
            *addrp = addr;
        }
    }

#if APR_HAS_THREADS
    if (worker_locked && (rv = PROXY_THREAD_UNLOCK(worker))) {
        if (r) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO()
                          "proxy unlock");
        }
        else {
            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO()
                         "proxy unlock");
        }
        return rv;
    }
#endif

    return status;
}


In ap_proxy_determine_connection we could then call

      err = ap_proxy_worker_addr_get(worker, NULL,
                                      conn->hostname, conn->port,
                                      r, r->server, conn, NULL);

BTW: I think p was the wrong pool in your and conn->pool needs to be used 
instead.

In mod_proxy_hcheck.c we could use

ap_proxy_worker_addr_get(worker, addr,
                                  worker->s->hostname_ex, worker->s->port,
                                  NULL, ctx->s, NULL, p);


There are still some gory details to be worked out, but this would be my high 
level approach.


Regards

Rüdiger

Reply via email to