Do Not Share Descriptor Table On FreeBSD

First of all, “threads” are fake. They exist in dreams, but not to the FreeBSD kernel. In the kernel source code, there’s something called “process leader” or p_leader. Every process has a p_leader.

Update(2024-05-20): ok, so, threads are real. They are non-standard and I’m not sure how they interact with Capsicum.

Here’s all you need to know about p_leader:

  • When a process exits, all processes having it as p_leader are sent SIGKILL.
  • When a process tries to hold an advisory lock, it will be held by its p_leader.
  • rfork(RFPROC|RFTHREAD) sets the new process’ p_leader to be the current process’ p_leader,
  • otherwise, a new process starts with itself as its p_leader.

Multiple processes can share a file descriptor table with rfork(2). I’d say this is a bad idea. You don’t want the time-at-access,time-at-use bug to appear in your program.

Writing secure applications on FreeBSD is easy.

  • use capsicum and refer to every resource as a file descriptor
  • don’t share descriptor table across processes

That’s it! Security on FreeBSD is easy :)

Caveat pid ppid pgrp->pg_id

Every process has a number. It’s called pid.

Every process has a parent process, except it doesn’t (ppid==0). The parent is tracked with a number (ppid) inside the kernel.

Every process is part of a process group (struct pgrp *p_pgrp). Every process group is part of a session (struct session *pg_session).

Process group is used for job control. See kill(2), wait(2).

Session is used for … legacy stuff. You will need to understand what “controlling terminal” is. See setlogin(2), setsid(2).

Process ID (pid and ppid), Process Group ID (p_pgrp->pg_id), Session ID (p_pgrp->pg_session->sid) are all different. You may not assume that a process’s pgrp->pg_id is the same as its pid, since your code may run after fork -> setpgrp -> execve.

In Capsicum mode, all of the above are irrelevant, since pdkill(2) can only send signal to one process at a time.