CLEANUP: counters: only retrieve zeroes for unallocated extra_counters

Since version 2.4 with commit 7f8f6cb926 ("BUG/MEDIUM: stats: prevent
crash if counters not alloc with dummy one") we can afford to always
update extra_counters because we know they're always either allocated
or linked to a dedicated trash. However, the ->fill_stats() callbacks
continue to access such values, making it technically possible to
retrieve random counters from this trash, which is not really clean.
Let's implement an explicit test in the ->fill_stats() functions to
only return 0 for the metric when not allocated like this. It's much
cleaner because it guarantees that we're returning an empty counter
in this case rather than random values.

The situation currently happens for dummy servers like the ones used
in Lua proxies as well as those used by rings (e.g. used for logging
or traces). Normally, none of the objects retrieved via stats or
Prometheus is concerned by this unallocated extra_counters situation,
so this is more about a cleanup than a real fix.
This commit is contained in:
Willy Tarreau 2026-02-25 14:24:33 +01:00
parent 95a9f472d2
commit 8dd22a62a4
6 changed files with 42 additions and 6 deletions

View file

@ -132,11 +132,16 @@ static struct h3_counters {
static int h3_fill_stats(struct stats_module *mod, struct extra_counters *ctr,
struct field *stats, unsigned int *selected_field)
{
struct h3_counters *counters = EXTRA_COUNTERS_GET(ctr, mod);
unsigned int current_field = (selected_field != NULL ? *selected_field : 0);
for (; current_field < H3_STATS_COUNT; current_field++) {
struct field metric = { 0 };
struct h3_counters *counters;
if (!ctr)
goto store_metric;
counters = EXTRA_COUNTERS_GET(ctr, mod);
switch (current_field) {
/* h3 frame type counters */
@ -233,6 +238,7 @@ static int h3_fill_stats(struct stats_module *mod, struct extra_counters *ctr,
return 0;
continue;
}
store_metric:
stats[current_field] = metric;
if (selected_field != NULL)
break;

View file

@ -275,11 +275,16 @@ static struct h1_counters {
static int h1_fill_stats(struct stats_module *mod, struct extra_counters *ctr,
struct field *stats, unsigned int *selected_field)
{
struct h1_counters *counters = EXTRA_COUNTERS_GET(ctr, mod);
unsigned int current_field = (selected_field != NULL ? *selected_field : 0);
for (; current_field < H1_STATS_COUNT; current_field++) {
struct field metric = { 0 };
struct h1_counters *counters;
if (!ctr)
goto store_metric;
counters = EXTRA_COUNTERS_GET(ctr, mod);
switch (current_field) {
case H1_ST_OPEN_CONN:
@ -316,6 +321,7 @@ static int h1_fill_stats(struct stats_module *mod, struct extra_counters *ctr,
return 0;
continue;
}
store_metric:
stats[current_field] = metric;
if (selected_field != NULL)
break;

View file

@ -374,11 +374,16 @@ static struct h2_counters {
static int h2_fill_stats(struct stats_module *mod, struct extra_counters *ctr,
struct field *stats, unsigned int *selected_field)
{
struct h2_counters *counters = EXTRA_COUNTERS_GET(ctr, mod);
unsigned int current_field = (selected_field != NULL ? *selected_field : 0);
for (; current_field < H2_STATS_COUNT; current_field++) {
struct field metric = { 0 };
struct h2_counters *counters;
if (!ctr)
goto store_metric;
counters = EXTRA_COUNTERS_GET(ctr, mod);
switch (current_field) {
case H2_ST_HEADERS_RCVD:
@ -428,6 +433,7 @@ static int h2_fill_stats(struct stats_module *mod, struct extra_counters *ctr,
return 0;
continue;
}
store_metric:
stats[current_field] = metric;
if (selected_field != NULL)
break;

View file

@ -95,11 +95,16 @@ struct quic_counters quic_counters;
static int quic_fill_stats(struct stats_module *mod, struct extra_counters *ctr,
struct field *stats, unsigned int *selected_field)
{
struct quic_counters *counters = EXTRA_COUNTERS_GET(ctr, mod);
unsigned int current_field = (selected_field != NULL ? *selected_field : 0);
for (; current_field < QUIC_STATS_COUNT; current_field++) {
struct field metric = { 0 };
struct quic_counters *counters;
if (!ctr)
goto store_metric;
counters = EXTRA_COUNTERS_GET(ctr, mod);
switch (current_field) {
case QUIC_ST_RXBUF_FULL:
@ -239,6 +244,7 @@ static int quic_fill_stats(struct stats_module *mod, struct extra_counters *ctr,
return 0;
continue;
}
store_metric:
stats[current_field] = metric;
if (selected_field != NULL)
break;

View file

@ -125,11 +125,16 @@ static struct dns_counters dns_counters;
static int resolv_fill_stats(struct stats_module *mod, struct extra_counters *ctr,
struct field *stats, unsigned int *selected_field)
{
struct dns_counters *counters = EXTRA_COUNTERS_GET(ctr, mod);
unsigned int current_field = (selected_field != NULL ? *selected_field : 0);
for (; current_field < RSLV_STAT_END; current_field++) {
struct field metric = { 0 };
struct dns_counters *counters;
if (!ctr)
goto store_metric;
counters = EXTRA_COUNTERS_GET(ctr, mod);
switch (current_field) {
case RSLV_STAT_ID:
@ -191,6 +196,7 @@ static int resolv_fill_stats(struct stats_module *mod, struct extra_counters *ct
return 0;
continue;
}
store_metric:
stats[current_field] = metric;
if (selected_field != NULL)
break;

View file

@ -197,11 +197,16 @@ static struct ssl_counters ssl_counters;
static int ssl_fill_stats(struct stats_module *mod, struct extra_counters *ctr,
struct field *stats, unsigned int *selected_field)
{
struct ssl_counters *counters = EXTRA_COUNTERS_GET(ctr, mod);
unsigned int current_field = (selected_field != NULL ? *selected_field : 0);
for (; current_field < SSL_ST_STATS_COUNT; current_field++) {
struct field metric = { 0 };
struct ssl_counters *counters;
if (!ctr)
goto store_metric;
counters = EXTRA_COUNTERS_GET(ctr, mod);
switch (current_field) {
case SSL_ST_SESS:
@ -228,6 +233,7 @@ static int ssl_fill_stats(struct stats_module *mod, struct extra_counters *ctr,
return 0;
continue;
}
store_metric:
stats[current_field] = metric;
if (selected_field != NULL)
break;