lib/netutil: fix IPv6 address corruption in proxy protocol v2 parser

Proxy protocol parser kept sub-slice reference for pooled bytesBuffer at readProxyProto
```
 bb := bbPool.Get()
 defer bbPool.Put(bb)   // ← buffer returned to pool AFTER function returns
...
   IP:   bb.B[0:16],  // ← BUG: sub-slice of pooled buffer!
...
 ```

 This commit properly allocates new slice for ipv6 address and copies buffer content to it.

 Fixes https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10839
This commit is contained in:
andriibeee
2026-04-20 13:11:04 +03:00
committed by f41gh7
parent a6fafa8387
commit b9fc9dc706
3 changed files with 26 additions and 1 deletions

View File

@@ -30,6 +30,7 @@ See also [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-rel
* FEATURE: [alerts](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/rules): add new `MetricNameStatsCacheUtilizationIsTooHigh` alerting rule to track overutilization of [Metric names usage stats tracker](https://docs.victoriametrics.com/victoriametrics/#track-ingested-metrics-usage) (used in [Cardinality Explorer](https://docs.victoriametrics.com/victoriametrics/#cardinality-explorer)). See [#10840](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/10840).
* FEATURE: [stream aggregation](https://docs.victoriametrics.com/victoriametrics/stream-aggregation/): add `vm_streamaggr_counter_resets_total` metric for `total*`, `increase*` and `rate*` outputs that is useful for aggregation behaviour tracking. These metrics help to identify issues described in [Troubleshooting: counter resets](https://docs.victoriametrics.com/victoriametrics/stream-aggregation/#counter-resets).
* BUGFIX: all VictoriaMetrics components: properly parse IPv6 source address when accepting connections with proxy protocol v2 enabled. See [#10839](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10839). Thanks to @andriibeee for the contribution.
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): `-maxScrapeSize` is now correctly applied when reading response bodies, including non-OK scrape error responses. See [#10804](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/10804).
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): fix `ec2_sd_configs` returning 401 `AuthFailure` from AWS when credentials are obtained via IRSA, instance role or `AWS_CONTAINER_CREDENTIALS_*` env vars. The regression was introduced in [v1.140.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.140.0). See [#10815](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10815).
* BUGFIX: [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/): fix leak of backend TCP connections, file descriptors and goroutines when the client cancels the request after the backend response has been received. See [#10833](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10833). Thanks to @andriibeee for the contribution.

View File

@@ -128,8 +128,10 @@ func readProxyProto(r io.Reader) (net.Addr, error) {
if len(bb.B) < 36 {
return nil, fmt.Errorf("cannot read ipv6 address from proxy protocol block with the length %d bytes; expected at least 36 bytes", len(bb.B))
}
var ipv6Addr net.IP
ipv6Addr = append(ipv6Addr, bb.B[:16]...)
remoteAddr := &net.TCPAddr{
IP: bb.B[0:16],
IP: ipv6Addr,
Port: int(binary.BigEndian.Uint16(bb.B[32:34])),
}
return remoteAddr, nil

View File

@@ -107,6 +107,28 @@ func TestParseProxyProtocolFail(t *testing.T) {
0, 80, 0, 0})
}
func TestParseProxyProtocolIPv6DoesNotAliasPool(t *testing.T) {
header := func(last byte) *bytes.Buffer {
return bytes.NewBuffer([]byte{
0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, 0x21, 0x21, 0x00, 0x24,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, last,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 80, 0, 0,
})
}
got, err := readProxyProto(header(1))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if _, err := readProxyProto(header(2)); err != nil {
t.Fatalf("unexpected error: %s", err)
}
want := &net.TCPAddr{IP: net.ParseIP("::1"), Port: 80}
if !reflect.DeepEqual(got, want) {
t.Fatalf("first addr mutated by pool reuse; got %v, want %v", got, want)
}
}
func TestProxyProtocolConnReadWriteSuccessful(t *testing.T) {
server, client := net.Pipe()
defer server.Close()