Repository: prometheus/procfs
Pull request #560 (2023-08-24 10:55:51)

Fix build tags for isRealProc function
Only aix, darwin, dragonfly freebsd and linux GOOS have a Type member in their syscall.Statfs_t struct. However, since the isRealProc compares that Type to a magic number (PROC_SUPER_MAGIC, 0x9fa0) which is only relevant to Linux (and possibly FreeBSD with Linux compatibility), adjust build tags accordingly.

This drops the previous nostatfs build tag workaround, since the "tamago" GOOS for which it was originally added won't match the updated build tags.

Fixes: #554.


Patch:
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
index 433f71b..8ace97b 100644
--- a/.github/workflows/golangci-lint.yml
+++ b/.github/workflows/golangci-lint.yml
@@ -18,15 +18,15 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout repository
-        uses: actions/checkout@v3
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
       - name: install Go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
         with:
           go-version: 1.20.x
       - name: Install snmp_exporter/generator dependencies
         run: sudo apt-get update && sudo apt-get -y install libsnmp-dev
         if: github.repository == 'prometheus/snmp_exporter'
       - name: Lint
-        uses: golangci/golangci-lint-action@v3.4.0
+        uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0
         with:
-          version: v1.53.3
+          version: v1.54.2
diff --git a/Makefile.common b/Makefile.common
index 0ce7ea4..062a281 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -61,7 +61,7 @@ PROMU_URL     := https://github.com/prometheus/promu/releases/download/v$(PROMU_
 SKIP_GOLANGCI_LINT :=
 GOLANGCI_LINT :=
 GOLANGCI_LINT_OPTS ?=
-GOLANGCI_LINT_VERSION ?= v1.53.3
+GOLANGCI_LINT_VERSION ?= v1.54.2
 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64.
 # windows isn't included here because of the path separator being different.
 ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin))
diff --git a/fs_statfs_notype.go b/fs_statfs_notype.go
index 13d74e3..134767d 100644
--- a/fs_statfs_notype.go
+++ b/fs_statfs_notype.go
@@ -11,8 +11,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//go:build netbsd || openbsd || solaris || windows || nostatfs
-// +build netbsd openbsd solaris windows nostatfs
+//go:build !freebsd && !linux
+// +build !freebsd,!linux
 
 package procfs
 
diff --git a/fs_statfs_type.go b/fs_statfs_type.go
index bee1514..80df79c 100644
--- a/fs_statfs_type.go
+++ b/fs_statfs_type.go
@@ -11,8 +11,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//go:build !netbsd && !openbsd && !solaris && !windows && !nostatfs
-// +build !netbsd,!openbsd,!solaris,!windows,!nostatfs
+//go:build freebsd || linux
+// +build freebsd linux
 
 package procfs
 
diff --git a/go.mod b/go.mod
index 8d937ab..95cd65f 100644
--- a/go.mod
+++ b/go.mod
@@ -5,5 +5,5 @@ go 1.19
 require (
 	github.com/google/go-cmp v0.5.9
 	golang.org/x/sync v0.3.0
-	golang.org/x/sys v0.10.0
+	golang.org/x/sys v0.11.0
 )
diff --git a/go.sum b/go.sum
index f484097..4d2f8b9 100644
--- a/go.sum
+++ b/go.sum
@@ -2,5 +2,5 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
 golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
-golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/nfs/nfs.go b/nfs/nfs.go
index 7292102..f17fa51 100644
--- a/nfs/nfs.go
+++ b/nfs/nfs.go
@@ -201,51 +201,56 @@ type ServerV4Stats struct {
 // V4Ops models the "proc4ops" line: NFSv4 operations
 // Variable list.
 // See:
-// - v4.0 https://tools.ietf.org/html/rfc3010 (38 operations)
+// - v4.0 https://tools.ietf.org/html/rfc3010 (38/39 operations)
+//   - nfs == v2.5.x 38 field : https://elixir.bootlin.com/linux/v2.5.75/source/include/linux/nfs4.h#L52
+//   - nfs >= v2.6.x 39 field : https://elixir.bootlin.com/linux/v2.6.39.4/source/include/linux/nfs4.h#L233
+//
 // - v4.1 https://tools.ietf.org/html/rfc5661 (58 operations)
 // - v4.2 https://tools.ietf.org/html/draft-ietf-nfsv4-minorversion2-41 (71 operations)
 //
 //nolint:godot
 type V4Ops struct {
 	//Values       uint64 // Variable depending on v4.x sub-version. TODO: Will this always at least include the fields in this struct?
-	Op0Unused    uint64
-	Op1Unused    uint64
-	Op2Future    uint64
-	Access       uint64
-	Close        uint64
-	Commit       uint64
-	Create       uint64
-	DelegPurge   uint64
-	DelegReturn  uint64
-	GetAttr      uint64
-	GetFH        uint64
-	Link         uint64
-	Lock         uint64
-	Lockt        uint64
-	Locku        uint64
-	Lookup       uint64
-	LookupRoot   uint64
-	Nverify      uint64
-	Open         uint64
-	OpenAttr     uint64
-	OpenConfirm  uint64
-	OpenDgrd     uint64
-	PutFH        uint64
-	PutPubFH     uint64
-	PutRootFH    uint64
-	Read         uint64
-	ReadDir      uint64
-	ReadLink     uint64
-	Remove       uint64
-	Rename       uint64
-	Renew        uint64
-	RestoreFH    uint64
-	SaveFH       uint64
-	SecInfo      uint64
-	SetAttr      uint64
-	Verify       uint64
-	Write        uint64
-	RelLockOwner uint64
+	Op0Unused          uint64
+	Op1Unused          uint64
+	Op2Future          uint64
+	Access             uint64
+	Close              uint64
+	Commit             uint64
+	Create             uint64
+	DelegPurge         uint64
+	DelegReturn        uint64
+	GetAttr            uint64
+	GetFH              uint64
+	Link               uint64
+	Lock               uint64
+	Lockt              uint64
+	Locku              uint64
+	Lookup             uint64
+	LookupRoot         uint64
+	Nverify            uint64
+	Open               uint64
+	OpenAttr           uint64
+	OpenConfirm        uint64
+	OpenDgrd           uint64
+	PutFH              uint64
+	PutPubFH           uint64
+	PutRootFH          uint64
+	Read               uint64
+	ReadDir            uint64
+	ReadLink           uint64
+	Remove             uint64
+	Rename             uint64
+	Renew              uint64
+	RestoreFH          uint64
+	SaveFH             uint64
+	SecInfo            uint64
+	SetAttr            uint64
+	SetClientID        uint64
+	SetClientIDConfirm uint64
+	Verify             uint64
+	Write              uint64
+	RelLockOwner       uint64
 }
 
 // ClientRPCStats models all stats from /proc/net/rpc/nfs.
@@ -270,6 +275,7 @@ type ServerRPCStats struct {
 	V3Stats        V3Stats
 	ServerV4Stats  ServerV4Stats
 	V4Ops          V4Ops
+	WdelegGetattr  uint64
 }
 
 // FS represents the pseudo-filesystem proc, which provides an interface to
diff --git a/nfs/parse.go b/nfs/parse.go
index 5e78c54..76e74cb 100644
--- a/nfs/parse.go
+++ b/nfs/parse.go
@@ -272,45 +272,53 @@ func parseV4Ops(v []uint64) (V4Ops, error) {
 		return V4Ops{}, fmt.Errorf("invalid V4Ops line %q", v)
 	}
 
+	// nfs v2.5.x 39field and >=v2.6.x 40 field;
+	v40 := uint64(0)
+	if values > 39 {
+		v40 = v[40]
+	}
+
 	stats := V4Ops{
-		Op0Unused:    v[1],
-		Op1Unused:    v[2],
-		Op2Future:    v[3],
-		Access:       v[4],
-		Close:        v[5],
-		Commit:       v[6],
-		Create:       v[7],
-		DelegPurge:   v[8],
-		DelegReturn:  v[9],
-		GetAttr:      v[10],
-		GetFH:        v[11],
-		Link:         v[12],
-		Lock:         v[13],
-		Lockt:        v[14],
-		Locku:        v[15],
-		Lookup:       v[16],
-		LookupRoot:   v[17],
-		Nverify:      v[18],
-		Open:         v[19],
-		OpenAttr:     v[20],
-		OpenConfirm:  v[21],
-		OpenDgrd:     v[22],
-		PutFH:        v[23],
-		PutPubFH:     v[24],
-		PutRootFH:    v[25],
-		Read:         v[26],
-		ReadDir:      v[27],
-		ReadLink:     v[28],
-		Remove:       v[29],
-		Rename:       v[30],
-		Renew:        v[31],
-		RestoreFH:    v[32],
-		SaveFH:       v[33],
-		SecInfo:      v[34],
-		SetAttr:      v[35],
-		Verify:       v[36],
-		Write:        v[37],
-		RelLockOwner: v[38],
+		Op0Unused:          v[1],
+		Op1Unused:          v[2],
+		Op2Future:          v[3],
+		Access:             v[4],
+		Close:              v[5],
+		Commit:             v[6],
+		Create:             v[7],
+		DelegPurge:         v[8],
+		DelegReturn:        v[9],
+		GetAttr:            v[10],
+		GetFH:              v[11],
+		Link:               v[12],
+		Lock:               v[13],
+		Lockt:              v[14],
+		Locku:              v[15],
+		Lookup:             v[16],
+		LookupRoot:         v[17],
+		Nverify:            v[18],
+		Open:               v[19],
+		OpenAttr:           v[20],
+		OpenConfirm:        v[21],
+		OpenDgrd:           v[22],
+		PutFH:              v[23],
+		PutPubFH:           v[24],
+		PutRootFH:          v[25],
+		Read:               v[26],
+		ReadDir:            v[27],
+		ReadLink:           v[28],
+		Remove:             v[29],
+		Rename:             v[30],
+		Renew:              v[31],
+		RestoreFH:          v[32],
+		SaveFH:             v[33],
+		SecInfo:            v[34],
+		SetAttr:            v[35],
+		SetClientID:        v[36],
+		SetClientIDConfirm: v[37],
+		Verify:             v[38],
+		Write:              v[39],
+		RelLockOwner:       v40,
 	}
 
 	return stats, nil
diff --git a/nfs/parse_nfsd.go b/nfs/parse_nfsd.go
index 857ae3b..66828e5 100644
--- a/nfs/parse_nfsd.go
+++ b/nfs/parse_nfsd.go
@@ -73,6 +73,8 @@ func ParseServerRPCStats(r io.Reader) (*ServerRPCStats, error) {
 			stats.ServerV4Stats, err = parseServerV4Stats(values)
 		case "proc4ops":
 			stats.V4Ops, err = parseV4Ops(values)
+		case "wdeleg_getattr":
+			stats.WdelegGetattr = values[0]
 		default:
 			return nil, fmt.Errorf("unknown NFSd metric line %q", metricLine)
 		}
diff --git a/proc_maps.go b/proc_maps.go
index 727549a..7e75c28 100644
--- a/proc_maps.go
+++ b/proc_maps.go
@@ -63,17 +63,17 @@ type ProcMap struct {
 // parseDevice parses the device token of a line and converts it to a dev_t
 // (mkdev) like structure.
 func parseDevice(s string) (uint64, error) {
-	toks := strings.Split(s, ":")
-	if len(toks) < 2 {
-		return 0, fmt.Errorf("%w: unexpected number of fields, expected: 2, got: %q", ErrFileParse, len(toks))
+	i := strings.Index(s, ":")
+	if i == -1 {
+		return 0, fmt.Errorf("%w: expected separator `:` in %s", ErrFileParse, s)
 	}
 
-	major, err := strconv.ParseUint(toks[0], 16, 0)
+	major, err := strconv.ParseUint(s[0:i], 16, 0)
 	if err != nil {
 		return 0, err
 	}
 
-	minor, err := strconv.ParseUint(toks[1], 16, 0)
+	minor, err := strconv.ParseUint(s[i+1:], 16, 0)
 	if err != nil {
 		return 0, err
 	}
@@ -93,17 +93,17 @@ func parseAddress(s string) (uintptr, error) {
 
 // parseAddresses parses the start-end address.
 func parseAddresses(s string) (uintptr, uintptr, error) {
-	toks := strings.Split(s, "-")
-	if len(toks) < 2 {
-		return 0, 0, fmt.Errorf("%w: invalid address", ErrFileParse)
+	idx := strings.Index(s, "-")
+	if idx == -1 {
+		return 0, 0, fmt.Errorf("%w: expected separator `-` in %s", ErrFileParse, s)
 	}
 
-	saddr, err := parseAddress(toks[0])
+	saddr, err := parseAddress(s[0:idx])
 	if err != nil {
 		return 0, 0, err
 	}
 
-	eaddr, err := parseAddress(toks[1])
+	eaddr, err := parseAddress(s[idx+1:])
 	if err != nil {
 		return 0, 0, err
 	}
diff --git a/proc_status.go b/proc_status.go
index c055d07..3b8b3e5 100644
--- a/proc_status.go
+++ b/proc_status.go
@@ -23,7 +23,7 @@ import (
 )
 
 // ProcStatus provides status information about the process,
-// read from /proc/[pid]/stat.
+// read from /proc/[pid]/status.
 type ProcStatus struct {
 	// The process ID.
 	PID int
diff --git a/sysfs/vulnerability.go b/sysfs/vulnerability.go
index 08927a2..5a14a3e 100644
--- a/sysfs/vulnerability.go
+++ b/sysfs/vulnerability.go
@@ -17,7 +17,6 @@
 package sysfs
 
 import (
-	"fmt"
 	"os"
 	"path/filepath"
 	"strings"
@@ -27,12 +26,14 @@ const (
 	notAffected = "not affected" // based on: https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-devices-system-cpu
 	vulnerable  = "vulnerable"
 	mitigation  = "mitigation"
+	unknown     = "unknown"
 )
 
 const (
 	VulnerabilityStateNotAffected = iota
 	VulnerabilityStateVulnerable
 	VulnerabilityStateMitigation
+	VulnerabilityStateUnknown
 )
 
 var (
@@ -42,6 +43,7 @@ var (
 		VulnerabilityStateNotAffected: notAffected,
 		VulnerabilityStateVulnerable:  vulnerable,
 		VulnerabilityStateMitigation:  mitigation,
+		VulnerabilityStateUnknown:     unknown,
 	}
 )
 
@@ -98,9 +100,17 @@ func parseVulnerability(name, rawContent string) (*Vulnerability, error) {
 		if len(m) > 1 {
 			v.Mitigation = strings.Join(m[1:], " ")
 		}
+	case strings.HasPrefix(rawContentLower, unknown):
+		v.State = VulnerabilityStateUnknown
+		m := strings.Fields(rawContent)
+		if len(m) > 1 {
+			v.Mitigation = strings.Join(m[1:], " ")
+		}
 	default:
-		return nil, fmt.Errorf("unknown vulnerability state for %s: %s", name, rawContent)
-
+		// Output the raw data obtained from the vulnerability, with state
+		// unknown, rather than erroring out
+		v.State = VulnerabilityStateUnknown
+		v.Mitigation = rawContent
 	}
 	return v, nil
 }


Test patch:
diff --git a/nfs/parse_nfsd_test.go b/nfs/parse_nfsd_test.go
index b09b3b5..a7e9689 100644
--- a/nfs/parse_nfsd_test.go
+++ b/nfs/parse_nfsd_test.go
@@ -33,7 +33,7 @@ func TestNewNFSdServerRPCStats(t *testing.T) {
 			content: "invalid",
 			invalid: true,
 		}, {
-			name: "good file",
+			name: "good file, proc4ops 72",
 			content: `rc 0 6 18622
 fh 0 0 0 0 0
 io 157286400 0
@@ -45,6 +45,7 @@ proc2 18 2 69 0 0 4410 0 0 0 0 0 0 0 0 0 0 0 99 2
 proc3 22 2 112 0 2719 111 0 0 0 0 0 0 0 0 0 0 0 27 216 0 2 1 0
 proc4 2 2 10853
 proc4ops 72 0 0 0 1098 2 0 0 0 0 8179 5896 0 0 0 0 5900 0 0 2 0 2 0 9609 0 2 150 1272 0 0 0 1236 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+wdeleg_getattr 16
 `,
 			stats: &nfs.ServerRPCStats{
 				ReplyCache: nfs.ReplyCache{
@@ -134,45 +135,485 @@ proc4ops 72 0 0 0 1098 2 0 0 0 0 8179 5896 0 0 0 0 5900 0 0 2 0 2 0 9609 0 2 150
 					Compound: 10853,
 				},
 				V4Ops: nfs.V4Ops{
-					Op0Unused:    0,
-					Op1Unused:    0,
-					Op2Future:    0,
-					Access:       1098,
-					Close:        2,
-					Commit:       0,
-					Create:       0,
-					DelegPurge:   0,
-					DelegReturn:  0,
-					GetAttr:      8179,
-					GetFH:        5896,
-					Link:         0,
-					Lock:         0,
-					Lockt:        0,
-					Locku:        0,
-					Lookup:       5900,
-					LookupRoot:   0,
-					Nverify:      0,
-					Open:         2,
-					OpenAttr:     0,
-					OpenConfirm:  2,
-					OpenDgrd:     0,
-					PutFH:        9609,
-					PutPubFH:     0,
-					PutRootFH:    2,
-					Read:         150,
-					ReadDir:      1272,
-					ReadLink:     0,
-					Remove:       0,
-					Rename:       0,
-					Renew:        1236,
-					RestoreFH:    0,
-					SaveFH:       0,
-					SecInfo:      0,
-					SetAttr:      0,
-					Verify:       3,
-					Write:        3,
-					RelLockOwner: 0,
+					Op0Unused:          0,
+					Op1Unused:          0,
+					Op2Future:          0,
+					Access:             1098,
+					Close:              2,
+					Commit:             0,
+					Create:             0,
+					DelegPurge:         0,
+					DelegReturn:        0,
+					GetAttr:            8179,
+					GetFH:              5896,
+					Link:               0,
+					Lock:               0,
+					Lockt:              0,
+					Locku:              0,
+					Lookup:             5900,
+					LookupRoot:         0,
+					Nverify:            0,
+					Open:               2,
+					OpenAttr:           0,
+					OpenConfirm:        2,
+					OpenDgrd:           0,
+					PutFH:              9609,
+					PutPubFH:           0,
+					PutRootFH:          2,
+					Read:               150,
+					ReadDir:            1272,
+					ReadLink:           0,
+					Remove:             0,
+					Rename:             0,
+					Renew:              1236,
+					RestoreFH:          0,
+					SaveFH:             0,
+					SecInfo:            0,
+					SetAttr:            0,
+					SetClientID:        3,
+					SetClientIDConfirm: 3,
+					Verify:             0,
+					Write:              0,
+					RelLockOwner:       0,
 				},
+				WdelegGetattr: 16,
+			},
+		}, {
+			name: "good file, proc4ops 40",
+			content: `rc 0 25020854 19157796
+fh 276 0 0 0 0
+io 899844043 2470085989
+th 1024 0 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000
+ra 2048 4250593 118232 55926 31504 20253 13815 9875 7028 5546 3991 171551
+net 44179842 1 44179026 3092
+rpc 44177753 0 0 0 0
+proc2 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+proc3 22 747 7259760 1383711 1570520 3464939 8436 4688207 21668847 1173194 6457 2127 172 213538 1253 556401 14950 1101 56245 90790 742 367 1989658
+proc4 2 0 0
+proc4ops 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+wdeleg_getattr 9`,
+
+			stats: &nfs.ServerRPCStats{
+				ReplyCache: nfs.ReplyCache{
+					Hits:    0,
+					Misses:  25020854,
+					NoCache: 19157796,
+				},
+				FileHandles: nfs.FileHandles{
+					Stale:        276,
+					TotalLookups: 0,
+					AnonLookups:  0,
+					DirNoCache:   0,
+					NoDirNoCache: 0,
+				},
+				InputOutput: nfs.InputOutput{
+					Read:  899844043,
+					Write: 2470085989,
+				},
+				Threads: nfs.Threads{
+					Threads: 1024,
+					FullCnt: 0,
+				},
+				ReadAheadCache: nfs.ReadAheadCache{
+					CacheSize:      2048,
+					CacheHistogram: []uint64{4250593, 118232, 55926, 31504, 20253, 13815, 9875, 7028, 5546, 3991},
+					NotFound:       171551,
+				},
+				Network: nfs.Network{
+					NetCount:   44179842,
+					UDPCount:   1,
+					TCPCount:   44179026,
+					TCPConnect: 3092,
+				},
+				ServerRPC: nfs.ServerRPC{
+					RPCCount: 44177753,
+					BadCnt:   0,
+					BadFmt:   0,
+					BadAuth:  0,
+					BadcInt:  0,
+				},
+				V2Stats: nfs.V2Stats{
+					Null:     0,
+					GetAttr:  0,
+					SetAttr:  0,
+					Root:     0,
+					Lookup:   0,
+					ReadLink: 0,
+					Read:     0,
+					WrCache:  0,
+					Write:    0,
+					Create:   0,
+					Remove:   0,
+					Rename:   0,
+					Link:     0,
+					SymLink:  0,
+					MkDir:    0,
+					RmDir:    0,
+					ReadDir:  0,
+					FsStat:   0,
+				},
+				V3Stats: nfs.V3Stats{
+					Null:        747,
+					GetAttr:     7259760,
+					SetAttr:     1383711,
+					Lookup:      1570520,
+					Access:      3464939,
+					ReadLink:    8436,
+					Read:        4688207,
+					Write:       21668847,
+					Create:      1173194,
+					MkDir:       6457,
+					SymLink:     2127,
+					MkNod:       172,
+					Remove:      213538,
+					RmDir:       1253,
+					Rename:      556401,
+					Link:        14950,
+					ReadDir:     1101,
+					ReadDirPlus: 56245,
+					FsStat:      90790,
+					FsInfo:      742,
+					PathConf:    367,
+					Commit:      1989658,
+				},
+				ServerV4Stats: nfs.ServerV4Stats{
+					Null:     0,
+					Compound: 0,
+				},
+				V4Ops: nfs.V4Ops{
+					Op0Unused:          0,
+					Op1Unused:          0,
+					Op2Future:          0,
+					Access:             0,
+					Close:              0,
+					Commit:             0,
+					Create:             0,
+					DelegPurge:         0,
+					DelegReturn:        0,
+					GetAttr:            0,
+					GetFH:              0,
+					Link:               0,
+					Lock:               0,
+					Lockt:              0,
+					Locku:              0,
+					Lookup:             0,
+					LookupRoot:         0,
+					Nverify:            0,
+					Open:               0,
+					OpenAttr:           0,
+					OpenConfirm:        0,
+					OpenDgrd:           0,
+					PutFH:              0,
+					PutPubFH:           0,
+					PutRootFH:          0,
+					Read:               0,
+					ReadDir:            0,
+					ReadLink:           0,
+					Remove:             0,
+					Rename:             0,
+					Renew:              0,
+					RestoreFH:          0,
+					SaveFH:             0,
+					SecInfo:            0,
+					SetAttr:            0,
+					SetClientID:        0,
+					SetClientIDConfirm: 0,
+					Verify:             0,
+					Write:              0,
+					RelLockOwner:       0,
+				},
+				WdelegGetattr: 9,
+			},
+		},
+		{
+			name: "good file, proc4ops 59",
+			content: `rc 0 268 742119
+fh 0 0 0 0 0
+io 2476981939 0
+th 8 0 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000
+ra 32 30104 0 0 0 0 0 0 0 0 0 71174
+net 742701 314 742393 10103960
+rpc 742406 310 310 0 0
+proc2 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+proc3 22 105 71158 0 175642 184711 17103 101277 0 0 0 0 0 0 0 0 0 0 8916 102 202 0 0
+proc4 2 101 182991
+proc4ops 59 0 0 0 18112 8341 0 0 0 3239 71595 11834 0 0 0 0 107097 0 0 8344 0 5100 0 181968 0 235 5735 4406 0 0 0 652 8342 8344 0 0 134 134 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+wdeleg_getattr 10`,
+			stats: &nfs.ServerRPCStats{
+				ReplyCache: nfs.ReplyCache{
+					Hits:    0,
+					Misses:  268,
+					NoCache: 742119,
+				},
+				FileHandles: nfs.FileHandles{
+					Stale:        0,
+					TotalLookups: 0,
+					AnonLookups:  0,
+					DirNoCache:   0,
+					NoDirNoCache: 0,
+				},
+				InputOutput: nfs.InputOutput{
+					Read:  2476981939,
+					Write: 0,
+				},
+				Threads: nfs.Threads{
+					Threads: 8,
+					FullCnt: 0,
+				},
+				ReadAheadCache: nfs.ReadAheadCache{
+					CacheSize:      32,
+					CacheHistogram: []uint64{30104, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+					NotFound:       71174,
+				},
+				Network: nfs.Network{
+					NetCount:   742701,
+					UDPCount:   314,
+					TCPCount:   742393,
+					TCPConnect: 10103960,
+				},
+				ServerRPC: nfs.ServerRPC{
+					RPCCount: 742406,
+					BadCnt:   310,
+					BadFmt:   310,
+					BadAuth:  0,
+					BadcInt:  0,
+				},
+				V2Stats: nfs.V2Stats{
+					Null:     0,
+					GetAttr:  0,
+					SetAttr:  0,
+					Root:     0,
+					Lookup:   0,
+					ReadLink: 0,
+					Read:     0,
+					WrCache:  0,
+					Write:    0,
+					Create:   0,
+					Remove:   0,
+					Rename:   0,
+					Link:     0,
+					SymLink:  0,
+					MkDir:    0,
+					RmDir:    0,
+					ReadDir:  0,
+					FsStat:   0,
+				},
+				V3Stats: nfs.V3Stats{
+					Null:        105,
+					GetAttr:     71158,
+					SetAttr:     0,
+					Lookup:      175642,
+					Access:      184711,
+					ReadLink:    17103,
+					Read:        101277,
+					Write:       0,
+					Create:      0,
+					MkDir:       0,
+					SymLink:     0,
+					MkNod:       0,
+					Remove:      0,
+					RmDir:       0,
+					Rename:      0,
+					Link:        0,
+					ReadDir:     0,
+					ReadDirPlus: 8916,
+					FsStat:      102,
+					FsInfo:      202,
+					PathConf:    0,
+					Commit:      0,
+				},
+				ServerV4Stats: nfs.ServerV4Stats{
+					Null:     101,
+					Compound: 182991,
+				},
+				V4Ops: nfs.V4Ops{
+					Op0Unused:          0,
+					Op1Unused:          0,
+					Op2Future:          0,
+					Access:             18112,
+					Close:              8341,
+					Commit:             0,
+					Create:             0,
+					DelegPurge:         0,
+					DelegReturn:        3239,
+					GetAttr:            71595,
+					GetFH:              11834,
+					Link:               0,
+					Lock:               0,
+					Lockt:              0,
+					Locku:              0,
+					Lookup:             107097,
+					LookupRoot:         0,
+					Nverify:            0,
+					Open:               8344,
+					OpenAttr:           0,
+					OpenConfirm:        5100,
+					OpenDgrd:           0,
+					PutFH:              181968,
+					PutPubFH:           0,
+					PutRootFH:          235,
+					Read:               5735,
+					ReadDir:            4406,
+					ReadLink:           0,
+					Remove:             0,
+					Rename:             0,
+					Renew:              652,
+					RestoreFH:          8342,
+					SaveFH:             8344,
+					SecInfo:            0,
+					SetAttr:            0,
+					SetClientID:        134,
+					SetClientIDConfirm: 134,
+					Verify:             0,
+					Write:              0,
+					RelLockOwner:       0,
+				},
+				WdelegGetattr: 10,
+			},
+		}, {
+			name: "good file, proc4ops 39",
+			content: `rc 0 25020854 19157796
+fh 276 0 0 0 0
+io 899844043 2470085989
+th 1024 0 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000
+ra 2048 4250593 118232 55926 31504 20253 13815 9875 7028 5546 3991 171551
+net 44179842 1 44179026 3092
+rpc 44177753 0 0 0 0
+proc2 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+proc3 22 747 7259760 1383711 1570520 3464939 8436 4688207 21668847 1173194 6457 2127 172 213538 1253 556401 14950 1101 56245 90790 742 367 1989658
+proc4 2 0 0
+proc4ops 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 39
+wdeleg_getattr 765432`,
+			stats: &nfs.ServerRPCStats{
+				ReplyCache: nfs.ReplyCache{
+					Hits:    0,
+					Misses:  25020854,
+					NoCache: 19157796,
+				},
+				FileHandles: nfs.FileHandles{
+					Stale:        276,
+					TotalLookups: 0,
+					AnonLookups:  0,
+					DirNoCache:   0,
+					NoDirNoCache: 0,
+				},
+				InputOutput: nfs.InputOutput{
+					Read:  899844043,
+					Write: 2470085989,
+				},
+				Threads: nfs.Threads{
+					Threads: 1024,
+					FullCnt: 0,
+				},
+				ReadAheadCache: nfs.ReadAheadCache{
+					CacheSize:      2048,
+					CacheHistogram: []uint64{4250593, 118232, 55926, 31504, 20253, 13815, 9875, 7028, 5546, 3991},
+					NotFound:       171551,
+				},
+				Network: nfs.Network{
+					NetCount:   44179842,
+					UDPCount:   1,
+					TCPCount:   44179026,
+					TCPConnect: 3092,
+				},
+				ServerRPC: nfs.ServerRPC{
+					RPCCount: 44177753,
+					BadCnt:   0,
+					BadFmt:   0,
+					BadAuth:  0,
+					BadcInt:  0,
+				},
+				V2Stats: nfs.V2Stats{
+					Null:     0,
+					GetAttr:  0,
+					SetAttr:  0,
+					Root:     0,
+					Lookup:   0,
+					ReadLink: 0,
+					Read:     0,
+					WrCache:  0,
+					Write:    0,
+					Create:   0,
+					Remove:   0,
+					Rename:   0,
+					Link:     0,
+					SymLink:  0,
+					MkDir:    0,
+					RmDir:    0,
+					ReadDir:  0,
+					FsStat:   0,
+				},
+				V3Stats: nfs.V3Stats{
+					Null:        747,
+					GetAttr:     7259760,
+					SetAttr:     1383711,
+					Lookup:      1570520,
+					Access:      3464939,
+					ReadLink:    8436,
+					Read:        4688207,
+					Write:       21668847,
+					Create:      1173194,
+					MkDir:       6457,
+					SymLink:     2127,
+					MkNod:       172,
+					Remove:      213538,
+					RmDir:       1253,
+					Rename:      556401,
+					Link:        14950,
+					ReadDir:     1101,
+					ReadDirPlus: 56245,
+					FsStat:      90790,
+					FsInfo:      742,
+					PathConf:    367,
+					Commit:      1989658,
+				},
+				ServerV4Stats: nfs.ServerV4Stats{
+					Null:     0,
+					Compound: 0,
+				},
+				V4Ops: nfs.V4Ops{
+					Op0Unused:          0,
+					Op1Unused:          0,
+					Op2Future:          0,
+					Access:             0,
+					Close:              0,
+					Commit:             0,
+					Create:             0,
+					DelegPurge:         0,
+					DelegReturn:        0,
+					GetAttr:            0,
+					GetFH:              0,
+					Link:               0,
+					Lock:               0,
+					Lockt:              0,
+					Locku:              0,
+					Lookup:             0,
+					LookupRoot:         0,
+					Nverify:            0,
+					Open:               0,
+					OpenAttr:           0,
+					OpenConfirm:        0,
+					OpenDgrd:           0,
+					PutFH:              0,
+					PutPubFH:           0,
+					PutRootFH:          0,
+					Read:               0,
+					ReadDir:            0,
+					ReadLink:           0,
+					Remove:             0,
+					Rename:             0,
+					Renew:              0,
+					RestoreFH:          0,
+					SaveFH:             0,
+					SecInfo:            0,
+					SetAttr:            0,
+					SetClientID:        0,
+					SetClientIDConfirm: 0,
+					Verify:             0,
+					Write:              39,
+					RelLockOwner:       0,
+				},
+				WdelegGetattr: 765432,
 			},
 		},
 	}
diff --git a/proc_maps64_test.go b/proc_maps64_test.go
index 66ef134..44ec68c 100644
--- a/proc_maps64_test.go
+++ b/proc_maps64_test.go
@@ -138,3 +138,40 @@ func TestProcMaps(t *testing.T) {
 	}
 
 }
+
+var start, end uintptr
+
+func BenchmarkParseAddress(b *testing.B) {
+	b.ReportAllocs()
+	var (
+		s, e uintptr
+		err  error
+	)
+	for i := 0; i < b.N; i++ {
+		s, e, err = parseAddresses("7f7d7469e000-7f7d746a0000")
+		if err != nil {
+			b.Fatal(err)
+		}
+	}
+	// Prevent the compiler from optimizing away benchmark code.
+	start = s
+	end = e
+}
+
+var device uint64
+
+func BenchmarkParseDevice(b *testing.B) {
+	b.ReportAllocs()
+	var (
+		d   uint64
+		err error
+	)
+	for i := 0; i < b.N; i++ {
+		d, err = parseDevice("00:22")
+		if err != nil {
+			b.Fatal(err)
+		}
+	}
+	// Prevent the compiler from optimizing away benchmark code.
+	device = d
+}
diff --git a/sysfs/vulnerability_test.go b/sysfs/vulnerability_test.go
index 255586d..ec9cafc 100644
--- a/sysfs/vulnerability_test.go
+++ b/sysfs/vulnerability_test.go
@@ -38,14 +38,15 @@ func TestFS_CPUVulnerabilities(t *testing.T) {
 		want              *Vulnerability
 		wantErr           bool
 	}{
-		{"Not affected", "itlb_multihit", &Vulnerability{CodeName: "itlb_multihit", State: VulnerabilityStateNotAffected, Mitigation: ""}, false},
-		{"Not affected with underscores", "tsx_async_abort", &Vulnerability{CodeName: "tsx_async_abort", State: VulnerabilityStateNotAffected, Mitigation: ""}, false},
+		{"Not affected", "tsx_async_abort", &Vulnerability{CodeName: "tsx_async_abort", State: VulnerabilityStateNotAffected, Mitigation: ""}, false},
 		{"Mitigation simple string", "spec_store_bypass", &Vulnerability{CodeName: "spec_store_bypass", State: VulnerabilityStateMitigation, Mitigation: "Speculative Store Bypass disabled via prctl"}, false},
 		{"Mitigation special chars", "retbleed", &Vulnerability{CodeName: "retbleed", State: VulnerabilityStateMitigation, Mitigation: "untrained return thunk; SMT enabled with STIBP protection"}, false},
 		{"Mitigation more special chars", "spectre_v1", &Vulnerability{CodeName: "spectre_v1", State: VulnerabilityStateMitigation, Mitigation: "usercopy/swapgs barriers and __user pointer sanitization"}, false},
 		{"Mitigation with multiple subsections", "spectre_v2", &Vulnerability{CodeName: "spectre_v2", State: VulnerabilityStateMitigation, Mitigation: "Retpolines, IBPB: conditional, STIBP: always-on, RSB filling, PBRSB-eIBRS: Not affected"}, false},
 		{"Vulnerable", "mds", &Vulnerability{CodeName: "mds", State: VulnerabilityStateVulnerable, Mitigation: ""}, false},
 		{"Vulnerable with mitigation available", "mmio_stale_data", &Vulnerability{CodeName: "mmio_stale_data", State: VulnerabilityStateVulnerable, Mitigation: "Clear CPU buffers attempted, no microcode"}, false},
+		{"Unknown", "srbds", &Vulnerability{CodeName: "srbds", State: VulnerabilityStateUnknown, Mitigation: "Dependent on hypervisor status"}, false},
+		{"Unknown with unparseable mitigation", "itlb_multihit", &Vulnerability{CodeName: "itlb_multihit", State: VulnerabilityStateUnknown, Mitigation: "KVM: Mitigation: VMX unsupported"}, false},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/testdata/fixtures.ttar b/testdata/fixtures.ttar
index ae2e0f0..39ec946 100644
--- a/testdata/fixtures.ttar
+++ b/testdata/fixtures.ttar
@@ -13239,7 +13239,7 @@ Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/sys/devices/system/cpu/vulnerabilities/itlb_multihit
 Lines: 1
-Not affected
+KVM: Mitigation: VMX unsupported
 Mode: 444
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/sys/devices/system/cpu/vulnerabilities/mds
@@ -13272,6 +13272,11 @@ Lines: 1
 Mitigation: Retpolines, IBPB: conditional, STIBP: always-on, RSB filling, PBRSB-eIBRS: Not affected
 Mode: 444
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/vulnerabilities/srbds
+Lines: 1
+Unknown: Dependent on hypervisor status
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/sys/devices/system/cpu/vulnerabilities/tsx_async_abort
 Lines: 1
 Not affected