Skip to content

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

Description

@jsczrangel

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions