From 7860a0d54a1e636605db7707d53681f1393235bc Mon Sep 17 00:00:00 2001 From: Maxython Date: Mon, 3 Feb 2025 20:12:04 +0300 Subject: [PATCH 1/2] shmem: improvements The possibility of remote interaction with shmem is implemented, i.e. the shmem socket is multifunctional and can not only send data, but also start an action and update shmem data. This allows you to implement shmem actions close to the original system V shared memory from Linux. For example: - Delete shmem if the process is not native - Count the number of attached and process ids - Filter shmget actions according to flags (i.e. shmget now understands the IPC_CREAT and IPC_EXCL flags) Also added new tests to check all these functionalities. --- .gitignore | 1 + shmem.c | 468 +++++++++++++++++---- test/Makefile | 6 +- test/interactions-shm/Makefile | 74 ++++ test/interactions-shm/check-attachments.sh | 18 + test/interactions-shm/endless-attachment.c | 13 + test/interactions-shm/host-available.sh | 6 + test/interactions-shm/host.c | 11 + test/interactions-shm/read.c | 15 + test/interactions-shm/remove.c | 11 + test/interactions-shm/status.c | 12 + test/interactions-shm/write.c | 14 + test/utils.h | 2 + 13 files changed, 565 insertions(+), 86 deletions(-) create mode 100644 test/interactions-shm/Makefile create mode 100755 test/interactions-shm/check-attachments.sh create mode 100644 test/interactions-shm/endless-attachment.c create mode 100755 test/interactions-shm/host-available.sh create mode 100644 test/interactions-shm/host.c create mode 100644 test/interactions-shm/read.c create mode 100644 test/interactions-shm/remove.c create mode 100644 test/interactions-shm/status.c create mode 100644 test/interactions-shm/write.c diff --git a/.gitignore b/.gitignore index 1930e81..c0fed70 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.so tags test/test-* +test/interactions-shm/test-* diff --git a/shmem.c b/shmem.c index 2f32095..649cd1f 100644 --- a/shmem.c +++ b/shmem.c @@ -22,6 +22,13 @@ #define ANDROID_SHMEM_SOCKNAME "/dev/shm/%08x" #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) +// Action numbers +#define ASHV_RM 0 +#define ASHV_GET 1 +#define ASHV_AT 2 +#define ASHV_DT 3 +#define ASHV_UPD 4 + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; typedef struct { @@ -33,6 +40,8 @@ typedef struct { size_t size; bool markedForDeletion; key_t key; + int countAttach; + pid_t *attachedPids; } shmem_t; static shmem_t* shmem = NULL; @@ -43,7 +52,7 @@ static size_t shmem_amount = 0; static int ashv_local_socket_id = 0; // To handle forks we store which pid the ashv_local_socket_id was // created for. -static int ashv_pid_setup = 0; +static pid_t ashv_pid_setup = 0; static pthread_t ashv_listening_thread_id = 0; static int ancil_send_fd(int sock, int fd) @@ -177,12 +186,82 @@ static int ashv_socket_id_from_shmid(int shmid) static int ashv_find_local_index(int shmid) { - for (size_t i = 0; i < shmem_amount; i++) - if (shmem[i].id == shmid) + for (size_t i = 0; i < shmem_amount; i++) { + if (shmem[i].id == shmid) { return i; + } + } return -1; } +static int ashv_write_pids(int sendsock, int idx) +{ + if (shmem[idx].countAttach > 0) { + pid_t pids[shmem[idx].countAttach]; + for (int i=0; i 0) { + pid_t pids[shmem[idx].countAttach]; + if (read(recvsock, &pids, sizeof(pid_t)*shmem[idx].countAttach) != sizeof(pid_t)*shmem[idx].countAttach) { + DBG("%s: ERROR: read pids failed: %s", __PRETTY_FUNCTION__, strerror(errno)); + return -1; + } + shmem[idx].attachedPids = NULL; + shmem[idx].attachedPids = realloc(shmem[idx].attachedPids, sizeof(pid_t)*shmem[idx].countAttach); + for (int i=0; isun_family = AF_UNIX; + sprintf(&addr->sun_path[1], ANDROID_SHMEM_SOCKNAME, ashv_socket_id_from_shmid(shmid)); + int addrlen = sizeof(addr->sun_family) + strlen(&addr->sun_path[1]) + 1; int recvsock = socket(AF_UNIX, SOCK_STREAM, 0); if (recvsock == -1) { DBG ("%s: cannot create UNIX socket: %s", __PRETTY_FUNCTION__, strerror(errno)); return -1; } - if (connect(recvsock, (struct sockaddr*) &addr, addrlen) != 0) { - DBG("%s: Cannot connect to UNIX socket %s: %s, len %d", __PRETTY_FUNCTION__, addr.sun_path + 1, strerror(errno), addrlen); + if (connect(recvsock, (struct sockaddr*)addr, addrlen) != 0) { + DBG("%s: Cannot connect to UNIX socket %s: %s, len %d", __PRETTY_FUNCTION__, addr->sun_path + 1, strerror(errno), addrlen); close(recvsock); return -1; } + return recvsock; +} + +static int ashv_send_shmid_action(int recvsock, struct sockaddr_un *addr, int shmid, int action) +{ if (send(recvsock, &shmid, sizeof(shmid), 0) != sizeof(shmid)) { - DBG ("%s: send() failed on socket %s: %s", __PRETTY_FUNCTION__, addr.sun_path + 1, strerror(errno)); + DBG("%s: send shmid failed on socket %s: %s", __PRETTY_FUNCTION__, addr->sun_path + 1, strerror(errno)); + return -1; + } + if (send(recvsock, &action, sizeof(action), 0) != sizeof(action)) { + DBG("%s: send action failed on socket %s: %s", __PRETTY_FUNCTION__, addr->sun_path + 1, strerror(errno)); + return -1; + } + return 0; +} + +#define FUNC_ASHV_READ(type, var) \ + type var; \ + if (read(recvsock, &var, sizeof(type)) != sizeof(type)) { \ + DBG("%s: ERROR: read %s failed", __PRETTY_FUNCTION__, #var); \ + close(recvsock); \ + return -1; \ + } + +static int ashv_delete_remote_segment(int shmid) +{ + struct sockaddr_un addr; + int recvsock = ashv_connect_socket(&addr, shmid); + if (recvsock == -1) { + return -1; + } + + int res = ashv_send_shmid_action(recvsock, &addr, shmid, ASHV_RM); + + close(recvsock); + return res; +} + +static int ashv_send_pid_remote_segment(int shmid, int action) +{ + struct sockaddr_un addr; + int recvsock = ashv_connect_socket(&addr, shmid); + if (recvsock == -1) { + return -1; + } + + if (ashv_send_shmid_action(recvsock, &addr, shmid, action) == -1) { close(recvsock); return -1; } - key_t key; - if (read(recvsock, &key, sizeof(key_t)) != sizeof(key_t)) { - DBG("%s: ERROR: failed read", __PRETTY_FUNCTION__); + if (write(recvsock, &ashv_pid_setup, sizeof(pid_t)) != sizeof(pid_t)) { + DBG("%s: ERROR: write pid failed", __PRETTY_FUNCTION__); + close(recvsock); + return -1; + } + + close(recvsock); + return 0; +} + +static int ashv_attach_remote_segment(int shmid) +{ + return ashv_send_pid_remote_segment(shmid, ASHV_AT); +} + +static int ashv_detach_remote_segment(int shmid) +{ + return ashv_send_pid_remote_segment(shmid, ASHV_DT); +} + +static int ashv_read_remote_segment(int shmid) +{ + struct sockaddr_un addr; + int recvsock = ashv_connect_socket(&addr, shmid); + if (recvsock == -1) { + return -1; + } + + if (ashv_send_shmid_action(recvsock, &addr, shmid, ASHV_GET) == -1) { + close(recvsock); + return -1; + } + + FUNC_ASHV_READ(key_t, key) + FUNC_ASHV_READ(bool, markedForDeletion) + FUNC_ASHV_READ(int, countAttach) + + if (markedForDeletion && countAttach == 0) { + DBG("%s: shmid %d is marked for deletion so it is not passed", __PRETTY_FUNCTION__, shmid); close(recvsock); return -1; } @@ -278,11 +485,54 @@ static int ashv_read_remote_segment(int shmid) shmem[idx].descriptor = descriptor; shmem[idx].size = size; shmem[idx].addr = NULL; - shmem[idx].markedForDeletion = false; + shmem[idx].markedForDeletion = markedForDeletion; shmem[idx].key = key; + shmem[idx].countAttach = countAttach; return idx; } +static int ashv_get_update_remote_segment(int idx) +{ + int shmid = shmem[idx].id; + struct sockaddr_un addr; + int recvsock = ashv_connect_socket(&addr, shmid); + if (recvsock == -1) { + goto removal; + } + + if (ashv_send_shmid_action(recvsock, &addr, shmid, ASHV_UPD) == -1) { + close(recvsock); + goto removal; + } + + if (read(recvsock, &shmem[idx].markedForDeletion, sizeof(bool)) != sizeof(bool)) { + close(recvsock); + goto removal; + } + + if (read(recvsock, &shmem[idx].countAttach, sizeof(int)) != sizeof(int)) { + close(recvsock); + goto removal; + } + + if (ashv_read_pids(recvsock, idx) != 0) { + close(recvsock); + goto removal; + } + + close(recvsock); + return 0; +removal: + DBG("%s: socket returned an error, shm %d will have a delete mark", __PRETTY_FUNCTION__, shmid); + shmem[idx].countAttach = 0; + shmem[idx].markedForDeletion = true; + shmem[idx].attachedPids = NULL; + if (shmem[idx].addr != NULL) { + android_shmem_attach_pid(idx, ashv_pid_setup); + } + return -1; +} + /* Get shared memory area identifier. */ int shmget(key_t key, size_t size, int flags) { @@ -348,13 +598,31 @@ int shmget(key_t key, size_t size, int flags) path_buffer[path_length] = '\0'; int shmid = atoi(path_buffer); if (shmid != 0) { + int socket_id = ashv_socket_id_from_shmid(shmid); int idx = ashv_find_local_index(shmid); - - if (idx == -1) { + if (idx == -1 && socket_id != ashv_local_socket_id) { idx = ashv_read_remote_segment(shmid); } if (idx != -1) { + if (socket_id != ashv_local_socket_id) { + ashv_get_update_remote_segment(idx); + } else { + android_shmem_check_pids(idx); + } + if (shmem[idx].markedForDeletion && shmem[idx].countAttach == 0) { + android_shmem_delete(idx); + idx = -1; + } + } + + if (idx != -1) { + if (flags & IPC_CREAT && flags & IPC_EXCL) { + DBG("%s: shm with key %d should be created but it already exists (IPC_CREAT+IPC_EXCL)", __PRETTY_FUNCTION__, key); + errno = EEXIST; + pthread_mutex_unlock(&mutex); + return -1; + } pthread_mutex_unlock(&mutex); return shmem[idx].id; } @@ -373,6 +641,13 @@ int shmget(key_t key, size_t size, int flags) } if (symlink(num_buffer, symlink_path) == 0) break; } + + if (!(flags & IPC_CREAT)) { + DBG("%s: shm with key %d was not found and no command was given to create it (no IPC_CREAT)", __PRETTY_FUNCTION__, key); + errno = ENOENT; + pthread_mutex_unlock(&mutex); + return -1; + } } @@ -394,6 +669,8 @@ int shmget(key_t key, size_t size, int flags) shmem[idx].id = shmid; shmem[idx].markedForDeletion = false; shmem[idx].key = key; + shmem[idx].countAttach = 0; + shmem[idx].attachedPids = NULL; if (shmem[idx].descriptor < 0) { DBG("%s: ashmem_create_region() failed for size %zu: %s", __PRETTY_FUNCTION__, size, strerror(errno)); @@ -428,37 +705,55 @@ int shmget(key_t key, size_t size, int flags) return shmid; } +#define INIT_SHMEM(ret_err) \ + int socket_id = ashv_socket_id_from_shmid(shmid); \ + int idx = ashv_find_local_index(shmid); \ + if (idx == -1 && socket_id != ashv_local_socket_id) { \ + idx = ashv_read_remote_segment(shmid); \ + } \ + if (idx == -1) { \ + DBG ("%s: ERROR: shmid %x does not exist\n", __PRETTY_FUNCTION__, shmid); \ + pthread_mutex_unlock(&mutex); \ + errno = EINVAL; \ + return ret_err; \ + } \ + if (socket_id != ashv_local_socket_id) { \ + ashv_get_update_remote_segment(idx); \ + } else { \ + android_shmem_check_pids(idx); \ + } \ + if (shmem[idx].markedForDeletion && shmem[idx].countAttach == 0) { \ + DBG ("%s: shmid %d marked for deletion, it will be deleted\n", __PRETTY_FUNCTION__, shmid); \ + android_shmem_delete(idx); \ + pthread_mutex_unlock(&mutex); \ + errno = EINVAL; \ + return ret_err; \ + } + /* Attach shared memory segment. */ void* shmat(int shmid, void const* shmaddr, int shmflg) { ashv_check_pid(); - int socket_id = ashv_socket_id_from_shmid(shmid); void *addr; pthread_mutex_lock(&mutex); - int idx = ashv_find_local_index(shmid); - if (idx == -1 && socket_id != ashv_local_socket_id) { - idx = ashv_read_remote_segment(shmid); - } - - if (idx == -1) { - DBG ("%s: shmid %x does not exist", __PRETTY_FUNCTION__, shmid); - pthread_mutex_unlock(&mutex); - errno = EINVAL; - return (void*) -1; - } + INIT_SHMEM((void*)-1) if (shmem[idx].addr == NULL) { + if (socket_id != ashv_local_socket_id) { + ashv_attach_remote_segment(shmid); + } + android_shmem_attach_pid(idx, ashv_pid_setup); shmem[idx].addr = mmap((void*) shmaddr, shmem[idx].size, PROT_READ | (shmflg == 0 ? PROT_WRITE : 0), MAP_SHARED, shmem[idx].descriptor, 0); if (shmem[idx].addr == MAP_FAILED) { - DBG ("%s: mmap() failed for ID %x FD %d: %s", __PRETTY_FUNCTION__, idx, shmem[idx].descriptor, strerror(errno)); + DBG ("%s: mmap() failed for ID %x FD %d: %s\n", __PRETTY_FUNCTION__, idx, shmem[idx].descriptor, strerror(errno)); shmem[idx].addr = NULL; } } addr = shmem[idx].addr; - DBG ("%s: mapped addr %p for FD %d ID %d", __PRETTY_FUNCTION__, addr, shmem[idx].descriptor, idx); + DBG ("%s: mapped addr %p for FD %d ID %d\n", __PRETTY_FUNCTION__, addr, shmem[idx].descriptor, idx); pthread_mutex_unlock (&mutex); return addr ? addr : (void *)-1; @@ -470,24 +765,36 @@ int shmdt(void const* shmaddr) ashv_check_pid(); pthread_mutex_lock(&mutex); - for (size_t i = 0; i < shmem_amount; i++) { + for (size_t i = 0; i < shmem_amount; i++) if (shmem[i].addr == shmaddr) { if (munmap(shmem[i].addr, shmem[i].size) != 0) { - DBG("%s: munmap %p failed", __PRETTY_FUNCTION__, shmaddr); + DBG("%s: munmap %p failed\n", __PRETTY_FUNCTION__, shmaddr); } shmem[i].addr = NULL; - DBG("%s: unmapped addr %p for FD %d ID %zu shmid %x", __PRETTY_FUNCTION__, shmaddr, shmem[i].descriptor, i, shmem[i].id); - if (shmem[i].markedForDeletion || ashv_socket_id_from_shmid(shmem[i].id) != ashv_local_socket_id) { - DBG ("%s: deleting shmid %x", __PRETTY_FUNCTION__, shmem[i].id); + DBG("%s: unmapped addr %p for FD %d ID %zu shmid %x\n", __PRETTY_FUNCTION__, shmaddr, shmem[i].descriptor, i, shmem[i].id); + + int socket_id = ashv_socket_id_from_shmid(shmem[i].id); + if (socket_id != ashv_local_socket_id) { + ashv_get_update_remote_segment(i); + ashv_detach_remote_segment(shmem[i].id); + } else { + android_shmem_check_pids(i); + } + android_shmem_detach_pid(i, ashv_pid_setup); + + if (shmem[i].markedForDeletion && shmem[i].countAttach == 0) { + DBG ("%s: deleting shmid %x\n", __PRETTY_FUNCTION__, shmem[i].id); android_shmem_delete(i); + if (socket_id != ashv_local_socket_id) { + ashv_delete_remote_segment(shmem[i].id); + } } pthread_mutex_unlock(&mutex); return 0; - } } pthread_mutex_unlock(&mutex); - DBG("%s: invalid address %p", __PRETTY_FUNCTION__, shmaddr); + DBG("%s: invalid address %p\n", __PRETTY_FUNCTION__, shmaddr); /* Could be a remove segment, do not report an error for that. */ return 0; } @@ -497,46 +804,32 @@ int shmctl(int shmid, int cmd, struct shmid_ds *buf) { ashv_check_pid(); - if (cmd == IPC_RMID) { - DBG("%s: IPC_RMID for shmid=%x", __PRETTY_FUNCTION__, shmid); - pthread_mutex_lock(&mutex); - int idx = ashv_find_local_index(shmid); - if (idx == -1) { - DBG("%s: shmid=%x does not exist locally", __PRETTY_FUNCTION__, shmid); - /* We do not rm non-local regions, but do not report an error for that. */ - pthread_mutex_unlock(&mutex); - return 0; - } + pthread_mutex_lock(&mutex); - if (shmem[idx].addr) { - // shmctl(2): The segment will actually be destroyed only - // after the last process detaches it (i.e., when the shm_nattch - // member of the associated structure shmid_ds is zero. - shmem[idx].markedForDeletion = true; - } else { - android_shmem_delete(idx); + INIT_SHMEM(-1) + + switch (cmd) { + case IPC_RMID: + DBG("%s: IPC_RMID for shmid=%x\n", __PRETTY_FUNCTION__, shmid); + + ashv_delete_segment(idx); + if (socket_id != ashv_local_socket_id) { + ashv_delete_remote_segment(shmid); } - pthread_mutex_unlock(&mutex); - return 0; - } else if (cmd == IPC_STAT) { + + goto ok; + case SHM_STAT: + case SHM_STAT_ANY: + case IPC_STAT: if (!buf) { - DBG ("%s: ERROR: buf == NULL for shmid %x", __PRETTY_FUNCTION__, shmid); - errno = EINVAL; - return -1; + DBG ("%s: ERROR: buf == NULL for shmid %x\n", __PRETTY_FUNCTION__, shmid); + goto error; } - pthread_mutex_lock(&mutex); - int idx = ashv_find_local_index(shmid); - if (idx == -1) { - DBG ("%s: ERROR: shmid %x does not exist", __PRETTY_FUNCTION__, shmid); - pthread_mutex_unlock (&mutex); - errno = EINVAL; - return -1; - } /* Report max permissive mode */ memset(buf, 0, sizeof(struct shmid_ds)); buf->shm_segsz = shmem[idx].size; - buf->shm_nattch = 1; + buf->shm_nattch = shmem[idx].countAttach; buf->shm_perm.key = shmem[idx].key; buf->shm_perm.uid = geteuid(); buf->shm_perm.gid = getegid(); @@ -545,11 +838,16 @@ int shmctl(int shmid, int cmd, struct shmid_ds *buf) buf->shm_perm.mode = 0666; buf->shm_perm.seq = 1; - pthread_mutex_unlock (&mutex); - return 0; + goto ok; + default: + DBG("%s: cmd %d not implemented yet!\n", __PRETTY_FUNCTION__, cmd); + goto error; } - - DBG("%s: cmd %d not implemented yet!", __PRETTY_FUNCTION__, cmd); +ok: + pthread_mutex_unlock (&mutex); + return 0; +error: + pthread_mutex_unlock (&mutex); errno = EINVAL; return -1; } diff --git a/test/Makefile b/test/Makefile index fbb54a2..3481f63 100644 --- a/test/Makefile +++ b/test/Makefile @@ -13,7 +13,7 @@ cleanup-memory: test ./cleanup-shared-memory.sh endif -test: $(addprefix testcase-,$(patsubst %.c,%,$(wildcard *.c))) ; +test: $(addprefix testcase-,$(patsubst %.c,%,$(wildcard *.c))) test-interactions-shm ; testcase-% : test-% ./$^ @@ -21,8 +21,12 @@ testcase-% : test-% test-%: %.c $(EXTRA_CFILE) $(CC) $(CFLAGS) $+ -o $@ +test-interactions-shm: ./interactions-shm/ + $(MAKE) -C ./interactions-shm/ + clean: rm -f test-* + $(MAKE) clean -C ./interactions-shm/ .PHONY: clean \ test \ diff --git a/test/interactions-shm/Makefile b/test/interactions-shm/Makefile new file mode 100644 index 0000000..667c93b --- /dev/null +++ b/test/interactions-shm/Makefile @@ -0,0 +1,74 @@ +CFLAGS += -Wall -Wextra -Werror -std=c99 -I.. + +OS := $(shell uname -o 2> /dev/null) + +ifeq ($(OS), Android) +CFLAGS += -I../.. -llog +EXTRA_CFILE = ../../shmem.c +cleanup-memory: test ; +else +CFLAGS += -DSYSV_ASHMEM_TEST_SYSTEM +EXTRA_CFILE = +cleanup-memory: test + ../cleanup-shared-memory.sh +endif + +test: testcase-1 testcase-2 testcase-3 + +testcase-1: test-host test-remove host-available.sh + ./test-host & + sleep 0.1 + + ./test-remove + ./host-available.sh no + +testcase-2: test-host test-write test-read test-remove host-available.sh + ./test-host & + sleep 0.1 + + ./test-host && exit 1 || true + ./host-available.sh yes + + ./test-write + ./test-read + + ./test-remove + +testcase-3: test-host test-write test-read test-endless-attachment test-status test-remove host-available.sh check-attachments.sh + ./test-host & + sleep 0.1 + + ./check-attachments.sh + + ./test-endless-attachment & + sleep 0.1 + + ./check-attachments.sh + + ./test-endless-attachment & + sleep 0.1 + + ./check-attachments.sh + + ./test-remove + ./host-available.sh yes + + kill $$(pidof -s test-endless-attachment) + ./host-available.sh yes + + ./test-write + ./test-read + + kill $$(pidof -s test-endless-attachment) + ./host-available.sh no + +test-%: %.c $(EXTRA_CFILE) + $(CC) $(CFLAGS) $+ -o $@ + +clean: + rm -f test-* + +.PHONY: clean \ + test + +.PRECIOUS: test-% diff --git a/test/interactions-shm/check-attachments.sh b/test/interactions-shm/check-attachments.sh new file mode 100755 index 0000000..26d59ee --- /dev/null +++ b/test/interactions-shm/check-attachments.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +set -e + +ca=$(./test-status) +cr=$(pidof test-endless-attachment | tr ' ' '\n' | wc -l) + +if [ "${ca}" = "${cr}" ]; then + echo "check-attachments - ok" + exit 0 +fi + +echo "check-attachments - no matches (ca:${ca} vs cr:${cr})" +for pid in $(pidof test-endless-attachment); do + kill $pid +done +./test-remove &> /dev/null || true +exit 1 diff --git a/test/interactions-shm/endless-attachment.c b/test/interactions-shm/endless-attachment.c new file mode 100644 index 0000000..110101a --- /dev/null +++ b/test/interactions-shm/endless-attachment.c @@ -0,0 +1,13 @@ +#include "utils.h" + +int main() { + key_t key; + if ((key = ftok(".", 1)) == -1) error_exit("ftok"); + int shmid; + if ((shmid = shmget(key, 30, 0666)) < 0) error_exit("shmget"); + if (shmat(shmid, NULL, 0) == (void*) -1) error_exit("shmat"); + printf("endless-attachment - ok\n"); + while (1) + continue; + return 0; +} diff --git a/test/interactions-shm/host-available.sh b/test/interactions-shm/host-available.sh new file mode 100755 index 0000000..fc9dadd --- /dev/null +++ b/test/interactions-shm/host-available.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +status=$(pidof -q test-host && echo yes || echo no) +echo "host available: $status" +[ "${status}" = "${1}" ] +exit $? diff --git a/test/interactions-shm/host.c b/test/interactions-shm/host.c new file mode 100644 index 0000000..d4d3752 --- /dev/null +++ b/test/interactions-shm/host.c @@ -0,0 +1,11 @@ +#include "utils.h" + +int main() { + key_t key; + if ((key = ftok(".", 1)) == -1) error_exit("ftok"); + if (shmget(key, 30, IPC_CREAT | IPC_EXCL | 0666) < 0) error_exit("shmget"); + while (shmget(key, 30, 0666) > 0) + continue; + printf("host - ok\n"); + return 0; +} diff --git a/test/interactions-shm/read.c b/test/interactions-shm/read.c new file mode 100644 index 0000000..0de43d1 --- /dev/null +++ b/test/interactions-shm/read.c @@ -0,0 +1,15 @@ +#include "utils.h" + +int main() { + key_t key; + if ((key = ftok(".", 1)) == -1) error_exit("ftok"); + int shmid; + if ((shmid = shmget(key, 30, 0666)) < 0) error_exit("shmget"); + char *shm; + if ((shm = shmat(shmid, NULL, 0)) == (char*) -1) error_exit("shmat"); + printf("%s\n", shm); + if (strcmp(TEST_TEXT, shm) != 0) error_exit("strcmp"); + if (shmdt(shm) != 0) error_exit("shmdt"); + printf("read - ok\n"); + return 0; +} diff --git a/test/interactions-shm/remove.c b/test/interactions-shm/remove.c new file mode 100644 index 0000000..75d3643 --- /dev/null +++ b/test/interactions-shm/remove.c @@ -0,0 +1,11 @@ +#include "utils.h" + +int main() { + key_t key; + if ((key = ftok(".", 1)) == -1) error_exit("ftok"); + int shmid; + if ((shmid = shmget(key, 30, 0666)) < 0) error_exit("shmget"); + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl"); + printf("remove - ok\n"); + return 0; +} diff --git a/test/interactions-shm/status.c b/test/interactions-shm/status.c new file mode 100644 index 0000000..d14b36a --- /dev/null +++ b/test/interactions-shm/status.c @@ -0,0 +1,12 @@ +#include "utils.h" + +int main() { + key_t key; + if ((key = ftok(".", 1)) == -1) error_exit("ftok"); + int shmid; + if ((shmid = shmget(key, 30, 0666)) < 0) error_exit("shmget"); + struct shmid_ds buf; + if (shmctl(shmid, IPC_STAT, &buf) == -1) error_exit("shmctl"); + printf("%lu\n", buf.shm_nattch); + return 0; +} diff --git a/test/interactions-shm/write.c b/test/interactions-shm/write.c new file mode 100644 index 0000000..4d7334c --- /dev/null +++ b/test/interactions-shm/write.c @@ -0,0 +1,14 @@ +#include "utils.h" + +int main() { + key_t key; + if ((key = ftok(".", 1)) == -1) error_exit("ftok"); + int shmid; + if ((shmid = shmget(key, 30, 0666)) < 0) error_exit("shmget"); + char *shm; + if ((shm = shmat(shmid, NULL, 0)) == (char*) -1) error_exit("shmat"); + memcpy(shm, TEST_TEXT, sizeof(TEST_TEXT)); + if (shmdt(shm) != 0) error_exit("shmdt"); + printf("write - ok\n"); + return 0; +} diff --git a/test/utils.h b/test/utils.h index a02a982..7f324de 100644 --- a/test/utils.h +++ b/test/utils.h @@ -13,6 +13,8 @@ # include "shm.h" #endif +#define TEST_TEXT "Hello, Test!" + void error_exit(char const* msg) { perror(msg); exit(1); From cdcf597167cc6de3affff5e770cebf1b1cf2155b Mon Sep 17 00:00:00 2001 From: Maxython Date: Wed, 19 Feb 2025 14:50:23 +0300 Subject: [PATCH 2/2] improvement: global socket implementation (test) --- exports.txt | 2 + shm.h | 3 + shmem.c | 647 ++++++++++++------ test/after-fork.c | 1 + test/at-memory.c | 1 + test/deadlock.c | 1 + test/error-codes.c | 1 + test/interactions-shm/Makefile | 53 +- test/interactions-shm/create-ipc.c | 8 + .../interactions-shm/{host.c => create-shm.c} | 4 +- test/interactions-shm/remove.c | 12 +- .../{host-available.sh => shm-available.sh} | 2 +- test/interactions-shm/testcase-1.sh | 5 + test/interactions-shm/testcase-2.sh | 10 + test/interactions-shm/testcase-3.sh | 20 + test/interactions-shm/testcase-4.sh | 21 + test/ipc-private.c | 2 + test/with-key.c | 1 + 18 files changed, 535 insertions(+), 259 deletions(-) create mode 100644 test/interactions-shm/create-ipc.c rename test/interactions-shm/{host.c => create-shm.c} (72%) rename test/interactions-shm/{host-available.sh => shm-available.sh} (59%) create mode 100644 test/interactions-shm/testcase-1.sh create mode 100644 test/interactions-shm/testcase-2.sh create mode 100644 test/interactions-shm/testcase-3.sh create mode 100644 test/interactions-shm/testcase-4.sh diff --git a/exports.txt b/exports.txt index 2640f41..5e3ee07 100644 --- a/exports.txt +++ b/exports.txt @@ -4,6 +4,8 @@ libandroid_shmget; libandroid_shmat; libandroid_shmdt; + libandroid_shmat_fd; + libandroid_shmdt_fd; shmctl; shmget; shmat; diff --git a/shm.h b/shm.h index faab787..4201f0a 100644 --- a/shm.h +++ b/shm.h @@ -31,6 +31,9 @@ extern void *shmat(int shmid, void const* shmaddr, int shmflg); #define shmdt libandroid_shmdt extern int shmdt(void const* shmaddr); +extern int libandroid_shmat_fd(int shmid, size_t* out_size); +extern int libandroid_shmdt_fd(int fd); + __END_DECLS #endif diff --git a/shmem.c b/shmem.c index 649cd1f..522636e 100644 --- a/shmem.c +++ b/shmem.c @@ -20,14 +20,17 @@ #define DBG(...) __android_log_print(ANDROID_LOG_INFO, "shmem", __VA_ARGS__) #define ASHV_KEY_SYMLINK_PATH _PATH_TMP "ashv_key_%d" #define ANDROID_SHMEM_SOCKNAME "/dev/shm/%08x" +#define ANDROID_SHMEM_GLOBAL_SOCKNAME "/dev/shm/global" +#define ANDROID_SHMEM_GLOBAL_PROCNAME "global-shmem" #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) // Action numbers -#define ASHV_RM 0 +#define ASHV_PUT 0 #define ASHV_GET 1 -#define ASHV_AT 2 -#define ASHV_DT 3 -#define ASHV_UPD 4 +#define ASHV_UPD 2 +#define ASHV_RM 3 +#define ASHV_AT 4 +#define ASHV_DT 5 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; @@ -42,6 +45,7 @@ typedef struct { key_t key; int countAttach; pid_t *attachedPids; + bool global; } shmem_t; static shmem_t* shmem = NULL; @@ -52,8 +56,10 @@ static size_t shmem_amount = 0; static int ashv_local_socket_id = 0; // To handle forks we store which pid the ashv_local_socket_id was // created for. -static pid_t ashv_pid_setup = 0; +static int ashv_pid_setup = 0; static pthread_t ashv_listening_thread_id = 0; +static int global_sendsock; +static bool global_conf = false; static int ancil_send_fd(int sock, int fd) { @@ -209,14 +215,35 @@ static int ashv_write_pids(int sendsock, int idx) return 0; } -void android_shmem_attach_pid(int idx, pid_t pid) { +static int ashv_read_pids(int recvsock, int idx) +{ + if (shmem[idx].countAttach > 0) { + pid_t pids[shmem[idx].countAttach]; + if (read(recvsock, &pids, sizeof(pid_t)*shmem[idx].countAttach) != sizeof(pid_t)*shmem[idx].countAttach) { + DBG("%s: ERROR: read pids failed: %s", __PRETTY_FUNCTION__, strerror(errno)); + return -1; + } + shmem[idx].attachedPids = NULL; + shmem[idx].attachedPids = realloc(shmem[idx].attachedPids, sizeof(pid_t)*shmem[idx].countAttach); + for (int i=0; i 0) { - pid_t pids[shmem[idx].countAttach]; - if (read(recvsock, &pids, sizeof(pid_t)*shmem[idx].countAttach) != sizeof(pid_t)*shmem[idx].countAttach) { - DBG("%s: ERROR: read pids failed: %s", __PRETTY_FUNCTION__, strerror(errno)); - return -1; - } - shmem[idx].attachedPids = NULL; - shmem[idx].attachedPids = realloc(shmem[idx].attachedPids, sizeof(pid_t)*shmem[idx].countAttach); - for (int i=0; isun_family = AF_UNIX; - sprintf(&addr->sun_path[1], ANDROID_SHMEM_SOCKNAME, ashv_socket_id_from_shmid(shmid)); + sprintf(&addr->sun_path[1], ANDROID_SHMEM_GLOBAL_SOCKNAME); int addrlen = sizeof(addr->sun_family) + strlen(&addr->sun_path[1]) + 1; - int recvsock = socket(AF_UNIX, SOCK_STREAM, 0); - if (recvsock == -1) { + int gsock = socket(AF_UNIX, SOCK_STREAM, 0); + if (gsock == -1) { DBG ("%s: cannot create UNIX socket: %s", __PRETTY_FUNCTION__, strerror(errno)); return -1; } - if (connect(recvsock, (struct sockaddr*)addr, addrlen) != 0) { + if (connect(gsock, (struct sockaddr*)addr, addrlen) != 0) { DBG("%s: Cannot connect to UNIX socket %s: %s, len %d", __PRETTY_FUNCTION__, addr->sun_path + 1, strerror(errno), addrlen); - close(recvsock); - return -1; + goto close; } - return recvsock; -} - -static int ashv_send_shmid_action(int recvsock, struct sockaddr_un *addr, int shmid, int action) -{ - if (send(recvsock, &shmid, sizeof(shmid), 0) != sizeof(shmid)) { - DBG("%s: send shmid failed on socket %s: %s", __PRETTY_FUNCTION__, addr->sun_path + 1, strerror(errno)); - return -1; + if (send(gsock, &action, sizeof(action), 0) != sizeof(action)) { + DBG("%s: send acction failed on socket %s: %s", __PRETTY_FUNCTION__, addr->sun_path + 1, strerror(errno)); + goto close; } - if (send(recvsock, &action, sizeof(action), 0) != sizeof(action)) { - DBG("%s: send action failed on socket %s: %s", __PRETTY_FUNCTION__, addr->sun_path + 1, strerror(errno)); - return -1; + if (send(gsock, &shmid, sizeof(shmid), 0) != sizeof(shmid)) { + DBG("%s: send shmid failed on socket %s: %s", __PRETTY_FUNCTION__, addr->sun_path + 1, strerror(errno)); + goto close; } - return 0; + + global_conf = true; + return gsock; +close: + close(gsock); + return -1; } -#define FUNC_ASHV_READ(type, var) \ - type var; \ - if (read(recvsock, &var, sizeof(type)) != sizeof(type)) { \ - DBG("%s: ERROR: read %s failed", __PRETTY_FUNCTION__, #var); \ - close(recvsock); \ - return -1; \ +static int ashv_status_gsocket(int gsock) { + int status; + if (read(gsock, &status, sizeof(int)) != sizeof(int)) { + DBG("%s: ERROR: read status failed", __PRETTY_FUNCTION__); + status = -1; } + close(gsock); + global_conf = false; + return status; +} -static int ashv_delete_remote_segment(int shmid) +static int ashv_one_action_gsocket(int action, int shmid) { struct sockaddr_un addr; - int recvsock = ashv_connect_socket(&addr, shmid); - if (recvsock == -1) { + int gsock = ashv_connect_gsocket(&addr, action, shmid); + if (gsock == -1) { return -1; } - int res = ashv_send_shmid_action(recvsock, &addr, shmid, ASHV_RM); - - close(recvsock); - return res; + return ashv_status_gsocket(gsock); } -static int ashv_send_pid_remote_segment(int shmid, int action) +static int ashv_send_pid_gsocket(int action, int shmid) { struct sockaddr_un addr; - int recvsock = ashv_connect_socket(&addr, shmid); - if (recvsock == -1) { + int gsock = ashv_connect_gsocket(&addr, action, shmid); + if (gsock == -1) { return -1; } - if (ashv_send_shmid_action(recvsock, &addr, shmid, action) == -1) { - close(recvsock); - return -1; - } - - if (write(recvsock, &ashv_pid_setup, sizeof(pid_t)) != sizeof(pid_t)) { + if (write(gsock, &ashv_pid_setup, sizeof(pid_t)) != sizeof(pid_t)) { DBG("%s: ERROR: write pid failed", __PRETTY_FUNCTION__); - close(recvsock); + close(gsock); return -1; } - close(recvsock); - return 0; + return ashv_status_gsocket(gsock); } -static int ashv_attach_remote_segment(int shmid) -{ - return ashv_send_pid_remote_segment(shmid, ASHV_AT); -} - -static int ashv_detach_remote_segment(int shmid) -{ - return ashv_send_pid_remote_segment(shmid, ASHV_DT); -} +#define ASHV_READ_GSOCK(type, var) \ + type var; \ + if (read(gsock, &var, sizeof(type)) != sizeof(type)) { \ + DBG("%s: ERROR: read %s failed", __PRETTY_FUNCTION__, #var); \ + goto error_close; \ + } -static int ashv_read_remote_segment(int shmid) +static int ashv_get_shm_gsocket(int shmid) { struct sockaddr_un addr; - int recvsock = ashv_connect_socket(&addr, shmid); - if (recvsock == -1) { + int gsock = ashv_connect_gsocket(&addr, ASHV_GET, shmid); + if (gsock == -1) { return -1; } - if (ashv_send_shmid_action(recvsock, &addr, shmid, ASHV_GET) == -1) { - close(recvsock); - return -1; - } - - FUNC_ASHV_READ(key_t, key) - FUNC_ASHV_READ(bool, markedForDeletion) - FUNC_ASHV_READ(int, countAttach) + ASHV_READ_GSOCK(key_t, key) + ASHV_READ_GSOCK(bool, markedForDeletion) + ASHV_READ_GSOCK(int, countAttach) if (markedForDeletion && countAttach == 0) { DBG("%s: shmid %d is marked for deletion so it is not passed", __PRETTY_FUNCTION__, shmid); - close(recvsock); - return -1; + goto error_close; } - int descriptor = ancil_recv_fd(recvsock); + int descriptor = ancil_recv_fd(gsock); if (descriptor < 0) { DBG("%s: ERROR: ancil_recv_fd() failed on socket %s: %s", __PRETTY_FUNCTION__, addr.sun_path + 1, strerror(errno)); - close(recvsock); - return -1; + goto error_close; } - close(recvsock); + global_conf = false; + close(gsock); int size = ashmem_get_size_region(descriptor); if (size == 0 || size == -1) { @@ -488,44 +676,44 @@ static int ashv_read_remote_segment(int shmid) shmem[idx].markedForDeletion = markedForDeletion; shmem[idx].key = key; shmem[idx].countAttach = countAttach; + shmem[idx].global = true; return idx; +error_close: + global_conf = false; + close(gsock); + return -1; } -static int ashv_get_update_remote_segment(int idx) +static int ashv_update_shm_gsocket(int idx) { int shmid = shmem[idx].id; struct sockaddr_un addr; - int recvsock = ashv_connect_socket(&addr, shmid); - if (recvsock == -1) { - goto removal; - } - - if (ashv_send_shmid_action(recvsock, &addr, shmid, ASHV_UPD) == -1) { - close(recvsock); + int gsock = ashv_connect_gsocket(&addr, ASHV_UPD, shmid); + if (gsock == -1) { goto removal; } - if (read(recvsock, &shmem[idx].markedForDeletion, sizeof(bool)) != sizeof(bool)) { - close(recvsock); + if (read(gsock, &shmem[idx].markedForDeletion, sizeof(bool)) != sizeof(bool)) { + close(gsock); goto removal; } - if (read(recvsock, &shmem[idx].countAttach, sizeof(int)) != sizeof(int)) { - close(recvsock); + if (read(gsock, &shmem[idx].countAttach, sizeof(int)) != sizeof(int)) { + close(gsock); goto removal; } - if (ashv_read_pids(recvsock, idx) != 0) { - close(recvsock); + if (ashv_read_pids(gsock, idx) != 0) { + close(gsock); goto removal; } - close(recvsock); - return 0; + return ashv_status_gsocket(gsock); removal: - DBG("%s: socket returned an error, shm %d will have a delete mark", __PRETTY_FUNCTION__, shmid); + DBG("%s: gsocket returned an error, shm %d will have a delete mark", __PRETTY_FUNCTION__, shmid); shmem[idx].countAttach = 0; shmem[idx].markedForDeletion = true; + shmem[idx].global = false; shmem[idx].attachedPids = NULL; if (shmem[idx].addr != NULL) { android_shmem_attach_pid(idx, ashv_pid_setup); @@ -533,11 +721,15 @@ static int ashv_get_update_remote_segment(int idx) return -1; } +#define FIND_SHMEM \ + int idx = ashv_find_local_index(shmid); \ + if (idx == -1 && (idx = ashv_get_shm_gsocket(shmid)) == -1 && ashv_socket_id_from_shmid(shmid) != ashv_local_socket_id) { \ + idx = ashv_put_remote_segment(shmid); \ + } + /* Get shared memory area identifier. */ int shmget(key_t key, size_t size, int flags) { - (void) flags; - ashv_check_pid(); // Counter wrapping around at 15 bits. @@ -598,18 +790,9 @@ int shmget(key_t key, size_t size, int flags) path_buffer[path_length] = '\0'; int shmid = atoi(path_buffer); if (shmid != 0) { - int socket_id = ashv_socket_id_from_shmid(shmid); - int idx = ashv_find_local_index(shmid); - if (idx == -1 && socket_id != ashv_local_socket_id) { - idx = ashv_read_remote_segment(shmid); - } - - if (idx != -1) { - if (socket_id != ashv_local_socket_id) { - ashv_get_update_remote_segment(idx); - } else { - android_shmem_check_pids(idx); - } + FIND_SHMEM + if (idx != -1 && shmem[idx].global) { + ashv_update_shm_gsocket(idx); if (shmem[idx].markedForDeletion && shmem[idx].countAttach == 0) { android_shmem_delete(idx); idx = -1; @@ -654,6 +837,13 @@ int shmget(key_t key, size_t size, int flags) int idx = shmem_amount; char buf[256]; sprintf(buf, ANDROID_SHMEM_SOCKNAME "-%d", ashv_local_socket_id, idx); + size = ROUND_UP(size, getpagesize()); + int descriptor = ashmem_create_region(buf, size); + if (descriptor < 0) { + DBG("%s: ashmem_create_region() failed for size %zu: %s", __PRETTY_FUNCTION__, size, strerror(errno)); + pthread_mutex_unlock(&mutex); + return -1; + } shmem_amount++; if (shmid == -1) { @@ -662,23 +852,19 @@ int shmget(key_t key, size_t size, int flags) } shmem = realloc(shmem, shmem_amount * sizeof(shmem_t)); - size = ROUND_UP(size, getpagesize()); shmem[idx].size = size; - shmem[idx].descriptor = ashmem_create_region(buf, size); + shmem[idx].descriptor = descriptor; shmem[idx].addr = NULL; shmem[idx].id = shmid; shmem[idx].markedForDeletion = false; shmem[idx].key = key; shmem[idx].countAttach = 0; shmem[idx].attachedPids = NULL; - - if (shmem[idx].descriptor < 0) { - DBG("%s: ashmem_create_region() failed for size %zu: %s", __PRETTY_FUNCTION__, size, strerror(errno)); - shmem_amount --; - shmem = realloc(shmem, shmem_amount * sizeof(shmem_t)); - pthread_mutex_unlock (&mutex); - return -1; + shmem[idx].global = false; + if (ashv_one_action_gsocket(ASHV_PUT, shmid) == 0 || ashv_fork_function() == 0) { + shmem[idx].global = true; } + //DBG("%s: ID %d shmid %x FD %d size %zu", __PRETTY_FUNCTION__, idx, shmid, shmem[idx].descriptor, shmem[idx].size); /* status = ashmem_set_prot_region (shmem[idx].descriptor, 0666); @@ -706,21 +892,15 @@ int shmget(key_t key, size_t size, int flags) } #define INIT_SHMEM(ret_err) \ - int socket_id = ashv_socket_id_from_shmid(shmid); \ - int idx = ashv_find_local_index(shmid); \ - if (idx == -1 && socket_id != ashv_local_socket_id) { \ - idx = ashv_read_remote_segment(shmid); \ - } \ + FIND_SHMEM \ if (idx == -1) { \ DBG ("%s: ERROR: shmid %x does not exist\n", __PRETTY_FUNCTION__, shmid); \ pthread_mutex_unlock(&mutex); \ errno = EINVAL; \ return ret_err; \ } \ - if (socket_id != ashv_local_socket_id) { \ - ashv_get_update_remote_segment(idx); \ - } else { \ - android_shmem_check_pids(idx); \ + if (shmem[idx].global) { \ + ashv_update_shm_gsocket(idx); \ } \ if (shmem[idx].markedForDeletion && shmem[idx].countAttach == 0) { \ DBG ("%s: shmid %d marked for deletion, it will be deleted\n", __PRETTY_FUNCTION__, shmid); \ @@ -742,18 +922,18 @@ void* shmat(int shmid, void const* shmaddr, int shmflg) INIT_SHMEM((void*)-1) if (shmem[idx].addr == NULL) { - if (socket_id != ashv_local_socket_id) { - ashv_attach_remote_segment(shmid); + if (shmem[idx].global) { + ashv_send_pid_gsocket(ASHV_AT, shmid); } android_shmem_attach_pid(idx, ashv_pid_setup); shmem[idx].addr = mmap((void*) shmaddr, shmem[idx].size, PROT_READ | (shmflg == 0 ? PROT_WRITE : 0), MAP_SHARED, shmem[idx].descriptor, 0); if (shmem[idx].addr == MAP_FAILED) { - DBG ("%s: mmap() failed for ID %x FD %d: %s\n", __PRETTY_FUNCTION__, idx, shmem[idx].descriptor, strerror(errno)); + DBG ("%s: mmap() failed for ID %x FD %d: %s", __PRETTY_FUNCTION__, idx, shmem[idx].descriptor, strerror(errno)); shmem[idx].addr = NULL; } } addr = shmem[idx].addr; - DBG ("%s: mapped addr %p for FD %d ID %d\n", __PRETTY_FUNCTION__, addr, shmem[idx].descriptor, idx); + DBG ("%s: mapped addr %p for FD %d ID %d", __PRETTY_FUNCTION__, addr, shmem[idx].descriptor, idx); pthread_mutex_unlock (&mutex); return addr ? addr : (void *)-1; @@ -768,33 +948,86 @@ int shmdt(void const* shmaddr) for (size_t i = 0; i < shmem_amount; i++) if (shmem[i].addr == shmaddr) { if (munmap(shmem[i].addr, shmem[i].size) != 0) { - DBG("%s: munmap %p failed\n", __PRETTY_FUNCTION__, shmaddr); + DBG("%s: munmap %p failed", __PRETTY_FUNCTION__, shmaddr); } shmem[i].addr = NULL; - DBG("%s: unmapped addr %p for FD %d ID %zu shmid %x\n", __PRETTY_FUNCTION__, shmaddr, shmem[i].descriptor, i, shmem[i].id); - - int socket_id = ashv_socket_id_from_shmid(shmem[i].id); - if (socket_id != ashv_local_socket_id) { - ashv_get_update_remote_segment(i); - ashv_detach_remote_segment(shmem[i].id); - } else { - android_shmem_check_pids(i); + DBG("%s: unmapped addr %p for FD %d ID %zu shmid %x", __PRETTY_FUNCTION__, shmaddr, shmem[i].descriptor, i, shmem[i].id); + + if (shmem[i].global) { + ashv_update_shm_gsocket(i); + ashv_send_pid_gsocket(ASHV_DT, shmem[i].id); } android_shmem_detach_pid(i, ashv_pid_setup); if (shmem[i].markedForDeletion && shmem[i].countAttach == 0) { - DBG ("%s: deleting shmid %x\n", __PRETTY_FUNCTION__, shmem[i].id); + DBG ("%s: deleting shmid %x", __PRETTY_FUNCTION__, shmem[i].id); + if (shmem[i].global) { + ashv_one_action_gsocket(ASHV_RM, shmem[i].id); + } android_shmem_delete(i); - if (socket_id != ashv_local_socket_id) { - ashv_delete_remote_segment(shmem[i].id); + } + pthread_mutex_unlock(&mutex); + return 0; + } + pthread_mutex_unlock(&mutex); + + DBG("%s: invalid address %p", __PRETTY_FUNCTION__, shmaddr); + /* Could be a remove segment, do not report an error for that. */ + return 0; +} + +/* Let PRoot attach shared memory segment to another process. */ +int libandroid_shmat_fd(int shmid, size_t* out_size) +{ + ashv_check_pid(); + + int fd; + + pthread_mutex_lock(&mutex); + + INIT_SHMEM(-1) + + if (shmem[idx].global) { + ashv_send_pid_gsocket(ASHV_AT, shmid); + } + + fd = shmem[idx].descriptor; + *out_size = shmem[idx].size; + DBG ("%s: mapped for FD %d ID %d", __PRETTY_FUNCTION__, shmem[idx].descriptor, idx); + pthread_mutex_unlock (&mutex); + + return fd; +} + +/* Let PRoot detach shared memory segment after last process detached. */ +int libandroid_shmdt_fd(int fd) +{ + ashv_check_pid(); + + pthread_mutex_lock(&mutex); + for (size_t i = 0; i < shmem_amount; i++) { + if (shmem[i].descriptor == fd) { + DBG("%s: unmapped for FD %d ID %zu shmid %x", __PRETTY_FUNCTION__, shmem[i].descriptor, i, shmem[i].id); + if (shmem[i].global) { + ashv_update_shm_gsocket(i); + ashv_send_pid_gsocket(ASHV_DT, shmem[i].id); + } + android_shmem_detach_pid(i, ashv_pid_setup); + + if (shmem[i].markedForDeletion && shmem[i].countAttach == 0) { + DBG ("%s: deleting shmid %x", __PRETTY_FUNCTION__, shmem[i].id); + if (shmem[i].global) { + ashv_one_action_gsocket(ASHV_RM, shmem[i].id); } + android_shmem_delete(i); } pthread_mutex_unlock(&mutex); return 0; + } } pthread_mutex_unlock(&mutex); - DBG("%s: invalid address %p\n", __PRETTY_FUNCTION__, shmaddr); + DBG("%s: invalid fd %d", __PRETTY_FUNCTION__, fd); /* Could be a remove segment, do not report an error for that. */ return 0; } @@ -810,19 +1043,19 @@ int shmctl(int shmid, int cmd, struct shmid_ds *buf) switch (cmd) { case IPC_RMID: - DBG("%s: IPC_RMID for shmid=%x\n", __PRETTY_FUNCTION__, shmid); + DBG("%s: IPC_RMID for shmid=%x", __PRETTY_FUNCTION__, shmid); - ashv_delete_segment(idx); - if (socket_id != ashv_local_socket_id) { - ashv_delete_remote_segment(shmid); + if (shmem[idx].global) { + ashv_one_action_gsocket(ASHV_RM, shmid); } + ashv_delete_segment(idx); goto ok; case SHM_STAT: case SHM_STAT_ANY: case IPC_STAT: if (!buf) { - DBG ("%s: ERROR: buf == NULL for shmid %x\n", __PRETTY_FUNCTION__, shmid); + DBG ("%s: ERROR: buf == NULL for shmid %x", __PRETTY_FUNCTION__, shmid); goto error; } @@ -840,7 +1073,7 @@ int shmctl(int shmid, int cmd, struct shmid_ds *buf) goto ok; default: - DBG("%s: cmd %d not implemented yet!\n", __PRETTY_FUNCTION__, cmd); + DBG("%s: cmd %d not implemented yet!", __PRETTY_FUNCTION__, cmd); goto error; } ok: diff --git a/test/after-fork.c b/test/after-fork.c index 4b2efa1..838d55d 100644 --- a/test/after-fork.c +++ b/test/after-fork.c @@ -18,6 +18,7 @@ int main() { if ((shm_child = shmat(shmid, NULL, 0)) == (char*) -1) error_exit("shmat-child-2"); shm_child[1] = '#'; if (shmdt(shm_child) != 0) error_exit("shmdt-child-2"); + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl-child"); return 0; } else { int shmid; diff --git a/test/at-memory.c b/test/at-memory.c index 73dcbad..52270a2 100644 --- a/test/at-memory.c +++ b/test/at-memory.c @@ -20,6 +20,7 @@ int main() { if (shm_child_copy != shm_child) failure_exit("not-at-requested-memory"); shm_child[1] = '#'; if (shmdt(shm_child) != 0) error_exit("shmdt-child-2"); + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl-child"); return 0; } else { int shmid; diff --git a/test/deadlock.c b/test/deadlock.c index f4f4643..ef65b36 100644 --- a/test/deadlock.c +++ b/test/deadlock.c @@ -6,6 +6,7 @@ int main() { int shmid_from_shmget; if ((shmid_from_shmget = shmget(SHMEM_KEY, 30, IPC_CREAT | 0666)) < 0) error_exit("shmget"); if ((shmid_from_shmget = shmget(SHMEM_KEY, 30, IPC_CREAT | 0666)) < 0) error_exit("shmget"); + if (shmctl(shmid_from_shmget, IPC_RMID, 0) == -1) error_exit("shmctl"); return 0; } diff --git a/test/error-codes.c b/test/error-codes.c index b539428..7f1eb0a 100644 --- a/test/error-codes.c +++ b/test/error-codes.c @@ -8,4 +8,5 @@ int main() { if (errno != EINVAL) error_exit("shmat-wrong-errno"); int shmid; if ((shmid = shmget(IPC_PRIVATE, 30, IPC_CREAT | 0666)) < 0) error_exit("shmget"); + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl"); } diff --git a/test/interactions-shm/Makefile b/test/interactions-shm/Makefile index 667c93b..ef3c9de 100644 --- a/test/interactions-shm/Makefile +++ b/test/interactions-shm/Makefile @@ -13,54 +13,19 @@ cleanup-memory: test ../cleanup-shared-memory.sh endif -test: testcase-1 testcase-2 testcase-3 +test: testcase-1 testcase-2 testcase-3 testcase-4 -testcase-1: test-host test-remove host-available.sh - ./test-host & - sleep 0.1 +testcase-1: test-create-shm test-remove shm-available.sh testcase-1.sh + bash ./testcase-1.sh - ./test-remove - ./host-available.sh no +testcase-2: test-create-shm test-write test-read test-remove shm-available.sh testcase-2.sh + bash ./testcase-2.sh -testcase-2: test-host test-write test-read test-remove host-available.sh - ./test-host & - sleep 0.1 +testcase-3: test-create-shm test-create-ipc test-remove shm-available.sh testcase-3.sh + bash ./testcase-3.sh - ./test-host && exit 1 || true - ./host-available.sh yes - - ./test-write - ./test-read - - ./test-remove - -testcase-3: test-host test-write test-read test-endless-attachment test-status test-remove host-available.sh check-attachments.sh - ./test-host & - sleep 0.1 - - ./check-attachments.sh - - ./test-endless-attachment & - sleep 0.1 - - ./check-attachments.sh - - ./test-endless-attachment & - sleep 0.1 - - ./check-attachments.sh - - ./test-remove - ./host-available.sh yes - - kill $$(pidof -s test-endless-attachment) - ./host-available.sh yes - - ./test-write - ./test-read - - kill $$(pidof -s test-endless-attachment) - ./host-available.sh no +testcase-4: test-create-shm test-write test-read test-endless-attachment test-status test-remove shm-available.sh check-attachments.sh + bash ./testcase-4.sh test-%: %.c $(EXTRA_CFILE) $(CC) $(CFLAGS) $+ -o $@ diff --git a/test/interactions-shm/create-ipc.c b/test/interactions-shm/create-ipc.c new file mode 100644 index 0000000..43ce03d --- /dev/null +++ b/test/interactions-shm/create-ipc.c @@ -0,0 +1,8 @@ +#include "utils.h" + +int main() { + int shmid; + if ((shmid = shmget(IPC_PRIVATE, 30, 0666)) < 0) error_exit("shmget"); + printf("%d\n", shmid); + return 0; +} diff --git a/test/interactions-shm/host.c b/test/interactions-shm/create-shm.c similarity index 72% rename from test/interactions-shm/host.c rename to test/interactions-shm/create-shm.c index d4d3752..d5f13da 100644 --- a/test/interactions-shm/host.c +++ b/test/interactions-shm/create-shm.c @@ -4,8 +4,6 @@ int main() { key_t key; if ((key = ftok(".", 1)) == -1) error_exit("ftok"); if (shmget(key, 30, IPC_CREAT | IPC_EXCL | 0666) < 0) error_exit("shmget"); - while (shmget(key, 30, 0666) > 0) - continue; - printf("host - ok\n"); + printf("create-shm - ok\n"); return 0; } diff --git a/test/interactions-shm/remove.c b/test/interactions-shm/remove.c index 75d3643..62ce683 100644 --- a/test/interactions-shm/remove.c +++ b/test/interactions-shm/remove.c @@ -1,10 +1,14 @@ #include "utils.h" -int main() { - key_t key; - if ((key = ftok(".", 1)) == -1) error_exit("ftok"); +int main(int argc, char **argv) { int shmid; - if ((shmid = shmget(key, 30, 0666)) < 0) error_exit("shmget"); + if (argc > 1) { + if ((shmid = atoi(argv[1])) == 0) error_exit("atoi"); + } else { + key_t key; + if ((key = ftok(".", 1)) == -1) error_exit("ftok"); + if ((shmid = shmget(key, 30, 0666)) < 0) error_exit("shmget"); + } if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl"); printf("remove - ok\n"); return 0; diff --git a/test/interactions-shm/host-available.sh b/test/interactions-shm/shm-available.sh similarity index 59% rename from test/interactions-shm/host-available.sh rename to test/interactions-shm/shm-available.sh index fc9dadd..dccb448 100755 --- a/test/interactions-shm/host-available.sh +++ b/test/interactions-shm/shm-available.sh @@ -1,6 +1,6 @@ #!/bin/sh -status=$(pidof -q test-host && echo yes || echo no) +status=$(grep global-shmem /proc/*/comm | wc -l) echo "host available: $status" [ "${status}" = "${1}" ] exit $? diff --git a/test/interactions-shm/testcase-1.sh b/test/interactions-shm/testcase-1.sh new file mode 100644 index 0000000..87931eb --- /dev/null +++ b/test/interactions-shm/testcase-1.sh @@ -0,0 +1,5 @@ +./test-create-shm +./shm-available.sh 1 + +./test-remove +./shm-available.sh 0 diff --git a/test/interactions-shm/testcase-2.sh b/test/interactions-shm/testcase-2.sh new file mode 100644 index 0000000..73e156f --- /dev/null +++ b/test/interactions-shm/testcase-2.sh @@ -0,0 +1,10 @@ +./test-create-shm + +./test-create-shm && exit 1 || true +./shm-available.sh 1 + +./test-write +./test-read + +./test-remove +./shm-available.sh 0 diff --git a/test/interactions-shm/testcase-3.sh b/test/interactions-shm/testcase-3.sh new file mode 100644 index 0000000..f7f4117 --- /dev/null +++ b/test/interactions-shm/testcase-3.sh @@ -0,0 +1,20 @@ +./test-create-shm + +IPC1=$(./test-create-ipc) +echo ${IPC1} +./shm-available.sh 1 +IPC2=$(./test-create-ipc) +echo ${IPC2} +./shm-available.sh 1 +IPC3=$(./test-create-ipc) +echo ${IPC3} +./shm-available.sh 1 + +./test-remove +./shm-available.sh 1 +./test-remove ${IPC1} +./shm-available.sh 1 +./test-remove ${IPC2} +./shm-available.sh 1 +./test-remove ${IPC3} +./shm-available.sh 0 diff --git a/test/interactions-shm/testcase-4.sh b/test/interactions-shm/testcase-4.sh new file mode 100644 index 0000000..f3fe627 --- /dev/null +++ b/test/interactions-shm/testcase-4.sh @@ -0,0 +1,21 @@ +./test-create-shm + +./check-attachments.sh + +./test-endless-attachment & +./check-attachments.sh + +./test-endless-attachment & +./check-attachments.sh + +./test-remove +./shm-available.sh yes + +kill $(pidof -s test-endless-attachment) +./shm-available.sh 1 + +./test-write +./test-read + +kill $(pidof -s test-endless-attachment) +./shm-available.sh 0 diff --git a/test/ipc-private.c b/test/ipc-private.c index 127f94e..0a819c7 100644 --- a/test/ipc-private.c +++ b/test/ipc-private.c @@ -25,6 +25,7 @@ int main() { if ((shm_child = shmat(shmid, NULL, 0)) == (char*) -1) error_exit("shmat-child"); shm_child[1] = '#'; if (shmdt(shm_child) != 0) error_exit("shmdt-child-2"); + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl-child"); // Create a new shared memory segment in the child to check that the // parent can access it. @@ -40,6 +41,7 @@ int main() { int dummy; if (read(exit_pipes[0], &dummy, sizeof(int)) != sizeof(int)) error_exit("read-pipe"); + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl-child-2"); return 0; } else { int shmid_from_child; diff --git a/test/with-key.c b/test/with-key.c index de7b3e6..6e91934 100644 --- a/test/with-key.c +++ b/test/with-key.c @@ -36,6 +36,7 @@ int main() { if ((shm_child = shmat(shmid, NULL, 0)) == (char*) -1) error_exit("shmat-child-2"); shm_child[1] = '#'; if (shmdt(shm_child) != 0) error_exit("shmdt-child-2"); + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl-child"); return 0; } else { int shmid;