BUG/MEDIUM: servers: Use a refcount for port_range and free it properly

port_range was never freed. That used not to be a problem, but now that
we can dynamically add and remove servers, it becomes one, as that leads
to a memory leak each time a server with a "source" directive is destroyed.
However, just adding a free() is not enough. We have to add a refcount,
because the server is not the only one with a reference to it. We may
also have one in fdinfo, so that we know which port to release when we
finally close the fd.
So add a refcount, and make sure to call port_range_release() when a
server is destroyed.

This should be backported up to 3.0.
This commit is contained in:
Olivier Houchard 2026-07-01 13:55:48 +02:00 committed by Olivier Houchard
parent af9bb8507d
commit f59da779ca
3 changed files with 16 additions and 0 deletions

View file

@ -27,6 +27,7 @@
struct port_range {
int size, get, put_h, put_t; /* range size, and get/put positions */
int refcount;
uint16_t ports[VAR_ARRAY]; /* array of <size> ports, in host byte order */
};

View file

@ -28,6 +28,17 @@
#define GET_NEXT_OFF(range, off) ((off) == (range)->size - 1 ? 0 : (off) + 1)
static inline void port_range_release(struct port_range *range)
{
if (range) {
int refcount = _HA_ATOMIC_SUB_FETCH(&range->refcount, 1);
BUG_ON(refcount < 0);
if (refcount == 0)
free(range);
}
}
/* return an available port from range <range>, or zero if none is left */
static inline int port_range_alloc_port(struct port_range *range)
{
@ -44,6 +55,7 @@ static inline int port_range_alloc_port(struct port_range *range)
return 0;
ret = range->ports[get];
} while (!(_HA_ATOMIC_CAS(&range->get, &get, GET_NEXT_OFF(range, get))));
_HA_ATOMIC_INC(&range->refcount);
return ret;
}
@ -77,6 +89,7 @@ static inline void port_range_release_port(struct port_range *range, int port)
* can use that port.
*/
_HA_ATOMIC_STORE(&range->put_t, GET_NEXT_OFF(range, put));
port_range_release(range);
}
/* return a new initialized port range of N ports. The ports are not
@ -90,6 +103,7 @@ static inline struct port_range *port_range_alloc_range(int n)
if (!ret)
return NULL;
ret->size = n + 1;
ret->refcount = 1;
/* Start at the first free element */
ret->put_h = ret->put_t = n;
return ret;

View file

@ -3245,6 +3245,7 @@ void srv_free_params(struct server *srv)
ha_free(&srv_tlv->fmt_string);
ha_free(&srv_tlv);
}
port_range_release(srv->conn_src.sport_range);
}
/* Deallocate a server <srv> and its member. <srv> must be allocated. For