Skip to content

Commit cbd4c5f

Browse files
authored
PRW2: Fix index out-of-range panic (#7299)
* Fix index out-of-range in PRW2 handler Signed-off-by: SungJin1212 <tjdwls1201@gmail.com> * changelog Signed-off-by: SungJin1212 <tjdwls1201@gmail.com> --------- Signed-off-by: SungJin1212 <tjdwls1201@gmail.com>
1 parent ac39439 commit cbd4c5f

3 files changed

Lines changed: 74 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
* [BUGFIX] Query Scheduler: If max_outstanding_requests_per_tenant value is updated to lesser value than the current number of requests in the queue, the excess requests (newest ones) will be dropped to prevent deadlocks. #7188
5858
* [BUGFIX] Distributor: Return remote write V2 stats headers properly when the request is HA deduplicated. #7240
5959
* [BUGFIX] Cache: Fix Redis Cluster EXECABORT error in MSet by using individual SET commands instead of transactions for cluster mode. #7262
60+
* [BUGFIX] Distributor: Fix an `index out of range` panic in PRW2.0 handler caused by dirty metadata when reusing requests from `sync.Pool`. #7299
6061

6162

6263
## 1.20.1 2025-12-03

pkg/cortexpb/timeseriesv2.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ func ReuseTimeseriesV2(ts *TimeSeriesV2) {
116116
ts.LabelsRefs = ts.LabelsRefs[:0]
117117
ts.Samples = ts.Samples[:0]
118118

119+
// clear metadata
120+
ts.Metadata.Type = 0
121+
ts.Metadata.UnitRef = 0
122+
ts.Metadata.HelpRef = 0
123+
119124
// clear exemplar label refs
120125
for i := range ts.Exemplars {
121126
ts.Exemplars[i].LabelsRefs = ts.Exemplars[i].LabelsRefs[:0]

pkg/util/push/push_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,3 +924,71 @@ func createCortexWriteRequestProtobuf(t *testing.T, skipLabelNameValidation bool
924924
require.NoError(t, err)
925925
return inoutBytes
926926
}
927+
928+
// TestHandler_RemoteWriteV2_MetadataPoolReset verifies that reusing a WriteRequestV2
929+
// from the sync.Pool does not carry over omitted Metadata fields (UnitRef and HelpRef),
930+
// which would otherwise cause an index-out-of-range panic
931+
func TestHandler_RemoteWriteV2_MetadataPoolReset(t *testing.T) {
932+
var limits validation.Limits
933+
flagext.DefaultValues(&limits)
934+
overrides := validation.NewOverrides(limits, nil)
935+
936+
mockPush := func(ctx context.Context, req *cortexpb.WriteRequest) (*cortexpb.WriteResponse, error) {
937+
return &cortexpb.WriteResponse{}, nil
938+
}
939+
940+
handler := Handler(true, 1000000, overrides, nil, mockPush, nil)
941+
ctx := user.InjectOrgID(context.Background(), "user-1")
942+
943+
sendRequest := func(reqProto *writev2.Request) *httptest.ResponseRecorder {
944+
reqBytes, err := reqProto.Marshal()
945+
require.NoError(t, err)
946+
947+
req := createRequest(t, reqBytes, true).WithContext(ctx)
948+
resp := httptest.NewRecorder()
949+
950+
// Should not panic
951+
require.NotPanics(t, func() {
952+
handler.ServeHTTP(resp, req)
953+
})
954+
955+
return resp
956+
}
957+
958+
symbols := make([]string, 100)
959+
symbols[0], symbols[1], symbols[2] = "", "__name__", "test_metric"
960+
for i := 3; i < 100; i++ {
961+
symbols[i] = fmt.Sprintf("sym-%d", i)
962+
}
963+
964+
req1Proto := writev2.Request{
965+
Symbols: symbols,
966+
Timeseries: []writev2.TimeSeries{
967+
{
968+
LabelsRefs: []uint32{1, 2},
969+
Samples: []writev2.Sample{{Value: 1, Timestamp: 1000}},
970+
Metadata: writev2.Metadata{
971+
Type: writev2.Metadata_METRIC_TYPE_COUNTER,
972+
UnitRef: 99,
973+
HelpRef: 98,
974+
},
975+
},
976+
},
977+
}
978+
979+
resp1 := sendRequest(&req1Proto)
980+
require.Equal(t, http.StatusNoContent, resp1.Code)
981+
982+
req2Proto := writev2.Request{
983+
Symbols: []string{"", "__name__", "foo", "bar"},
984+
Timeseries: []writev2.TimeSeries{
985+
{
986+
LabelsRefs: []uint32{1, 2},
987+
Samples: []writev2.Sample{{Value: 2, Timestamp: 2000}},
988+
},
989+
},
990+
}
991+
992+
resp2 := sendRequest(&req2Proto)
993+
require.Equal(t, http.StatusNoContent, resp2.Code)
994+
}

0 commit comments

Comments
 (0)