Settings

Theme

Toward race-free process signaling

lwn.net

23 points by pandalicious 7 years ago · 3 comments

Reader

theamk 7 years ago

> it is done using a new system call named procfd_signal(). This system call operates on a file descriptor of a process; the previous discussions convinced Brauner that this is a solution preferred over an ioctl().

This is quite a pity! I do a lot of Python and Bash programming, and regular file write will be simple and easily supported.

ioctl would require some magic constants and arch dependence, but would still be relatively safe - there are no dangerous ioctl operators you can do on a open /proc directory entry, and ioctl has sane error reporting.

the "new syscall" path is the worst of them all -- I am literally two characters away from invoking all sorts of dangerous commands like "kill all processes", "shutdown machine" and so on.

  • cyphar 7 years ago

    The problem with an ioctl is that ioctls are not namespaced and so a buggy userspace program might end up being tricked into killing a process (if the ioctl overlaps).

    ioctl also doesn't have exceptionally sane error reporting, especially when we're comparing it to syscalls.

    In addition, this is the first step toward more generic procfds and for some other use cases ioctls are simply insufficient and it would be strange to have a mismash of both APIs.

    > I am literally two characters away from invoking all sorts of dangerous commands like "kill all processes", "shutdown machine" and so on.

    Can you explain what you mean? Syscalls are the least bad solution because we are guaranteed no reasonably buggy program will have used the syscall already.

    Writing to a procfile would be significantly worse than either, but ioctls still have problems.

    • theamk 7 years ago

      > The problem with an ioctl is that ioctls are not namespaced and so a buggy userspace program might end up being tricked into killing a process (if the ioctl overlaps).

      I am not sure what is the threat model here -- it If we have a buggy userspace program which can be tricked into opening arbitrary files by an un-authorized user, it is already game over. What you do after is irrelevant -- the writes would damage the system, reads would leak info. The fact that they won't be able to kill processes does not help much if they could overwrite /sbin/init instead. As Tycho Andersen said in [0], "It seems completely theoretical to me."

      > ioctl also doesn't have exceptionally sane error reporting, especially when we're comparing it to syscalls.

      I could be wrong here, but I thought that most ioctl's do not generally cause the program to crash, they just fail with meaningful error code. On the other hand, syscalls will cause the process to segfault.

      >> I am literally two characters away from invoking all sorts of dangerous commands like "kill all processes", "shutdown machine" and so on.

      > Can you explain what you mean?

      Sure, let's say this goes in, and I want to use it ASAP. Libc takes ages to update, so I have no wrappers. I am using Python. What are my options?

      Option 1: File

           f = open('/proc/%d/kill' % pid, 'w')
           ...
           f.write("9")
           f.close()
      
      What kind of mistakes I can make? I can mistype '/proc/%d/kill' format string, I can write() the wrong thing.

      What is the worst that could happen? I will get nice exception (like "File not found" during open or "Invalid operation") during close. In the the worst case, I write to "/proc/%d/mem" and damage the process instead, but this not very likely -- this typo is easy to spot. And it will only affect the process I want killed anyway.

      Summary: Please give me that option!

      Option 2: ioctl

           PSKILLCTL = (0x80046A01 if sys.bits == 32 else 0x80046E01)
           f = open('/proc/%d' % pid, 'w')
           ...
           fcntl.ioctl(f, PSKILLCTL, 9)
           f.close()
      
      What kind of mistakes I can make? I can get the constant value wrong, I can get ioctl args wrong (maybe it needs pointer to integer)

      What is the worst that could happen? ioctl will succeed but will do the wrong thing, i.e. not kill the process. Luckily, there are very few ioctls which /proc/%d supports, and none of them will have permanent effect. Yeah, maybe this will switch to non-blocking mode or we will get aio enabled on the handle -- who cares, we will close it right away.

      Summary: meh, finding ioctl codes are annoying with all the macros but I can live with this.

      Option 3: syscall

           __NR_KILL_VIA_PID = (382 if sys.bits == 32 else 731)
           libc = ctypes.CDLL(None)
           f = open('/proc/%d' % pid, 'w')
           ...
           libc.syscall(__NR_KILL_VIA_PID, f.fileno(), 9)
           f.close()
      
      What kind of mistakes I can make? I can get the constant value wrong, I can get syscall args wrong (maybe it needs a struct)

      What is the worst that could happen? I reboot machine (sys_shutdown). I kill all processes on this tty (sys_vhangup). I mess up my entire process in a subtle way (sys_vfork, sys_modify_ldt). I break many other processes on the machine (sys_sethostname).

      Summary: I really want to make sure I get this right. I feel this is like shaving with straight edge -- don't make mistakes.

      Hopefully, I have explained my point. Note that this is from POV of python programmer -- I know that from the C land, it all looks different. But if we have Python-only packages, or a shell scripts, adding a new C program is a significant burden.

      Could you elaborate why do you think writing to procfile will be worse? Especially if we already have writable /proc/%d/mem.

      [0] https://lwn.net/ml/linux-kernel/20181119223954.GA4992@cisco/

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection