From dc9eb0f9a92563e66f2bb86d3cf890aa4e8695a5 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 19 Oct 2026 13:12:48 -0400 Subject: [PATCH] Reject metric timestamp offsets that overflow time.Duration Metricsviz accepts unsigned 64-bit nanosecond offsets, then converts them to time.Duration. Offsets above MaxInt64 become negative and place samples before the collection start instead of rejecting the invalid log. Limit offsets to the nonnegative time.Duration range and retain the parser's range error. Check both valid endpoints and overflowing values through the existing metrics log parser. Assisted-by: OpenAI Codex --- test/metricsviz/metricsviz.go | 3 ++- .../metricsviz_cli/metricsviz_cli_test.go | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/test/metricsviz/metricsviz.go b/test/metricsviz/metricsviz.go index 1e514567d88..2b1a81ca922 100644 --- a/test/metricsviz/metricsviz.go +++ b/test/metricsviz/metricsviz.go @@ -693,7 +693,8 @@ func Parse(logs string, hasPrefix bool) (*Data, error) { if len(tabularData) != len(header)+1 { return nil, fmt.Errorf("invalid data line: %q with %d components which does not match header which has %d components. This is likely due to a log buffer overrun or similar issue causing the line to be cut off; please configure the container or the runtime to allow higher logging volume", line, len(tabularData), len(header)) } - offsetNanos, err := strconv.ParseUint(tabularData[0], 10, 64) + // Offsets must fit in time.Duration without becoming negative. + offsetNanos, err := strconv.ParseUint(tabularData[0], 10, 63) if err != nil { return nil, fmt.Errorf("invalid data line: %q (bad timestamp: %w)", line, err) } diff --git a/test/metricsviz/metricsviz_cli/metricsviz_cli_test.go b/test/metricsviz/metricsviz_cli/metricsviz_cli_test.go index 6f04d3f315e..936ad4d330b 100644 --- a/test/metricsviz/metricsviz_cli/metricsviz_cli_test.go +++ b/test/metricsviz/metricsviz_cli/metricsviz_cli_test.go @@ -17,11 +17,14 @@ package metricsviz_cli_test import ( "context" + "errors" "fmt" + "math" "math/rand/v2" "os" "os/exec" "path" + "strconv" "strings" "testing" "time" @@ -31,6 +34,26 @@ import ( "gvisor.dev/gvisor/test/metricsviz" ) +func TestTimestampOffset(t *testing.T) { + for _, tc := range []struct { + offset uint64 + wantErr error + }{ + {offset: 0}, + {offset: math.MaxInt64}, + {offset: math.MaxInt64 + 1, wantErr: strconv.ErrRange}, + {offset: math.MaxUint64, wantErr: strconv.ErrRange}, + } { + t.Run(strconv.FormatUint(tc.offset, 10), func(t *testing.T) { + logs := fmt.Sprintf("%s/counter\t{}\n%s1\n%s\t/counter\n%d\t1\n", + metric.MetricsMetaIndicator, metric.MetricsStartTimeIndicator, metric.TimeColumn, tc.offset) + if _, err := metricsviz.Parse(logs, false); !errors.Is(err, tc.wantErr) { + t.Fatalf("Parse() error = %v, want %v", err, tc.wantErr) + } + }) + } +} + func TestMetricsvizCLI(t *testing.T) { ctx := context.Background() cliPath, err := testutil.FindFile("test/metricsviz/metricsviz_cli/metricsviz_cli")