ParcelFileDescritor.createPipe(), aka pipe(2), and security ParcelFileDescritor.createPipe(), aka pipe(2), and security unix unix

ParcelFileDescritor.createPipe(), aka pipe(2), and security


The Linux kernel keeps track of one file descriptor table (struct fdtable) for each process (more or less). The entries in this table are indexed by small integers —starting with 0, 1, 2, etc., new entries are given the smallest available integer— and each points to one open file (struct file).

A file in the Linux kernel is a handle to an inode (struct inode) and some state (such as seek position).

If you open the same file multiple times, you will have multiple entries in the file descriptor table, each pointing to different file structures, each pointing to the same inode structure.

If you open a file, and then dup the file descriptor, you will have multiple entries in the file descriptor table, each pointing to the same file structure.

Creating a pipe results in two file descriptors: the read end and the write end. They're somewhat magical: reading from the first file descriptor will return data that was written into the second file descriptor. At the time of creation, both ends of the pipe are only accessible to this process.

Passing a file descriptor to another process (which is normally done by sendmsg over an AF_UNIX domain socket with auxiliary SCM_RIGHTS attached, but on Android is done by Binder.transact with a Parcel.writeFileDescriptor) results in a new entry being added to the receiving process's file descriptor table, pointing at the same file structure as the original entry in the sending process's file descriptor table. NB: The integer index for the same file in the two processes is not related; in fact, it is likely to be different.

Normally in C, you would use fopen to obtain a FILE * structure that you can fread/fwrite/etc. on. The C runtime library does this by opening a file descriptor and wrapping it a structure (that holds additional buffering, etc.). fdopen takes a file descriptor that's already open in the local process, and a FILE * structure around it.

Putting the pieces together:

No other process can open a file by guessing the FD number, because those numbers only have meaning within a single process.* Passing a file descriptor between processes is secure, mediated by the kernel which is manipulating objects that only the kernel has access to.

*Given appropriate privileges, you can go through the /proc/$PID/fd/$FD pseudo-filesystem to find other processes' file descriptors and re-open them for yourself. However, "appropriate privileges" is "same user or root". On Android, all applications run as different users, and none run as root – this is impossible. Additionally, Android's SELinux policy prevents applications from interacting with the /proc interfaces anyway.