#1128397 linux-image-6.18.10+deb14-amd64: open(/proc/$pid/maps) is empty after $pid exec()s, unless you read a partial line from the fd before, in which case it has the rest of the line only

#1128397#5
Date:
2026-02-19 08:47:14 UTC
From:
To:
Dear Maintainer,

I reproduce this on 6.18.10+deb14-amd64 (current nightly d-i),
6.1.0-35-amd64, and 6.7.7-amd64:

	$ cat a.c
	int main() {
			close(0);
			open("/proc/self/maps", 0);
			char    buf[20];
			read(0, buf, sizeof(buf));
			execlp("cat", "cat", (void *)0);
	}
	$ cc a.c
	$ ./a.out
	b3000 r--p 00000000 00:8a 816985                     /home/nabijaczleweli/uwu/otp/a.out
	$ sed -e 's:read://&:' -e s/cat/wc/ a.c > b.c
	$ cc b.c
	$ ./a.out
	0 0 0

You can reproduce this with bash with  wc < /proc/self/maps,
which yields 0 0 0 because bash does fork -> open -> exec.

dash does open -> fork -> exec, and it shows dash's maps.

Arguably bash is more correct, except Linux doesn't implement the file
in a useful way (doesn't implement it correctly(?)).

For non-/self/ I tried:
	1$ echo $$
	1727476
	1$ exec sleep inf   # after the sleep below

	2$ (sleep 10; wc) < /proc/1727476/maps
	0 0 0
and
	1$ echo $$
	1730151
	1$ exec sleep inf   # after the sleep below

	2$ (head -c 32; sleep 10; wc) < /proc/1730151/maps
    55cf54298000-55cf542c7000 r--p 01 4 55
which replicates this directly.

(read()ing a /proc/$pid/maps fd after $pid does exist returns ESRCH,
 which is as-expected I think.)

Best,

#1128397#18
Date:
2026-02-25 19:23:22 UTC
From:
To:
Opening /proc/$pid/maps gives you a representation of the current memory
space of $pid.  execve() tears down that memory space and creates a new
memory space.  The open file is left referring to the old memory space,
which is now empty (but won't be freed as long as the file is open).  I
think it's correct that no more mappings are visible through the open
file at this point.

What are you proposing would be the correct behaviour?

Ben.

#1128397#25
Date:
2026-02-25 22:29:23 UTC
From:
To:
I'll be honest with you, I don't really know.

Locking in the memory map on read() instead of on open() seems at least
as bad if not worse.

For read -> exec -> read, the second read yielding the partial
"b3000 r--p 00000000 00:8a 816985                     /home/nabijaczleweli/uwu/otp/a.out\n"
isn't pretty, but the alternative is that you get your line truncated
for no reason if your buffer falls mid-line,
which does now sound worse than keeping that as-is.

Of course, the seek behaviour seems obviously wrong as well:
  lseek(0, 200, SET) reads empty,
  lseek(0, 220, SET) reads the first line skip 20 characters,
  lseek(0, 201, SET) reads the first line skip 1 character,
  lseek(0, 110, SET) reads the first line skip 10 characters:
  (yes, the hundred digit seems meaningless here? 6.1.0-35-amd64)
  int main() {
  	close(0);
  	open("/proc/self/maps", 0);
  	lseek(0, (long)110, 0);
  	execlp("cat", "cat", (void *)0);
  }
but that seems tangential.

Best,