prometheus / prometheus/procfs

stat.go: /proc/stat processes field overflow causes parseStat() to fail entirely

Open Beginner friendly
#831 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
883
Forks
388
Avg merge
13h 28m
Merged PRs (30d)
3

Description

Bug Report: /proc/stat processes field overflow causes entire Stat() parsing to fail

Affected Code

stat.goparseStat() function, processes case (lines ~177-180):

case parts[0] == "processes":
    if stat.ProcessCreated, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
        return Stat{}, fmt.Errorf("%w: couldn't parse %q (processes): %w", ErrFileParse, parts[1], err)
    }
Root Cause

The processes field in /proc/stat represents "number of forks since boot" and is stored as an unsigned int in the Linux kernel. On long-running systems (300+ days) with high process churn, this counter wraps around and /proc/stat reports a negative integer string:

processes -1948031333

strconv.ParseUint cannot parse negative values, causing the entire parseStat() function to return an error. This breaks ALL consumers that depend on Stat(), including:

  • node_exporter CPU collector — zero CPU metrics exposed
  • node_exporter stat collector — zero stat metrics exposed
Impact

This affects any system with >4 billion process forks since boot. In containerized environments (Kubernetes), containers start and stop frequently, accelerating the counter overflow.

Reproduction
# On a system with uptime >300 days:
cat /proc/stat | grep "^processes"
# processes -1948031333  ← negative!

# Any code calling fs.Stat() fails:
# "couldn't parse \"-1948031333\" (processes): strconv.ParseUint: parsing \"-1948031333\": invalid syntax"

Tested and confirmed failing on:

  • node_exporter v1.3.1 through v1.8.2 (all versions)
  • procfs v0.21.0 (latest as of June 2026)
  • Linux kernel 5.x, system uptime 388 days
Proposed Fix

Use strconv.ParseInt instead of strconv.ParseUint for the processes field. In Go, converting a negative int64 to uint64 produces the correct unsigned representation via two's complement:

case parts[0] == "processes":
    v, err := strconv.ParseInt(parts[1], 10, 64)
    if err != nil {
        // Don't fail the entire Stat() — skip this field gracefully
        break
    }
    stat.ProcessCreated = uint64(v)

This handles both cases:

  • Normal: ParseInt("12345")int64(12345)uint64(12345)
  • Overflowed: ParseInt("-1948031333")int64(-1948031333) → correct uint64
References
Environment
Component Version
procfs master (v0.21.0)
node_exporter v1.3.1 / v1.8.2
Linux kernel 5.x
System uptime 388 days
/proc/stat processes -1948031333

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in stat.go at parseStat(), specifically the processes case around lines 177-180, and reproduce the failure with a negative processes value such as -1948031333. Verify that Stat() no longer fails when /proc/stat reports this value and that consumers can still receive the remaining statistics.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
operating-systems
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.