let's look and see which files one of my processes has open. the process has pid 18255, and it's owned by me:
$ ps p 18255
PID TTY STAT TIME COMMAND
18255 pts/10 S+ 0:00 python3 test.py 100000 3
we can see it has four files open:
$ ll /proc/18255/fd/
lrwx------ 64 ash ash 20 Jun 20:26 0
lrwx------ 64 ash ash 20 Jun 20:26 1
lrwx------ 64 ash ash 20 Jun 20:26 2
lr-x------ 64 ash ash 20 Jun 20:26 3
...wait, don't those filenames normally have the symlink target after them?
$ realpath /proc/18255/fd/3
realpath: /proc/18255/fd/3: Permission denied
weird. ok, let's check the fdinfo files:
1 $ ll /proc/18255/fdinfo/
.r-------- 0 ash ash 20 Jun 20:26 0
.r-------- 0 ash ash 20 Jun 20:26 1
.r-------- 0 ash ash 20 Jun 20:26 2
.r-------- 0 ash ash 20 Jun 20:26 3
and...
$ cat /proc/18255/fdinfo/3
pos: 3
flags: 02100000
mnt_id: 29
...what?? we can read these ones just fine!
so we're in the very weird situation of not being able to see which files our process has open, but knowing what offset it has them all open at. if we look in /proc/18255/mountinfo, we can even see which filesystem the files are located on!
why on earth has this happened? i'm not sure of the details, but my
program was not in fact run entirely normally -- it has a bunch of
capabilities (specifically, the cap_dac_read_search
ambient
capability), and i guess the kernel tries to stop you messing with
processes with more capabilities than you have, even if the process is
owned by you.
if you want to try this yourself, i used this command (you'll need a
pretty recent version of capsh in order for --addamb
to work):
sudo capsh --caps="cap_dac_read_search+eip cap_setpcap,cap_setuid,cap_setgid+ep" --keep=1 --user=ash --addamb="cap_dac_read_search+eip" -- -c "python3 test.py 100000 3"
incidentally, this is very similar to the command i use to run my backups; it gives the command read access to all files on the system, without giving it the full powers of uid 0.
here's the python file i used:
import sys
import time
with open(__file__) as f:
f.seek(int(sys.argv[2]))
time.sleep(int(sys.argv[1]))