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.
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 */
return 0;
continue;
}
+ store_metric:
stats[current_field] = metric;
if (selected_field != NULL)
break;
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:
return 0;
continue;
}
+ store_metric:
stats[current_field] = metric;
if (selected_field != NULL)
break;
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:
return 0;
continue;
}
+ store_metric:
stats[current_field] = metric;
if (selected_field != NULL)
break;
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:
return 0;
continue;
}
+ store_metric:
stats[current_field] = metric;
if (selected_field != NULL)
break;
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:
return 0;
continue;
}
+ store_metric:
stats[current_field] = metric;
if (selected_field != NULL)
break;
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:
return 0;
continue;
}
+ store_metric:
stats[current_field] = metric;
if (selected_field != NULL)
break;