From b65a4102eea8f359300d09921a2f07be63ec22d8 Mon Sep 17 00:00:00 2001 From: Yanhu007 Date: Wed, 15 Apr 2026 14:36:12 +0800 Subject: [PATCH] fix: prevent slice aliasing in LabelValues.With LabelValues.With used append directly, which reuses the underlying array when capacity is available. This caused sibling With calls to overwrite each other's label values: base := counter.With("a", "1") c1 := base.With("b", "2") // has ["a","1","b","2"] c2 := base.With("c", "3") // overwrites c1 to ["a","1","c","3"] Fix by allocating a new slice in With to prevent aliasing. Add test to verify sibling With calls don't corrupt each other. Fixes #1296 --- metrics/internal/lv/labelvalues.go | 7 ++++++- metrics/internal/lv/labelvalues_test.go | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/metrics/internal/lv/labelvalues.go b/metrics/internal/lv/labelvalues.go index 8bb1ba094..d33cda444 100644 --- a/metrics/internal/lv/labelvalues.go +++ b/metrics/internal/lv/labelvalues.go @@ -10,5 +10,10 @@ func (lvs LabelValues) With(labelValues ...string) LabelValues { if len(labelValues)%2 != 0 { labelValues = append(labelValues, "unknown") } - return append(lvs, labelValues...) + // Create a new slice to avoid aliasing the underlying array. + // Without this, sibling With calls (e.g., c1.With("a","1") and + // c1.With("b","2")) can overwrite each other's label values. + result := make(LabelValues, len(lvs), len(lvs)+len(labelValues)) + copy(result, lvs) + return append(result, labelValues...) } diff --git a/metrics/internal/lv/labelvalues_test.go b/metrics/internal/lv/labelvalues_test.go index 5e72609a9..f29d37c44 100644 --- a/metrics/internal/lv/labelvalues_test.go +++ b/metrics/internal/lv/labelvalues_test.go @@ -20,3 +20,17 @@ func TestWith(t *testing.T) { t.Errorf("With does not appear to return the right thing: want %q, have %q", want, have) } } + +func TestWithNoAliasing(t *testing.T) { + base := LabelValues{"a", "1", "b", "2"} + c1 := base.With("c", "3", "d", "4") + c2 := base.With("e", "5") + + // c2 should not have overwritten c1's values + if c1[4] != "c" || c1[5] != "3" { + t.Errorf("c1 was corrupted by sibling With call: got %v", c1) + } + if c2[4] != "e" || c2[5] != "5" { + t.Errorf("c2 has wrong values: got %v", c2) + } +}