Repository: prometheus/procfs
Pull request #250 (2019-12-19 10:16:31)

Fix the overflow while parsing ipv6 addr inside net_udp.go
Hey @pgier, Hey @discordianfish, Hey @SuperQ 

Currently inside the net_udp.go while parsing the local_addr and remote_addr fields based on ipv6, uint64 is not enough. So, I decided to use []uint8 instead.

Example output running the node_exporter using this lib:

```
ERROR: udp_queues collector failed after 0.001350s: couldn't get upd6 queued bytes:
cannot parse local_address value in udp socket line: strconv.ParseUint: parsing "000080FE00000000FFADE15609667CFE": value out of range  source="collector.go:132"
```

Signed-off-by: Peter Bueschel <peter.bueschel@logmein.com>

discordianfish: Oh good catch!
peterbueschel: Hi @discordianfish, @pgier, @SuperQ can someone please hit the merge button?

Patch:
diff --git a/fixtures.ttar b/fixtures.ttar
index eeb3d00..a21ccad 100644
--- a/fixtures.ttar
+++ b/fixtures.ttar
@@ -1527,6 +1527,11 @@ blocksize    : 16
 min keysize  : 16
 max keysize  : 32
 
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/loadavg
+Lines: 1
+0.02 0.04 0.05 1/497 11947
 Mode: 444
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/proc/diskstats
@@ -1828,18 +1833,18 @@ Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/proc/net/udp
 Lines: 4
-  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     
-   0: 00000000:0016 00000000:0000 0A 00000000:00000001 00:00000000 00000000     0        0 2740 1 ffff88003d3af3c0 100 0 0 10 0                      
-   1: 00000000:0016 00000000:0000 0A 00000001:00000000 00:00000000 00000000     0        0 2740 1 ffff88003d3af3c0 100 0 0 10 0                      
-   2: 00000000:0016 00000000:0000 0A 00000001:00000001 00:00000000 00000000     0        0 2740 1 ffff88003d3af3c0 100 0 0 10 0                      
+  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
+   0: 0A000005:0016 00000000:0000 0A 00000000:00000001 00:00000000 00000000     0        0 2740 1 ffff88003d3af3c0 100 0 0 10 0
+   1: 00000000:0016 00000000:0000 0A 00000001:00000000 00:00000000 00000000     0        0 2740 1 ffff88003d3af3c0 100 0 0 10 0
+   2: 00000000:0016 00000000:0000 0A 00000001:00000001 00:00000000 00000000     0        0 2740 1 ffff88003d3af3c0 100 0 0 10 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/proc/net/udp6
 Lines: 3
   sl  local_address                         remote_address                        st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode ref pointer drops
  1315: 00000000000000000000000000000000:14EB 00000000000000000000000000000000:0000 07 00000000:00000000 00:00000000 00000000   981        0 21040 2 0000000013726323 0
- 6073: 00000000000000000000000000000000:C781 00000000000000000000000000000000:0000 07 00000000:00000000 00:00000000 00000000  1000        0 11337031 2 00000000b9256fdd 0
-Mode: 444
+ 6073: 000080FE00000000FFADE15609667CFE:C781 00000000000000000000000000000000:0000 07 00000000:00000000 00:00000000 00000000  1000        0 11337031 2 00000000b9256fdd 0
+Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/proc/net/udp_broken
 Lines: 2
diff --git a/loadavg.go b/loadavg.go
new file mode 100644
index 0000000..00bbe14
--- /dev/null
+++ b/loadavg.go
@@ -0,0 +1,62 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/util"
+)
+
+// LoadAvg represents an entry in /proc/loadavg
+type LoadAvg struct {
+	Load1  float64
+	Load5  float64
+	Load15 float64
+}
+
+// LoadAvg returns loadavg from /proc.
+func (fs FS) LoadAvg() (*LoadAvg, error) {
+	path := fs.proc.Path("loadavg")
+
+	data, err := util.ReadFileNoStat(path)
+	if err != nil {
+		return nil, err
+	}
+	return parseLoad(data)
+}
+
+// Parse /proc loadavg and return 1m, 5m and 15m.
+func parseLoad(loadavgBytes []byte) (*LoadAvg, error) {
+	loads := make([]float64, 3)
+	parts := strings.Fields(string(loadavgBytes))
+	if len(parts) < 3 {
+		return nil, fmt.Errorf("malformed loadavg line: too few fields in loadavg string: %s", string(loadavgBytes))
+	}
+
+	var err error
+	for i, load := range parts[0:3] {
+		loads[i], err = strconv.ParseFloat(load, 64)
+		if err != nil {
+			return nil, fmt.Errorf("could not parse load '%s': %s", load, err)
+		}
+	}
+	return &LoadAvg{
+		Load1:  loads[0],
+		Load5:  loads[1],
+		Load15: loads[2],
+	}, nil
+}
diff --git a/net_udp.go b/net_udp.go
index 1799119..d017e3f 100644
--- a/net_udp.go
+++ b/net_udp.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright 2020 The Prometheus Authors
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
@@ -15,8 +15,10 @@ package procfs
 
 import (
 	"bufio"
+	"encoding/hex"
 	"fmt"
 	"io"
+	"net"
 	"os"
 	"strconv"
 	"strings"
@@ -54,9 +56,9 @@ type (
 	// For the proc file format details, see https://linux.die.net/man/5/proc.
 	netUDPLine struct {
 		Sl        uint64
-		LocalAddr uint64
+		LocalAddr net.IP
 		LocalPort uint64
-		RemAddr   uint64
+		RemAddr   net.IP
 		RemPort   uint64
 		St        uint64
 		TxQueue   uint64
@@ -172,7 +174,7 @@ func parseNetUDPLine(fields []string) (*netUDPLine, error) {
 		return nil, fmt.Errorf(
 			"cannot parse local_address field in udp socket line: %s", fields[1])
 	}
-	if line.LocalAddr, err = strconv.ParseUint(l[0], 16, 64); err != nil {
+	if line.LocalAddr, err = hex.DecodeString(l[0]); err != nil {
 		return nil, fmt.Errorf(
 			"cannot parse local_address value in udp socket line: %s", err)
 	}
@@ -187,7 +189,7 @@ func parseNetUDPLine(fields []string) (*netUDPLine, error) {
 		return nil, fmt.Errorf(
 			"cannot parse rem_address field in udp socket line: %s", fields[1])
 	}
-	if line.RemAddr, err = strconv.ParseUint(r[0], 16, 64); err != nil {
+	if line.RemAddr, err = hex.DecodeString(r[0]); err != nil {
 		return nil, fmt.Errorf(
 			"cannot parse rem_address value in udp socket line: %s", err)
 	}


Test patch:
diff --git a/loadavg_test.go b/loadavg_test.go
new file mode 100644
index 0000000..924ba0c
--- /dev/null
+++ b/loadavg_test.go
@@ -0,0 +1,93 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestLoadAvg(t *testing.T) {
+	fs, err := NewFS(procTestFixtures)
+	if err != nil {
+		t.Fatalf("failed to open procfs: %v", err)
+	}
+
+	loadavg, err := fs.LoadAvg()
+	if err != nil {
+		t.Fatalf("failed to get loadavg: %v", err)
+	}
+
+	if diff := cmp.Diff(0.02, loadavg.Load1); diff != "" {
+		t.Fatalf("unexpected LoadAvg Per a minute:\n%s", diff)
+	}
+	if diff := cmp.Diff(0.04, loadavg.Load5); diff != "" {
+		t.Fatalf("unexpected LoadAvg Per five minutes:\n%s", diff)
+	}
+	if diff := cmp.Diff(0.05, loadavg.Load15); diff != "" {
+		t.Fatalf("unexpected LoadAvg Per fifteen minutes:\n%s", diff)
+	}
+}
+
+func Test_parseLoad(t *testing.T) {
+	tests := []struct {
+		name    string
+		s       string
+		ok      bool
+		loadavg *LoadAvg
+	}{
+		{
+			name: "empty",
+			ok:   false,
+		},
+		{
+			name: "not enough fields",
+			s:    `0.00 0.03`,
+			ok:   false,
+		},
+		{
+			name: "invalid line",
+			s:    `malformed line`,
+			ok:   false,
+		},
+		{
+			name:    "valid line",
+			s:       `0.00 0.03 0.05 1/502 33634`,
+			ok:      true,
+			loadavg: &LoadAvg{Load1: 0, Load5: 0.03, Load15: 0.05},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			loadavg, err := parseLoad([]byte(tt.s))
+			if err != nil {
+				if tt.ok {
+					t.Fatalf("failed to parse loadavg: %v", err)
+				}
+
+				t.Logf("OK error: %v", err)
+				return
+			}
+			if !tt.ok {
+				t.Fatal("expected an error, but none occurred")
+			}
+
+			if diff := cmp.Diff(tt.loadavg, loadavg); diff != "" {
+				t.Errorf("unexpected loadavg(-want +got):\n%s", diff)
+			}
+		})
+	}
+}
diff --git a/net_udp_test.go b/net_udp_test.go
index 563530c..363fdb2 100644
--- a/net_udp_test.go
+++ b/net_udp_test.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Prometheus Authors
+// Copyright 2020 The Prometheus Authors
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
@@ -14,6 +14,7 @@
 package procfs
 
 import (
+	"net"
 	"reflect"
 	"testing"
 )
@@ -30,9 +31,9 @@ func Test_parseNetUDPLine(t *testing.T) {
 			fields: []string{"11:", "00000000:0000", "00000000:0000", "0A", "00000017:0000002A", "0:0", "0", "1000"},
 			want: &netUDPLine{
 				Sl:        11,
-				LocalAddr: 0,
+				LocalAddr: net.IP{0, 0, 0, 0},
 				LocalPort: 0,
-				RemAddr:   0,
+				RemAddr:   net.IP{0, 0, 0, 0},
 				RemPort:   0,
 				St:        10,
 				TxQueue:   23,
@@ -99,19 +100,8 @@ func Test_parseNetUDPLine(t *testing.T) {
 			if tt.want == nil && got != nil {
 				t.Errorf("parseNetUDPLine() = %v, want %v", got, tt.want)
 			}
-			if got != nil {
-				if (got.Sl != tt.want.Sl) ||
-					(got.LocalAddr != tt.want.LocalAddr) ||
-					(got.LocalPort != tt.want.LocalPort) ||
-					(got.RemAddr != tt.want.RemAddr) ||
-					(got.RemPort != tt.want.RemPort) ||
-					(got.St != tt.want.St) ||
-					(got.TxQueue != tt.want.TxQueue) ||
-					(got.RxQueue != tt.want.RxQueue) ||
-					(got.UID != tt.want.UID) {
-
-					t.Errorf("parseNetUDPLine() = %#v, want %#v", got, tt.want)
-				}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("parseNetUDPLine() = %#v, want %#v", got, tt.want)
 			}
 		})
 	}
@@ -129,13 +119,37 @@ func Test_newNetUDP(t *testing.T) {
 			file: "fixtures/proc/net/udp",
 			want: []*netUDPLine{
 				&netUDPLine{
-					Sl: 0, LocalAddr: 0, LocalPort: 22, RemAddr: 0, RemPort: 0, St: 10, TxQueue: 0, RxQueue: 1, UID: 0,
+					Sl:        0,
+					LocalAddr: net.IP{10, 0, 0, 5},
+					LocalPort: 22,
+					RemAddr:   net.IP{0, 0, 0, 0},
+					RemPort:   0,
+					St:        10,
+					TxQueue:   0,
+					RxQueue:   1,
+					UID:       0,
 				},
 				&netUDPLine{
-					Sl: 1, LocalAddr: 0, LocalPort: 22, RemAddr: 0, RemPort: 0, St: 10, TxQueue: 1, RxQueue: 0, UID: 0,
+					Sl:        1,
+					LocalAddr: net.IP{0, 0, 0, 0},
+					LocalPort: 22,
+					RemAddr:   net.IP{0, 0, 0, 0},
+					RemPort:   0,
+					St:        10,
+					TxQueue:   1,
+					RxQueue:   0,
+					UID:       0,
 				},
 				&netUDPLine{
-					Sl: 2, LocalAddr: 0, LocalPort: 22, RemAddr: 0, RemPort: 0, St: 10, TxQueue: 1, RxQueue: 1, UID: 0,
+					Sl:        2,
+					LocalAddr: net.IP{0, 0, 0, 0},
+					LocalPort: 22,
+					RemAddr:   net.IP{0, 0, 0, 0},
+					RemPort:   0,
+					St:        10,
+					TxQueue:   1,
+					RxQueue:   1,
+					UID:       0,
 				},
 			},
 			wantErr: false,
@@ -145,10 +159,26 @@ func Test_newNetUDP(t *testing.T) {
 			file: "fixtures/proc/net/udp6",
 			want: []*netUDPLine{
 				&netUDPLine{
-					Sl: 1315, LocalAddr: 0, LocalPort: 5355, RemAddr: 0, RemPort: 0, St: 7, TxQueue: 0, RxQueue: 0, UID: 981,
+					Sl:        1315,
+					LocalAddr: net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+					LocalPort: 5355,
+					RemAddr:   net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+					RemPort:   0,
+					St:        7,
+					TxQueue:   0,
+					RxQueue:   0,
+					UID:       981,
 				},
 				&netUDPLine{
-					Sl: 6073, LocalAddr: 0, LocalPort: 51073, RemAddr: 0, RemPort: 0, St: 7, TxQueue: 0, RxQueue: 0, UID: 1000,
+					Sl:        6073,
+					LocalAddr: net.IP{0, 0, 128, 254, 0, 0, 0, 0, 255, 173, 225, 86, 9, 102, 124, 254},
+					LocalPort: 51073,
+					RemAddr:   net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+					RemPort:   0,
+					St:        7,
+					TxQueue:   0,
+					RxQueue:   0,
+					UID:       1000,
 				},
 			},
 			wantErr: false,