kern/60601: tmpfs size calculation overflows on 32-bit ports when memory plus swap exceeds 4GB

"hashimoto.kenichi@&#12301; via gnats" <[email protected]>
Newsgroups gmane.os.netbsd.bugs
Message-ID <[email protected]>
>Number:         60601
>Category:       kern
>Synopsis:       tmpfs size calculation overflows on 32-bit ports when memory plus swap exceeds 4GB
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Aug 16 06:45:00 +0000 2026
>Originator:     Hashimoto Kenichi
>Release:        NetBSD 10.1
>Organization:
genetec corporation
>Environment:
System: NetBSD 10.1 (GENERIC) i386, 2.9GB RAM, 32GB swap (QEMU/NVMM guest)
>Description:
In tmpfs_bytes_max() (sys/fs/tmpfs/tmpfs_mem.c rev 1.8):

	psize_t freepages = tmpfs_mem_info(false);
	uint64_t avail_mem;
	...
	avail_mem = round_page(mp->tm_bytes_used) + (freepages << PAGE_SHIFT);

The shift is evaluated in the width of psize_t before the addition.
On i386 without PAE, psize_t is unsigned long, i.e. 32 bits.
tmpfs_mem_info() returns a page count covering main memory and swap,
so once it exceeds 2^20 pages the result wraps modulo 2^32.

Two measurements, df(1) and vmstat(1) run from a single command line:

                        07:23:16        14:29:34        delta
  pages free              24,233         193,815     +169,582
  cached file pages      281,540         157,941     -123,599
  swap pages in use      512,841         515,106       +2,265
  (swap pages          8,421,126, wired 2,827, freetarg 5,461: unchanged)
  ------------------------------------------------------------
  sum                  8,205,770       8,249,488      +43,718
  df -k /tmp Size      4,186,968         148,488   -4,038,480

The input grew by 43,718 pages while the reported size fell by 4GB.
tmpfs_bytes_max() is monotonic in freepages, so this requires
truncation.  The delta matches one crossing of a 2^32 boundary to
within the error of reading swpgavail/swpgonly from vmstat.

This mount has no -s option (size_max=18446744073709551615), so MIN()
always selects the overflowed term.  A -sram%25 mount on the same
system was clamped to the same wrong value.

tmpfs_mem_incr() uses the same function, so this is not only a
reporting problem: build.sh failed with

  fatal error: error writing to /tmp//cc5cFXCC.s: No space left on device

while df reported 100M free on an idle /tmp.

An amd64 system running 11.0 reports expected values, consistent with
psize_t being 64 bits there.  PAE kernels should be unaffected
(untested).  rev 1.7 used size_t, also 32 bits on i386, so this is not
specific to the psize_t change.
>How-To-Repeat:
On a 32-bit port with main memory plus swap exceeding 4GB, mount a
tmpfs without -s and observe over time:

  { date +%T; df -k /tmp; \
    vmstat -s | grep -E 'pages free|cached file pages|swap pages'; }

The reported size does not track the page counts monotonically.
512MB RAM with 5GB swap should suffice but is unverified; the data
above is from a system in production use.

>Fix:
Unknown.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.