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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions