Input validation / Bounds checking
Description
The commit adds strict validation for time-series limits to prevent out-of-range values that could lead to data corruption during ingestion. Specifically, maxLabelsPerTimeseries, maxLabelNameLen, and maxLabelValueLen must be within the inclusive range [1, 65535]. The patch introduces a MustInit function that validates these inputs and logs a fatal error if they are out of range, then initializes the limits. This addresses a potential data integrity risk from invalid configuration inputs, by failing fast instead of proceeding with corrupted/unsupported limits.
Proof of Concept
Proof of Concept (reproducible steps):
1) Build VictoriaMetrics with this commit and run vminsert.
2) Start vminsert with an invalid limit, e.g. -maxLabelNameLen=70000 (or -maxLabelNameLen=0).
3) Observe startup failure and exit with a fatal log similar to:
incorrect limit: "maxLabelNameLen" value: 70000, must be in range 1..65535
This demonstrates that the process cannot start with out-of-range limits, preventing potential data corruption due to invalid lengths. As an alternative, you can reproduce via a small Go snippet calling MustInit(70000, 100, 100), which will trigger the same fatal path in a test environment.
Commit Details
Author: Nikolay
Date: 2026-07-02 14:41 UTC
Message:
lib/timeserieslimits: properly check range for limit values
Limit cannot be 0 or greater then 65535, because it may corrupt
ingested data. Historically VictoriaMetrics supported only 65535 bytes
len for label name and value.
See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11128
Triage Assessment
Vulnerability Type: Input validation
Confidence: HIGH
Reasoning:
The patch adds validation to ensure limit values (maxLabelsPerTimeseries, maxLabelNameLen, maxLabelValueLen) are within 1..65535, preventing values that could corrupt ingested data. This is an input validation fix with potential security/data integrity implications (e.g., avoiding out-of-range conditions that could be exploited).
Verification Assessment
Vulnerability Type: Input validation / Bounds checking
Confidence: HIGH
Affected Versions: 1.139.0 and earlier (pre-fix).
Code Diff
diff --git a/app/vminsert/main.go b/app/vminsert/main.go
index dbb7b748b2b30..e86909f3ac892 100644
--- a/app/vminsert/main.go
+++ b/app/vminsert/main.go
@@ -68,9 +68,11 @@ var (
"at -opentsdbHTTPListenAddr . See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt")
configAuthKey = flagutil.NewPassword("configAuthKey", "Authorization key for accessing /config page. It must be passed via authKey query arg. It overrides -httpAuth.*")
reloadAuthKey = flagutil.NewPassword("reloadAuthKey", "Auth key for /-/reload http endpoint. It must be passed via authKey query arg. It overrides httpAuth.* settings.")
- maxLabelsPerTimeseries = flag.Int("maxLabelsPerTimeseries", 40, "The maximum number of labels per time series to be accepted. Series with superfluous labels are ignored. In this case the vm_rows_ignored_total{reason=\"too_many_labels\"} metric at /metrics page is incremented")
- maxLabelNameLen = flag.Int("maxLabelNameLen", 256, "The maximum length of label name in the accepted time series. Series with longer label name are ignored. In this case the vm_rows_ignored_total{reason=\"too_long_label_name\"} metric at /metrics page is incremented")
- maxLabelValueLen = flag.Int("maxLabelValueLen", 4*1024, "The maximum length of label values in the accepted time series. Series with longer label value are ignored. In this case the vm_rows_ignored_total{reason=\"too_long_label_value\"} metric at /metrics page is incremented")
+ maxLabelsPerTimeseries = flag.Int("maxLabelsPerTimeseries", 40, "The maximum number of labels per time series to be accepted. Series with superfluous labels are ignored. In this case the vm_rows_ignored_total{reason=\"too_many_labels\"} metric at /metrics page is incremented.")
+ maxLabelNameLen = flag.Int("maxLabelNameLen", 256, "The maximum length of label name in the accepted time series. Series with longer label name are ignored. In this case the vm_rows_ignored_total{reason=\"too_long_label_name\"} metric at /metrics page is incremented. "+
+ "Value must be in range 1..65535.")
+ maxLabelValueLen = flag.Int("maxLabelValueLen", 4*1024, "The maximum length of label values in the accepted time series. Series with longer label value are ignored. In this case the vm_rows_ignored_total{reason=\"too_long_label_value\"} metric at /metrics page is incremented. "+
+ "Value must be in range 1..65535.")
)
var (
@@ -106,7 +108,7 @@ func Init() {
promscrape.Init(func(_ *auth.Token, wr *prompb.WriteRequest) {
prompush.Push(wr)
})
- timeserieslimits.Init(*maxLabelsPerTimeseries, *maxLabelNameLen, *maxLabelValueLen)
+ timeserieslimits.MustInit(*maxLabelsPerTimeseries, *maxLabelNameLen, *maxLabelValueLen)
}
// Stop stops vminsert.
diff --git a/lib/timeserieslimits/timeseries_limits.go b/lib/timeserieslimits/timeseries_limits.go
index 921191ae1a541..432adda01ba35 100644
--- a/lib/timeserieslimits/timeseries_limits.go
+++ b/lib/timeserieslimits/timeseries_limits.go
@@ -30,6 +30,18 @@ var (
maxLabelsPerTimeseries = 40
)
+// MustInit checks if limits are with-in supported range and prepares package for usage
+func MustInit(inputMaxLabelsPerTimeseries, inputMaxLabelNameLen, inputMaxLabelValueLen int) {
+ mustBeInRange := func(name string, limit int) {
+ if limit <= 0 || limit > math.MaxUint16 {
+ logger.Fatalf("incorrect limit: %q value: %d, must be in range 1..%d", name, limit, math.MaxUint16)
+ }
+ }
+ mustBeInRange("maxLabelNameLen", inputMaxLabelNameLen)
+ mustBeInRange("maxLabelValueLen", inputMaxLabelValueLen)
+ Init(inputMaxLabelsPerTimeseries, inputMaxLabelNameLen, inputMaxLabelValueLen)
+}
+
// Init prepares package for usage
func Init(inputMaxLabelsPerTimeseries, inputMaxLabelNameLen, inputMaxLabelValueLen int) {
maxLabelsPerTimeseries = inputMaxLabelsPerTimeseries