ParcelFileDescritor.createPipe(), aka pipe(2), and security
android, android-binder, parcel, pipe, unix
Solution
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.
Problem
Note that while I'm asking this in the context of Android, it's more of a general unix question w/ regard to pipe(2) ... To transfer large amounts of data from one process to another, one can use `ParcelFileDescritor.createPipe()`, then send the read end of the pipe to another process via binder. `ParcelFileDescritor.createPipe()` maps directly to the unix pipe(2) system call. While the FD was transferred securely over binder to the other process, since ultimately the FD is just an int, is it possible that it could be discovered, or even guessed by a malicious process, opened, and read from? From my reading, it seems like this boils down to security via obscurity. As long as you don't know, and can't guess the FD int value, it's fine. Anonymous pipes don't expose a way to otherwise discover the FD. But it seems theoretically possible that someone could write an app with a large number of threads that continually tries to open ints based on a random int value, maybe exploiting some pattern in which the numbers are chosen and eventually exploit pipe(2).